357 lines
14 KiB
C#
357 lines
14 KiB
C#
namespace Marro.PacManUdon
|
|
{
|
|
using System;
|
|
using UdonSharp;
|
|
using UnityEngine;
|
|
using VRC.SDKBase;
|
|
using VRC.Udon;
|
|
using VRC.SDK3.Components;
|
|
|
|
public class PacMan : GridMover
|
|
{
|
|
private GameManager gameController;
|
|
private PlayerInput input;
|
|
private float defaultSpeed;
|
|
private float powerPelletSpeed;
|
|
private float speed;
|
|
private Vector3 startPosition;
|
|
private Quaternion startRotation;
|
|
private Vector3 startScale;
|
|
private Animator animator;
|
|
new Renderer renderer;
|
|
private VRCObjectPool pelletPool;
|
|
private bool hideUntilUnfrozen;
|
|
private bool dead;
|
|
private bool kinematic;
|
|
|
|
private bool followingPredefinedPath;
|
|
private Vector2[] predefinedPath;
|
|
private int predefinedPathIndex;
|
|
|
|
private Vector2 syncedPosition;
|
|
private Vector2 targetDirection;
|
|
private float freezeSeconds;
|
|
private bool frozen;
|
|
|
|
#region Animator constants
|
|
private const string AnimatorKeyDead = "Dead";
|
|
private const string AnimatorKeyDirection = "Direction";
|
|
private const float AnimatorDirectionNone = 0f;
|
|
private const float AnimatorDirectionRight = 0.25f;
|
|
private const float AnimatorDirectionLeft = 0.50f;
|
|
private const float AnimatorDirectionUp = 0.75f;
|
|
private const float AnimatorDirectionDown = 1f;
|
|
private const float AnimatorDirectionRightBig = 1.25f;
|
|
#endregion
|
|
|
|
|
|
public void Initialize(PlayerInput input, VRCObjectPool pelletPool, GameManager gameController)
|
|
{
|
|
this.gameController = gameController;
|
|
this.input = input;
|
|
this.pelletPool = pelletPool;
|
|
animator = GetComponent<Animator>();
|
|
renderer = GetComponent<Renderer>();
|
|
frozen = false;
|
|
hideUntilUnfrozen = false;
|
|
startPosition = transform.localPosition;
|
|
startRotation = transform.localRotation;
|
|
startScale = transform.localScale;
|
|
}
|
|
|
|
public void Reset()
|
|
{
|
|
// Debug.Log($"{gameObject} Reset!");
|
|
transform.localPosition = startPosition;
|
|
transform.localRotation = startRotation;
|
|
transform.localScale = startScale;
|
|
direction = Vector2.left;
|
|
targetDirection = Vector2.left;
|
|
speed = defaultSpeed;
|
|
kinematic = false;
|
|
followingPredefinedPath = false;
|
|
|
|
SetDead(false);
|
|
animator.SetTrigger("Reset");
|
|
}
|
|
|
|
void FixedUpdate()
|
|
{
|
|
// gameStateManager.statusDisplay.SetDebugText(1, this.targetDirection.ToString());
|
|
|
|
if (frozen)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (hideUntilUnfrozen)
|
|
{
|
|
hideUntilUnfrozen = false;
|
|
SetVisibility(true);
|
|
}
|
|
|
|
float speed = this.speed;
|
|
if (freezeSeconds > 0)
|
|
{
|
|
float freezePart = freezeSeconds / Time.deltaTime;
|
|
if (freezePart >= 1)
|
|
{
|
|
freezeSeconds -= Time.deltaTime;
|
|
animator.speed = 0;
|
|
return;
|
|
}
|
|
speed *= 1 - freezePart;
|
|
animator.speed = 1 - freezePart;
|
|
freezeSeconds = 0;
|
|
}
|
|
else
|
|
{
|
|
animator.speed = 1;
|
|
}
|
|
|
|
Vector2 position = GetPosition();
|
|
Vector2 nextPosition = GridMoverTools.GetNextPosition(position, direction, speed); // The position pacman will move to, assuming it doens't get changed
|
|
|
|
if (!kinematic)
|
|
{
|
|
if (followingPredefinedPath)
|
|
{
|
|
nextPosition = ProcessPredefinedPath(position, nextPosition);
|
|
}
|
|
else
|
|
{
|
|
nextPosition = ProcessNextPosition(position, nextPosition);
|
|
}
|
|
}
|
|
|
|
SetPosition(nextPosition);
|
|
}
|
|
|
|
private Vector2 ProcessNextPosition(Vector2 position, Vector2 nextPosition)
|
|
{
|
|
if (GridMoverTools.CrossesTileCenter(position, nextPosition, direction.x != 0, false) // If pacman is moving horizontally, check if he may cross the center of a tile in that axis
|
|
&& (targetDirection.x == 0 || GridMoverTools.CheckCollisionInDirection(transform, nextPosition, new Vector2(direction.x, 0))))
|
|
{ // If the target direction is in the other axis or if we're about to run into a wall
|
|
nextPosition.x = GridMoverTools.PositionToGrid(nextPosition).x; // Snap pacman to the center of his current tile in this axis
|
|
SetDirection(new Vector2(0, direction.y));
|
|
// Debug.Log($"{gameObject} crossed X tile center from {currentPosition}, nextPosition is now {nextPosition} and direction is now {direction}");
|
|
}
|
|
|
|
if (GridMoverTools.CrossesTileCenter(position, nextPosition, false, direction.y != 0) // See comments above but now vertical
|
|
&& (targetDirection.y == 0 || GridMoverTools.CheckCollisionInDirection(transform, nextPosition, new Vector2(0, direction.y))))
|
|
{
|
|
nextPosition.y = GridMoverTools.PositionToGrid(nextPosition).y;
|
|
SetDirection(new Vector2(direction.x, 0));
|
|
// Debug.Log($"{gameObject} crossed Y tile center from {currentPosition} with targetDirection {targetDirection}, nextPosition is now {nextPosition} and direction is now {direction}");
|
|
}
|
|
|
|
if (Networking.IsOwner(gameObject))
|
|
{
|
|
Vector2 inputDirection = input.GetDirection();
|
|
if (!inputDirection.Equals(Vector2.zero) && !inputDirection.Equals(targetDirection) // Ignore neutral input or input in our current direction
|
|
&& (inputDirection.x == 0 || (Math.Round(nextPosition.y, 5) - 0.5) % 1 != 0) && (inputDirection.y == 0 || (Math.Round(nextPosition.x, 5) - 0.5) % 1 != 0) // Target grid position near the edge of a tile may not be correct, ignore inputs near the border
|
|
&& !GridMoverTools.CheckCollisionInDirection(transform, nextPosition, inputDirection))
|
|
{ // Check if the requested direction does not have a wall
|
|
if (inputDirection.x != 0)
|
|
{ // Move in the requested direction, as well as perpundicular to it to get to the center of the tunnel
|
|
SetDirection(inputDirection + new Vector2(0, GridMoverTools.PositionToGrid(nextPosition).y - nextPosition.y).normalized);
|
|
}
|
|
else
|
|
{
|
|
SetDirection(inputDirection + new Vector2(GridMoverTools.PositionToGrid(nextPosition).x - nextPosition.x, 0).normalized);
|
|
}
|
|
SetTargetDirection(inputDirection); // This is the direction most logic should assume pacman is moving, the actual direction may be different due to cornering
|
|
}
|
|
}
|
|
|
|
return nextPosition;
|
|
}
|
|
|
|
private Vector2 ProcessPredefinedPath(Vector2 position, Vector2 nextPosition)
|
|
{
|
|
if (GridMoverTools.CrossesTileCenter(position, nextPosition, direction.x != 0, direction.y != 0))
|
|
{
|
|
// Find the next valid direction which isn't Vector2.zero
|
|
int nextValidDirectionIndex = predefinedPathIndex;
|
|
while (predefinedPath[nextValidDirectionIndex] == Vector2.zero)
|
|
{
|
|
nextValidDirectionIndex += 1;
|
|
}
|
|
if (!GridMoverTools.CheckCollisionInDirection(transform, nextPosition, predefinedPath[nextValidDirectionIndex]))
|
|
{
|
|
// If we're at a Vector2.zero, we skip applying the direction and only increment.
|
|
if (nextValidDirectionIndex == predefinedPathIndex)
|
|
{
|
|
SetDirection(predefinedPath[nextValidDirectionIndex]);
|
|
SetTargetDirection(predefinedPath[nextValidDirectionIndex]);
|
|
nextPosition = GridMoverTools.PositionToGrid(nextPosition) + direction.normalized * 0.01f;
|
|
|
|
// Check if we've reached the end of the path, which includes making sure the path doesn't end on Vector2.zero
|
|
do
|
|
{
|
|
nextValidDirectionIndex += 1;
|
|
if (nextValidDirectionIndex >= predefinedPath.Length)
|
|
{
|
|
followingPredefinedPath = false;
|
|
break;
|
|
}
|
|
} while (predefinedPath[nextValidDirectionIndex] == Vector2.zero);
|
|
}
|
|
|
|
// gameStateManager.statusDisplay.SetDebugText(1, predefinedPathIndex.ToString());
|
|
|
|
predefinedPathIndex++;
|
|
}
|
|
}
|
|
return nextPosition;
|
|
}
|
|
|
|
protected override void UpdateAnimator()
|
|
{
|
|
// Debug.Log($"{gameObject} UpdateAnimator with direction {direction}, dead {dead}, frozen {frozen}");
|
|
if (!gameObject.activeInHierarchy)
|
|
return;
|
|
|
|
animator.SetBool(AnimatorKeyDead, dead);
|
|
if (dead)
|
|
{
|
|
animator.speed = 1;
|
|
return;
|
|
}
|
|
|
|
if (frozen || direction.Equals(Vector2.zero))
|
|
{
|
|
animator.SetFloat(AnimatorKeyDirection, AnimatorDirectionNone);
|
|
animator.speed = 0;
|
|
}
|
|
else
|
|
{
|
|
animator.speed = 1;
|
|
if (targetDirection.Equals(Vector2.right))
|
|
{
|
|
animator.SetFloat(AnimatorKeyDirection, AnimatorDirectionRight);
|
|
}
|
|
else if (targetDirection.Equals(Vector2.left))
|
|
{
|
|
animator.SetFloat(AnimatorKeyDirection, AnimatorDirectionLeft);
|
|
}
|
|
else if (targetDirection.Equals(Vector2.up))
|
|
{
|
|
animator.SetFloat(AnimatorKeyDirection, AnimatorDirectionUp);
|
|
}
|
|
else if (targetDirection.Equals(Vector2.down))
|
|
{
|
|
animator.SetFloat(AnimatorKeyDirection, AnimatorDirectionDown);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void SetDead(bool dead)
|
|
{
|
|
this.dead = dead;
|
|
UpdateAnimator();
|
|
}
|
|
|
|
public void SetFrozen(bool frozen)
|
|
{
|
|
this.frozen = frozen;
|
|
UpdateAnimator();
|
|
}
|
|
|
|
public void HideUntilUnfrozen()
|
|
{
|
|
hideUntilUnfrozen = true;
|
|
SetVisibility(false);
|
|
}
|
|
|
|
public void SetLevel(int level)
|
|
{
|
|
// Debug.Log($"{gameObject} SetLevel {level}");
|
|
defaultSpeed = PacManConstants.GetPacManDefaultSpeedForLevel(level);
|
|
powerPelletSpeed = PacManConstants.GetPacManPowerPelletSpeedForLevel(level);
|
|
}
|
|
|
|
public void SetPowerPellet(bool powerPellet)
|
|
{
|
|
if (powerPellet)
|
|
{
|
|
speed = powerPelletSpeed;
|
|
}
|
|
else
|
|
{
|
|
speed = defaultSpeed;
|
|
}
|
|
}
|
|
|
|
public void SetActive(bool active)
|
|
{
|
|
gameObject.SetActive(active);
|
|
renderer.enabled = active;
|
|
if (active)
|
|
{
|
|
UpdateAnimator();
|
|
}
|
|
}
|
|
|
|
public void SetKinematic(bool kinematic)
|
|
{
|
|
this.kinematic = kinematic;
|
|
}
|
|
|
|
public void SetPredefinedPath(Vector2[] predefinedPath)
|
|
{
|
|
this.predefinedPath = predefinedPath;
|
|
followingPredefinedPath = true;
|
|
predefinedPathIndex = 0;
|
|
}
|
|
|
|
public void BecomeBig()
|
|
{
|
|
animator.SetFloat(AnimatorKeyDirection, AnimatorDirectionRightBig);
|
|
}
|
|
|
|
void SetVisibility(bool visible)
|
|
{
|
|
renderer.enabled = visible;
|
|
}
|
|
|
|
public void SetTargetDirection(Vector2 targetDirection)
|
|
{
|
|
this.targetDirection = targetDirection;
|
|
UpdateAnimator();
|
|
}
|
|
|
|
void OnTriggerEnter(Collider other)
|
|
{
|
|
Pellet pellet = other.gameObject.GetComponent<Pellet>();
|
|
if (pellet)
|
|
{
|
|
if (Networking.IsOwner(gameObject))
|
|
{
|
|
pelletPool.Return(pellet.gameObject);
|
|
}
|
|
else
|
|
{
|
|
pellet.pelletRenderer.enabled = false;
|
|
pellet.gameObject.SetActive(false);
|
|
}
|
|
|
|
if (pellet.isPowerPellet)
|
|
{
|
|
gameController.GotPowerPellet();
|
|
freezeSeconds = 0.05f;
|
|
}
|
|
else
|
|
{
|
|
gameController.GotPellet();
|
|
freezeSeconds = 0.0166666666666667f;
|
|
}
|
|
return;
|
|
}
|
|
else if (Networking.IsOwner(gameObject) && other.gameObject.GetComponent<BonusFruit>())
|
|
{
|
|
gameController.GotFruit();
|
|
}
|
|
}
|
|
}
|
|
} |