Files
PacManUdon/Assets/Scripts/PacMan.cs

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();
}
}
}
}