Source file unity-csharp/arcade-classics/pac-man-lite/GridMover.cs from the GDnD code examples. Download raw file
// GDnD wiki example
// Demonstrates: a shared base class that moves an actor smoothly cell-to-cell and asks subclasses which way to turn
// Related pages: [[overview-arcade-classics-as-learning-projects]], [[csharp-inheritance]]
using UnityEngine;
// Both the player and the ghosts move the same way: glide to the next cell
// centre, then decide a direction. That shared mechanism lives here; the
// decision (read input vs run AI) is left abstract for subclasses. This is a
// small, concrete use of [[csharp-inheritance]].
public abstract class GridMover : MonoBehaviour
{
[SerializeField] protected float speed = 4f;
public Vector2Int CurrentCell { get; private set; }
protected MazeGrid maze;
protected Vector2Int currentDir = Vector2Int.zero;
private Vector2Int targetCell;
private Vector3 targetPos;
private bool hasTarget;
public virtual void Init(MazeGrid grid, Vector2Int startCell)
{
maze = grid;
SnapTo(startCell);
}
public void SnapTo(Vector2Int cell)
{
CurrentCell = cell;
transform.position = maze.CellToWorld(cell);
currentDir = Vector2Int.zero;
hasTarget = false;
}
private void Update()
{
if (maze == null)
{
return;
}
OnTick();
StepMovement();
}
private void StepMovement()
{
if (!hasTarget)
{
// At a cell centre: ask the subclass which way to go next.
Vector2Int dir = ChooseDirection(CurrentCell, currentDir);
if (dir != Vector2Int.zero && maze.IsWalkable(CurrentCell + dir))
{
currentDir = dir;
targetCell = CurrentCell + dir;
targetPos = maze.CellToWorld(targetCell);
hasTarget = true;
}
else
{
return; // nowhere to go this frame — idle
}
}
transform.position = Vector3.MoveTowards(transform.position, targetPos, speed * Time.deltaTime);
if ((transform.position - targetPos).sqrMagnitude < 0.0001f)
{
transform.position = targetPos;
CurrentCell = targetCell;
hasTarget = false;
OnArrived(CurrentCell);
}
}
// Runs every frame before movement — used by the player to buffer input.
protected virtual void OnTick() { }
// Decide the next grid direction from the current cell. Vector2Int.zero = stay.
protected abstract Vector2Int ChooseDirection(Vector2Int cell, Vector2Int dir);
// Called once each time the actor reaches a new cell centre.
protected virtual void OnArrived(Vector2Int cell) { }
}