Pacman is a simple game consume all the pellets and complete the level but you have to watch out for ghosts or you will die.
//Setting up the unique behaviours inside the ghost
public class Ghost_Clyde : Ghosts
{
public override void Initialize(PlayerController aPacman, FloorManager aFloorManager)
{
base.Initialize(aPacman ,aFloorManager );
m_GhostBehaviours.Add(GhostStates.Standby, new Behaviour_StandBy());
m_GhostBehaviours.Add(GhostStates.GhostUnique, new Behaviour_Clyde());
m_GhostBehaviours.Add(GhostStates.Agression, new Behaviour_Blinky());
m_GhostBehaviours.Add(GhostStates.Corner, new Behaviour_Corner());
m_GhostBehaviours.Add(GhostStates.Eatable, new Behaviour_Eatable());
m_GhostBehaviours.Add(GhostStates.Death, new Behaviour_ReturnToSpawn());
foreach (var behaviour in m_GhostBehaviours)
{
behaviour.Value.Initialize(this, m_Pacman , m_FloorManager);
}
m_DefaultGhostMaterial = Resources.Load("Ghost/Material_Clyde");
m_GhostType = GhostTypes.Cylde;
}
}
//Each behaviour is only updated when its the current behaviour
public void Update()
{
if (m_CurrentBehaviour != null)
{
m_CurrentBehaviour.UpdateBehaviour();
}
}
// Inside the behaviour they act on how they are moving
private int m_DistanceFromPacman = 4;
private float m_FearTimer = 2;
private float m_CurrentFearTimer;
public override void Initialize(Ghosts aGhost , PlayerController aPacman, FloorManager aFloorManager)
{
base.Initialize(aGhost,aPacman,aFloorManager);
m_CalculationRefreshRate = Helpers.Constants.GhostFrameRecalculation;
m_GhostSpeed = Helpers.Constants.GhostNormalSpeed;
m_TimerEnd = Helpers.Constants.GhostAggressiveTime;
}
public Vector2Int RandomNode()
{
return m_FloorManager.GetRandomNode().m_PositionInGrid;
}
public override void ActivateBehaviour()
{
m_GoalPosition = m_Ghost.m_Pacman.m_CurrentNode.m_PositionInGrid;
m_Paths = m_Ghost.CalculatePath(m_GoalPosition);
m_Ghost.SetGhostMaterial(m_Ghost.m_DefaultGhostMaterial);
m_CurrentTimer = 0;
NextMove();
}
public override void UpdateBehaviour()
{
m_CurrentTimer += Time.deltaTime;
m_CurrentFearTimer += Time.deltaTime;
if (m_CurrentTimer >= m_TimerEnd)
{
m_Ghost.SetGhostBehaviour(Ghosts.GhostStates.Agression, true);
}
if (m_CurrentFearTimer >= m_FearTimer)
{
if (Vector3.Distance(m_Ghost.gameObject.transform.position, m_Pacman.transform.position) < 15.0f)
{
Debug.Log("RUN AWAY");
m_GoalPosition = RandomNode();
m_Paths = m_Ghost.CalculatePath(m_GoalPosition);
m_CurrentFearTimer = 0;
}
}
if (m_Paths.Count > 0)
{
if (m_Paths[0] != null)
{
DirectMovement(m_Ghost.gameObject.transform, m_Paths[0], m_GhostSpeed);
}
}
}
public void Reset()
{
m_GoalPosition = m_Ghost.m_Pacman.m_CurrentNode.m_PositionInGrid;
m_Paths = m_Ghost.CalculatePath(m_GoalPosition);
m_CurrentTimer = 0;
NextMove();
}
public override void NextMove()
{
if (m_Paths.Count <= 1)
{
Reset();
return;
}
m_Paths.RemoveAt(0);
}
The Ghosts in pacman are the classic defintion of the statemachine design pattern. Every ghost has there own states that is currently being updated and they can swap between quickly without any dependencys
The level is first generated with me getting each direction that would be avaliable to move under. Then we do a second pass where we add the unique objects like the ghost buster and pellets/
public class Floor_Classic : Floor
{
public enum FloorGimmicks
{
Empty = 0,
Pellet = 1,
Ghostbuster = 2,
}
public enum FloorDirections
{
Empty = 0,
Up = 1,
Down = 2,
Left = 3,
Right = 4,
AllSidesOpen = 5,
UpDown = 6,
UpLeft = 7,
Upright = 8,
LeftDown = 9,
RightDown = 10,
LeftRight = 11,
UpLeftRight = 12,
UpLeftDown = 13,
UpRightDown = 14,
DownLeftRight = 15,
}
// Start is called before the first frame update
public override void Initialize()
{
m_GridDimensionX = 20;
m_GridDimensionY = 20;
m_FloorBlueprint = new short[]
{
10, 11, 15, 11, 11, 9, 0, 10, 11, 11, 15, 11, 9, 0, 0, 0, 0, 0, 0, 0, //1
6, 0, 6, 0, 0, 6, 0, 6, 0, 0, 6, 0, 6, 0, 0, 0, 0, 0, 0, 0, //2
14, 11, 5, 11, 15, 12, 11, 12, 15, 11, 5, 11, 13, 0, 0, 0, 0, 0, 0, 0, //3
6, 0, 6, 0, 6, 0, 0, 0, 6, 0, 6, 0, 6, 0, 0, 0, 0, 0, 0, 0, //4
8, 11, 13, 0, 8, 9, 0, 10, 7, 0, 14, 11, 7, 0, 0, 0, 0, 0, 0, 0, //5
0, 0, 6, 0, 0, 6, 0, 6, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, //6
0, 0, 6, 0, 10, 12, 11, 12, 9, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, //7
0, 0, 14, 11, 13, 4, 12, 3, 14, 11, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, //8
0, 0, 6, 0, 14, 11, 11, 11, 13, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, //9
0, 0, 6, 0, 6, 0, 0, 0, 6, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, //10
10, 11, 5, 11, 12, 9, 0, 10, 12, 11, 5, 11, 9, 0, 0, 0, 0, 0, 0, 0, //11
6, 0, 6, 0, 0, 6, 0, 6, 0, 0, 6, 0, 6, 0, 0, 0, 0, 0, 0, 0, //12
8, 9, 14, 11, 15, 12, 11, 12, 15, 11, 13, 10, 7, 0, 0, 0, 0, 0, 0, 0, //13
0, 14, 7, 0, 6, 0, 0, 0, 6, 0, 8, 13, 0, 0, 0, 0, 0, 0, 0, 0, //14
10, 7, 0, 0, 8, 9, 0, 10, 7, 0, 0, 8, 9, 0, 0, 0, 0, 0, 0, 0, //15
6, 0, 0, 0, 0, 6, 0, 6, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, //16
8, 11, 11, 11, 11, 12, 11, 12, 11, 11, 11, 11, 7, 0, 0, 0, 0, 0, 0, 0, //17
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //18
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //19
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 //20
// 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
};
m_GoalsBlueprint = new short[]
{
1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, //1
2, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, //2
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, //3
1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, //4
1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, //5
0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, //6
0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, //7
0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, //8
0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, //9
0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, //10
1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, //11
2, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, //12
1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, //13
0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, //14
1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, //15
1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, //16
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, //17
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //18
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //19
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 //20
// 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
};
SpawnPlayer();
SpawnGhosts();
}
//Spawning the unique objects on the pacman floor I called them gimmicks
public void SpawnGimmick(FloorNode aFloornode, short aGimmickType)
{
GameObject gimmickToSpawn = null;
switch (aGimmickType)
{
case (short)FloorGimmicks.Pellet:
//Each gimmick is initially object pooled so we can easily reset
gimmickToSpawn = GetInActiveGimmickObject( m_PelletPool);
if (gimmickToSpawn == null)
{
return;
}
gimmickToSpawn.transform.position = aFloornode.transform.position + Helpers.Constants.HeightOffGrid;;
break;
case (short)FloorGimmicks.Ghostbuster:
gimmickToSpawn = GetInActiveGimmickObject( m_GhostBusterPool);
if (gimmickToSpawn == null)
{
return;
}
gimmickToSpawn.transform.position = aFloornode.transform.position + Helpers.Constants.HeightOffGrid;;
break;
}
}
Pacman was a great project to quickly make. We really have come a long way since pacman.