323 lines
12 KiB
C#
323 lines
12 KiB
C#
using System;
|
|
using UnityEngine;
|
|
|
|
namespace Marro.PacManUdon
|
|
{
|
|
enum PacManAnimatorState
|
|
{
|
|
Idle,
|
|
Moving,
|
|
Stopped,
|
|
Big,
|
|
Dead,
|
|
}
|
|
|
|
[RequireComponent(typeof(Animator))]
|
|
[RequireComponent(typeof(Renderer))]
|
|
public class PacMan : GridMover
|
|
{
|
|
private PlayerInput input;
|
|
private CollisionManager collisionManager;
|
|
private float defaultSpeed;
|
|
private float powerPelletSpeed;
|
|
private float speed;
|
|
private Vector2 startPosition;
|
|
private Animator animator;
|
|
new Renderer renderer;
|
|
private bool hideUntilUnfrozen;
|
|
private bool dead;
|
|
private bool kinematic;
|
|
|
|
private bool followingPredefinedPath;
|
|
private Direction[] predefinedPath;
|
|
private int predefinedPathIndex;
|
|
|
|
private float freezeSeconds;
|
|
private bool frozen;
|
|
|
|
private readonly int animatorKeyState = Animator.StringToHash("State");
|
|
private readonly int animatorKeyDirection = Animator.StringToHash("Direction");
|
|
|
|
public void Initialize(PlayerInput input, Transform startTransform, GameManager gameManager, CollisionManager collisionManager)
|
|
{
|
|
this.collisionManager = collisionManager;
|
|
this.input = input;
|
|
animator = GetComponent<Animator>();
|
|
renderer = GetComponent<Renderer>();
|
|
frozen = false;
|
|
hideUntilUnfrozen = false;
|
|
startPosition = startTransform.localPosition;
|
|
}
|
|
|
|
public void Reset()
|
|
{
|
|
SetPosition(startPosition);
|
|
direction = Direction.Left;
|
|
targetDirection = Direction.Left;
|
|
speed = defaultSpeed;
|
|
kinematic = false;
|
|
followingPredefinedPath = false;
|
|
|
|
SetDead(false);
|
|
|
|
SetAnimatorState((int)PacManAnimatorState.Idle);
|
|
}
|
|
|
|
public override void SyncedUpdate()
|
|
{
|
|
// 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 / networkManager.SyncedDeltaTime;
|
|
if (freezePart >= 1)
|
|
{
|
|
freezeSeconds -= networkManager.SyncedDeltaTime;
|
|
animator.speed = 0;
|
|
speed = 0;
|
|
}
|
|
else
|
|
{
|
|
speed *= 1 - freezePart;
|
|
animator.speed = 1 - freezePart;
|
|
freezeSeconds = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
animator.speed = 1;
|
|
}
|
|
|
|
Vector2 position = GetPosition();
|
|
Vector2 nextPosition = GetNextPosition(position, directionVectors[(int)direction], speed, networkManager.SyncedDeltaTime); // 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);
|
|
}
|
|
}
|
|
|
|
if (CrossesTileBorder(position, nextPosition, direction))
|
|
{
|
|
CheckNewTile(position, nextPosition);
|
|
}
|
|
|
|
SetPosition(nextPosition);
|
|
}
|
|
|
|
private Vector2 ProcessNextPosition(Vector2 position, Vector2 nextPosition)
|
|
{
|
|
if (CrossesTileCenter(position, nextPosition, Direction.Left) // If pacman is moving horizontally, check if he may cross the center of a tile in that axis
|
|
&& (!IsHorizontal(targetDirection) || collisionManager.IsWallUpcoming(nextPosition, directionVectors[(int)HorizontalComponent(direction)])))
|
|
{ // If the target direction is in the other axis or if we're about to run into a wall
|
|
nextPosition.x = PositionToGrid(nextPosition).x; // Snap pacman to the center of his current tile in this axis
|
|
SetDirection(VerticalComponent(direction));
|
|
//Debug.Log($"{gameObject} crossed X tile center from {position}, nextPosition is now {nextPosition} and direction is now {direction}");
|
|
}
|
|
|
|
if (CrossesTileCenter(position, nextPosition, Direction.Down) // See comments above but now vertical
|
|
&& (!IsVertical(targetDirection) || collisionManager.IsWallUpcoming(nextPosition, directionVectors[(int)VerticalComponent(direction)])))
|
|
{
|
|
nextPosition.y = PositionToGrid(nextPosition).y;
|
|
SetDirection(HorizontalComponent(direction));
|
|
//Debug.Log($"{gameObject} crossed Y tile center from {position} with targetDirection {targetDirection}, nextPosition is now {nextPosition} and direction is now {direction}");
|
|
}
|
|
|
|
var inputDirection = input.GetDirection();
|
|
if (!inputDirection.Equals(Direction.Zero) && !inputDirection.Equals(targetDirection) // Ignore neutral input or input in our current direction
|
|
&& !collisionManager.IsWallUpcoming(nextPosition, directionVectors[(int)inputDirection])) // Check if the requested direction does not have a wall
|
|
{
|
|
// Move in the requested direction, as well as perpundicular to it to get to the center of the tunnel
|
|
if (IsHorizontal(inputDirection))
|
|
{
|
|
var directionToCenter = VerticalToDirection(PositionToGrid(nextPosition).y - nextPosition.y);
|
|
SetDirection((Direction)((int)inputDirection | (int)directionToCenter));
|
|
}
|
|
else
|
|
{
|
|
var directionToCenter = HorizontalToDirection(PositionToGrid(nextPosition).x - nextPosition.x);
|
|
SetDirection((Direction)((int)inputDirection | (int)directionToCenter));
|
|
}
|
|
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 (CrossesTileCenter(position, nextPosition, direction))
|
|
{
|
|
// Find the next valid direction which isn't Vector2.zero
|
|
int nextValidDirectionIndex = predefinedPathIndex;
|
|
while (predefinedPath[nextValidDirectionIndex] == Direction.Zero)
|
|
{
|
|
nextValidDirectionIndex += 1;
|
|
}
|
|
if (!collisionManager.IsWallUpcoming(nextPosition, directionVectors[(int)predefinedPath[nextValidDirectionIndex]]))
|
|
{
|
|
// If we're at a Vector2.zero, we skip applying the direction and only increment.
|
|
if (nextValidDirectionIndex == predefinedPathIndex)
|
|
{
|
|
SetDirectionAndTargetDirection(predefinedPath[nextValidDirectionIndex]);
|
|
nextPosition = PositionToGrid(nextPosition) + directionVectors[(int)direction] * 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] == Direction.Zero);
|
|
}
|
|
|
|
// gameStateManager.statusDisplay.SetDebugText(1, predefinedPathIndex.ToString());
|
|
|
|
predefinedPathIndex++;
|
|
}
|
|
}
|
|
return nextPosition;
|
|
}
|
|
|
|
private void CheckNewTile(Vector2 position, Vector2 nextPosition)
|
|
{
|
|
var eatResult = collisionManager.PacManMoveToTile(position, nextPosition);
|
|
|
|
if (eatResult == EatResult.Pellet)
|
|
{
|
|
freezeSeconds = 0.0166666666666667f;
|
|
}
|
|
else if (eatResult == EatResult.PowerPellet)
|
|
{
|
|
freezeSeconds = 0.05f;
|
|
}
|
|
}
|
|
|
|
protected override void UpdateAnimator()
|
|
{
|
|
//Debug.Log($"{gameObject} UpdateAnimator with direction {direction}, dead {dead}, frozen {frozen}");
|
|
if (!gameObject.activeInHierarchy)
|
|
return;
|
|
|
|
if (dead)
|
|
{
|
|
SetAnimatorState((int)PacManAnimatorState.Dead);
|
|
animator.speed = 1;
|
|
return;
|
|
}
|
|
|
|
animator.speed = frozen ? 0 : 1;
|
|
|
|
if (frozen)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (direction.Equals(Direction.Zero))
|
|
{
|
|
SetAnimatorState((int)PacManAnimatorState.Stopped);
|
|
}
|
|
else
|
|
{
|
|
SetAnimatorState((int)PacManAnimatorState.Moving);
|
|
SetAnimatorDirection((int)targetDirection);
|
|
}
|
|
}
|
|
|
|
private void SetAnimatorDirection(int value) =>
|
|
animator.SetFloat(animatorKeyDirection, value);
|
|
|
|
private void SetAnimatorState(int value) =>
|
|
animator.SetFloat(animatorKeyState, value);
|
|
|
|
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(Direction[] predefinedPath)
|
|
{
|
|
this.predefinedPath = predefinedPath;
|
|
followingPredefinedPath = true;
|
|
predefinedPathIndex = 0;
|
|
}
|
|
|
|
public void BecomeBig()
|
|
{
|
|
SetAnimatorState((int)PacManAnimatorState.Big);
|
|
}
|
|
|
|
void SetVisibility(bool visible)
|
|
{
|
|
renderer.enabled = visible;
|
|
}
|
|
}
|
|
} |