How to Make Video Games 19

Ms Pacman CloneIn this video I finish setting up the system that will allow Ms. Pac-Man to perfectly travel through the maze. We’ll cover how to calculate if Ms. Pac-Man can Move in Certain Dirctions, Working with GameObjects in an Array, Keeping Ms. Pac-Man from Getting Stuck, Simulating Hitting Walls, Animating, Changing Ms. Pac-Man’s Direction without Pivot Points and more.

All of the images are here and the code follows the video below. You can also Download all of my Unity game files so far for PongSpace InvadersTetris and Mario.

If you like videos like this, consider donating $1 on Patreon, or simply turn off AdBlocker. Either helps a lot.

[googleplusone]

Gameboard.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Gameboard : MonoBehaviour {

	// Create array that holds all TurningPoints
	public Transform[,] gBPoints = new Transform[27,30];

	// References the empty that contains all the points
	private GameObject turningPoints;

	// Use this for initialization
	void Start () {

		// Get the Empty named TurningPoints
		turningPoints = GameObject.Find("TurningPoints");

		// Cycle through each point in that empty
		foreach (Transform point in turningPoints.transform) {

			// Get vector position of point
			Vector2 pos = point.position;

			// Put point in the array
			gBPoints[(int)pos.x, (int)pos.y] = point;

		}
	}
	

}

TurningPoint.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TurningPoint : MonoBehaviour {

	public TurningPoint[] nextPoints;
	public Vector2[] vectToNextPoint;

	// Use this for initialization
	void Start () {

		// Initialize the array that will hold the vector to
		// each nearby TurningPoint
		vectToNextPoint = new Vector2[nextPoints.Length];

		for (int i = 0; i < nextPoints.Length; i++) {

			// Get each point so we can find its position
			// in relation to the current TurningPoint
			TurningPoint nextPoint = nextPoints [i];

			// Get the Vector to the next TurningPoint
			// Returns (1, 0) for right, (0, -1) for down, etc.
			Vector2 pointVect = nextPoint.transform.localPosition - transform.localPosition;

			// Store vector to Vector2 array
			// Without normalized the values wouldn't be 0, 1, or -1
			vectToNextPoint[i] = pointVect.normalized;
		}

	}
}

MsPacman.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

// Used to find out if Pac-Man hits a wall
using System;

public class MsPacman : MonoBehaviour {

	public float speed = 0.4f;

	// Used to move Ms. Pacman
	private Rigidbody2D rb;

	// 5. Sprite used when Pac-Man is paused
	public Sprite pausedSprite;

	// Makes sure components have been created when the 
	// game starts
	void Awake(){
		// Get Pac-man Rigidbody
		rb = GetComponent<Rigidbody2D> ();
	}

	void Start(){
		rb.velocity = new Vector2 (-1, 0) * speed;
	}
		
	/* 1. 
	void FixedUpdate(){

		float horzMove = Input.GetAxisRaw ("Horizontal");
		float vertMove = Input.GetAxisRaw ("Vertical");

		// Move Left
		if (Input.GetKeyDown ("a")) {

			// Changes the direction to left
			rb.velocity = new Vector2 (horzMove, 0) * speed;

			// Faces Pacman left
			transform.localScale = new Vector2 (1, 1);

			// Sets rotation to default
			transform.localRotation = Quaternion.Euler (0, 0, 0);
		} else if (Input.GetKeyDown ("d")) {

			// Changes the direction to right
			rb.velocity = new Vector2 (horzMove, 0) * speed;

			// Faces Pacman right
			transform.localScale = new Vector2 (-1, 1);

			// Sets rotation to default
			transform.localRotation = Quaternion.Euler (0, 0, 0);
		} else if (Input.GetKeyDown ("w")){

			// Move up
			rb.velocity = new Vector2 (0, vertMove) * speed;

			// Sets facing direction to default
			transform.localScale = new Vector2 (1, 1);

			// Rotate facing up
			transform.localRotation = Quaternion.Euler (0, 0, 270);
		} else if (Input.GetKeyDown ("s")){

			// Move Down
			rb.velocity = new Vector2 (0, vertMove) * speed;

			// Sets facing direction to default
			transform.localScale = new Vector2 (1, 1);

			// Rotate facing down
			transform.localRotation = Quaternion.Euler (0, 0, 90);
		}
	}
	*/

	/*
	// 2. All direction changes using Turning Points
	void FixedUpdate(){

		float horzMove = Input.GetAxisRaw ("Horizontal");
		float vertMove = Input.GetAxisRaw ("Vertical");

		Vector2 moveVect;

		// Move Left
		if (Input.GetKeyDown ("a")) {

			// Direction I want to move
			moveVect = new Vector2 (horzMove, 0);

			if(canIMoveInDirection(moveVect)){

				// 3. Position Pac-Man at middle of the lane
				transform.position = new Vector2 ((int)transform.position.x + .5f, 
					(int)transform.position.y + .5f);

				// Changes the direction to left
				rb.velocity = moveVect * speed;

				// Faces Pacman left
				transform.localScale = new Vector2 (1, 1);

				// Sets rotation to default
				transform.localRotation = Quaternion.Euler (0, 0, 0);
			}


		} else if (Input.GetKeyDown ("d")) {

			// Direction I want to move
			moveVect = new Vector2 (horzMove, 0);

			if (canIMoveInDirection (moveVect)) {

				// 3. Position Pac-Man at middle of the lane
				transform.position = new Vector2 ((int)transform.position.x + .5f, 
					(int)transform.position.y + .5f);

				// Changes the direction to right
				rb.velocity = moveVect * speed;

				// Faces Pacman right
				transform.localScale = new Vector2 (-1, 1);

				// Sets rotation to default
				transform.localRotation = Quaternion.Euler (0, 0, 0);

			}


		} else if (Input.GetKeyDown ("w")){

			// Direction I want to move
			moveVect = new Vector2 (0, vertMove);

			if (canIMoveInDirection (moveVect)) {

				// 3. Position Pac-Man at middle of the lane
				transform.position = new Vector2 ((int)transform.position.x + .5f, 
					(int)transform.position.y + .5f);

				// Move up
				rb.velocity = moveVect * speed;

				// Sets facing direction to default
				transform.localScale = new Vector2 (1, 1);

				// Rotate facing up
				transform.localRotation = Quaternion.Euler (0, 0, 270);

			}


		} else if (Input.GetKeyDown ("s")){

			// Direction I want to move
			moveVect = new Vector2 (0, vertMove);

			if (canIMoveInDirection (moveVect)) {

				// 3. Position Pac-Man at middle of the lane
				transform.position = new Vector2 ((int)transform.position.x + .5f, 
					(int)transform.position.y + .5f);

				// Move Down
				rb.velocity = moveVect * speed;

				// Sets facing direction to default
				transform.localScale = new Vector2 (1, 1);

				// Rotate facing down
				transform.localRotation = Quaternion.Euler (0, 0, 90);

			}
		}

		// 5. Will pause and unpause Pac-Man animation 
		// depending on if Pac-Man is at a wall or not
		UpdateEatingAnimation();

	}
	*/

	// 6. Add the ability to go in reverse without a pivot point
	void FixedUpdate(){

		float horzMove = Input.GetAxisRaw ("Horizontal");
		float vertMove = Input.GetAxisRaw ("Vertical");

		Vector2 moveVect;

		// 6. Used to get direction Pac-Man is moving on
		// the X & Y access
		var localVelocity = transform.InverseTransformDirection(rb.velocity);

		// Move Left
		if (Input.GetKeyDown ("a")) {

			// 6. Allows me to reverse direction without the need
			// to hit a point

			if (localVelocity.x > 0) {

				// Direction I want to move
				moveVect = new Vector2 (horzMove, 0);

				// Position Pac-Man at middle of the lane
				transform.position = new Vector2 ((int)transform.position.x + .5f, 
					(int)transform.position.y + .5f);

				// Changes the direction to left
				rb.velocity = moveVect * speed;

				// Faces Pacman left
				transform.localScale = new Vector2 (1, 1);

				// Sets rotation to default
				transform.localRotation = Quaternion.Euler (0, 0, 0);

			} else {

				// Direction I want to move
				moveVect = new Vector2 (horzMove, 0);

				if (canIMoveInDirection (moveVect)) {

					// 3. Position Pac-Man at middle of the lane
					transform.position = new Vector2 ((int)transform.position.x + .5f, 
						(int)transform.position.y + .5f);

					// Changes the direction to left
					rb.velocity = moveVect * speed;

					// Faces Pacman left
					transform.localScale = new Vector2 (1, 1);

					// Sets rotation to default
					transform.localRotation = Quaternion.Euler (0, 0, 0);
				}
			}


		} else if (Input.GetKeyDown ("d")) {

			if (localVelocity.x < 0) {

				// Direction I want to move
				moveVect = new Vector2 (horzMove, 0);

				// Position Pac-Man at middle of the lane
				transform.position = new Vector2 ((int)transform.position.x + .5f, 
					(int)transform.position.y + .5f);

				// Changes the direction to left
				rb.velocity = moveVect * speed;

				// Faces Pacman left
				transform.localScale = new Vector2 (-1, 1);

				// Sets rotation to default
				transform.localRotation = Quaternion.Euler (0, 0, 0);

			} else {

				// Direction I want to move
				moveVect = new Vector2 (horzMove, 0);

				if (canIMoveInDirection (moveVect)) {

					// 3. Position Pac-Man at middle of the lane
					transform.position = new Vector2 ((int)transform.position.x + .5f, 
						(int)transform.position.y + .5f);

					// Changes the direction to right
					rb.velocity = moveVect * speed;

					// Faces Pacman right
					transform.localScale = new Vector2 (-1, 1);

					// Sets rotation to default
					transform.localRotation = Quaternion.Euler (0, 0, 0);

				}
			}
				
		} else if (Input.GetKeyDown ("w")){

			if (localVelocity.y > 0) {

				// Direction I want to move
				moveVect = new Vector2 (0, vertMove);

				// Position Pac-Man at middle of the lane
				transform.position = new Vector2 ((int)transform.position.x + .5f, 
					(int)transform.position.y + .5f);

				// Changes the direction to left
				rb.velocity = moveVect * speed;

				// Faces Pacman left
				transform.localScale = new Vector2 (1, 1);

				// Sets rotation to default
				transform.localRotation = Quaternion.Euler (0, 0, 270);

			} else {

				// Direction I want to move
				moveVect = new Vector2 (0, vertMove);

				if (canIMoveInDirection (moveVect)) {

					// 3. Position Pac-Man at middle of the lane
					transform.position = new Vector2 ((int)transform.position.x + .5f, 
						(int)transform.position.y + .5f);

					// Move up
					rb.velocity = moveVect * speed;

					// Sets facing direction to default
					transform.localScale = new Vector2 (1, 1);

					// Rotate facing up
					transform.localRotation = Quaternion.Euler (0, 0, 270);

				}
			}


		} else if (Input.GetKeyDown ("s")){

			if (localVelocity.y < 0) {

				// Direction I want to move
				moveVect = new Vector2 (0, vertMove);

				// Position Pac-Man at middle of the lane
				transform.position = new Vector2 ((int)transform.position.x + .5f, 
					(int)transform.position.y + .5f);

				// Changes the direction to left
				rb.velocity = moveVect * speed;

				// Faces Pacman left
				transform.localScale = new Vector2 (1, 1);

				// Sets rotation to default
				transform.localRotation = Quaternion.Euler (0, 0, 90);

			} else {

			// Direction I want to move
			moveVect = new Vector2 (0, vertMove);

				if (canIMoveInDirection (moveVect)) {

					// 3. Position Pac-Man at middle of the lane
					transform.position = new Vector2 ((int)transform.position.x + .5f, 
						(int)transform.position.y + .5f);

					// Move Down
					rb.velocity = moveVect * speed;

					// Sets facing direction to default
					transform.localScale = new Vector2 (1, 1);

					// Rotate facing down
					transform.localRotation = Quaternion.Euler (0, 0, 90);
				}
			}
		}

		// 5. Will pause and unpause Pac-Man animation 
		// depending on if Pac-Man is at a wall or not
		UpdateEatingAnimation();

	}


	// 2. Find out if Pac-man is on a TurningPoint
	bool canIMoveInDirection(Vector2 dir){

		// Pac-Man position
		Vector2 pos = transform.position;

		// Used to find if there a Point in the array or null
		Transform point = GameObject.Find ("GBGrid").GetComponent<Gameboard> ().gBPoints [(int)pos.x, (int)pos.y];

		// Did I find a Point here?
		if (point != null) {

			// Get Points associated GameObject
			GameObject pointGO = point.gameObject;

			// Get vectToNextPoint array attached to the Point
			Vector2[] vectToNextPoint = pointGO.GetComponent<TurningPoint> ().vectToNextPoint;

			// Cycle through the attached vectToNextPoint array
			foreach (Vector2 vect in vectToNextPoint) {
				if (vect == dir) {
					return true;
				} 
			}
		} 
		return false;
	}

	// 4. Check if Pac-Man hit a wall
	// Check Is trigger for Pac-Man Circle Collider
	// Change all Dots to Dot Tag and Pills to Pill Tag
	// Add Is Trigger to Points with Circle Collider Radius .1
	// Add Tag Point to all Points
	void OnTriggerEnter2D(Collider2D col){

		bool hitAWall = false;

		// If Pac-Man hit a Point
		if (col.gameObject.tag == "Point") {

			// Get vectToNextPoint array attached to the Point
			Vector2[] vectToNextPoint = col.GetComponent<TurningPoint> ().vectToNextPoint;

			// Cycle through the attached vectToNextPoint array
			// to see if there is a Vector2 == Pac-Mans velocity 
			// or the direction Pac-Man wants to travel
			if (Array.Exists (vectToNextPoint, element => element == rb.velocity.normalized)) {
				hitAWall = false;
			} else {
				hitAWall = true;
			}

			// 5. Needed to make Pac-Man stop on the Point when it 
			// hits a wall
			transform.position = new Vector2 ((int)col.transform.position.x + .5f, 
				(int)col.transform.position.y + .5f);

			// Stops Pac-Man when it hits a wall
			if (hitAWall) 
				rb.velocity = Vector2.zero;
				
		}

	}

	// 5. When Pac-Man hits a wall the animation will stop
	void UpdateEatingAnimation(){
		if (rb.velocity == Vector2.zero) {
			GetComponent<Animator> ().enabled = false;
			GetComponent<SpriteRenderer> ().sprite = pausedSprite;
		} else {
			GetComponent<Animator> ().enabled = true;
		}
	}

}

Leave a Reply

Your email address will not be published. Required fields are marked *