How to Make Video Games 30

Legend of Zelda CloneIn this part of my video game tutorial I will continue making Zelda. I’ll focus this time on something I have not covered before which is how to create the AI for our villain so that he will travel on his own around the game board looking for Link. The villain will travel around using very few system resources. He will be able to locate Link any place on the map. We will use multidimensional arrays to store travel nodes. We will also learn how to find out which node game objects are closest to.

All of the code used follows the video below.

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

[googleplusone]

Code From the Video

Villain.cs

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

// Create empty called TravelNodes with 0, 0 position
// Create Node0 - Node10 and add collider with is trigger
// with size .2 and Y off set at .25

public class Villain : MonoBehaviour {

	public float speed = 2f;

	// The villains will all start going right when loaded
	Vector2 direction = Vector2.right;

	// Keeps track of if Villain is chasing or not
	bool chasing = true;

	// Get reference to the Animator so I can change animations
	private Animator animator;

	// Reference to Link
	GameObject linkGO;

	// Reference to rigidbody
	Rigidbody2D rb;

	// NEW STUFF 1
	// Create array of all nodes villains travel to
	int[,] villainNodes = new int[,] { { 1, 1 }, { 10, 2 }, { 5, 9 }, { 7, 15 },
		{13, 15}, {15, 10}, {15, 3}, {22, 3}, {24, 5}, {21, 9}, {23, 13}};

	// Set the closest node here 
	public int currentNode = 1;

	// Track the target node
	public int targetNode = 2;

	// END OF NEW STUFF 1

	// NEW STUFF 2

	// Will hold all Villain Nodes
	GameObject[] villainNodeGOs;

	// Stores the node Link is the closest to
	GameObject linksClosestNodeGO;

	// END OF NEW STUFF 2

	void Awake(){
		// Get reference to Link
		linkGO = GameObject.Find("Link");

		// Get reference to rigid body
		rb = GetComponent<Rigidbody2D> ();

		animator = GetComponent<Animator> ();

		// NEW STUFF 2 Get villainNodes
		villainNodeGOs = GameObject.FindGameObjectsWithTag("VillainNode");
	}


	void FixedUpdate(){

		// NEW STUFF 1
		if (Input.GetKeyUp ("f")) {

			if (targetNode < 10) {
				// Increments through the villain nodes
				targetNode = targetNode + 1;

				// Move the villain to the node
				GoForwardToNode ();

				// NEW STUFF 2 Get Links closest node
				linksClosestNodeGO = LinksClosestNode ();

				Debug.Log ("Links closest node " + 
					GetIndexForLinksClosestNode(linksClosestNodeGO));
			}
		}

		if (Input.GetKeyUp ("b")) {

			if (targetNode > 0) {
				// Decrements through the villain nodes
				targetNode = targetNode - 1;

				// Move the villain to the node
				GoForwardToNode ();
			}
		}
		// END OF NEW STUFF 1

		/*
		transform.position = Vector3.MoveTowards (transform.position,
			linkGO.transform.position,
			.03f);
			*/

	}

	// NEW STUFF 1

	void GoForwardToNode(){

		if (!AtTargetNode ()) {
		
			// Define target x y position
			Vector2 targetPosition = new Vector2 (villainNodes [targetNode, 0] - transform.position.x,
				villainNodes [targetNode, 1] - transform.position.y);

			Debug.Log ("Target X : " + villainNodes [targetNode, 0] +
			"Y : " + villainNodes [targetNode, 1]);

			Debug.Log ("Target Node : " + targetNode);

			direction = targetPosition;
		}
	}

	// Gets the target node which stores the x / y values for the target
	// and compares that to the villains current position
	bool AtTargetNode(){
		if (transform.position.x == villainNodes [targetNode, 0] &&
		    transform.position.y == villainNodes [targetNode, 1]) {
			return true;
		} else {
			return false;
		}
	}

	void Update(){

		// Change the direction as needed
		rb.velocity = direction;
	}

	// END OF NEW STUFF 1

	// NEW STUFF 2
	// Returns the node Link is closest to

	GameObject LinksClosestNode(){

		// Will hold the closest node
		GameObject closestNodeGO = null;

		foreach (GameObject nodeGO in villainNodeGOs) {

			if (closestNodeGO == null) {
				closestNodeGO = nodeGO;
			}

			if (Vector3.Distance (linkGO.transform.position, 
				   nodeGO.transform.position) <= Vector3.Distance (linkGO.transform.position,
				   closestNodeGO.transform.position)) {

				// Assign the closest gameobject for comparison
				closestNodeGO = nodeGO;
			}
		}

		return closestNodeGO;

	}

	// Returns the index in the node array for Links closest node
	int GetIndexForLinksClosestNode(GameObject linksNodeGO){

		int nodeIndex = 0;

		for (int i = 0; i <= villainNodes.GetUpperBound (0); i++) {

			Debug.Log ("X : " + villainNodes [i, 0]);
			Debug.Log ("Y : " + villainNodes [i, 1]);

			if (linksNodeGO.transform.position.x == villainNodes [i, 0] &&
				   linksNodeGO.transform.position.y == villainNodes [i, 1]) {

				nodeIndex = i;

			}
				
		}
		

		return nodeIndex;

	}

	// END OF NEW STUFF 2

	// Handles changing direction when we hit VillainStart and end
	void OnTriggerEnter2D(Collider2D col){

		if (!chasing) {
			// When we collide make the Villain go in the opposite direction

			transform.localScale = new Vector2 (-1 * transform.localScale.x,
				transform.localScale.y);

			// Change direction
			direction = new Vector2 (-1 * direction.x, direction.y);
				
		} 

		else {

		}

		// NEW STUFF 1

		// Give all Villain travel nodes the tag VillainNode
		// Add box colliders and Is Trigger to all nodes
		if (col.tag == "VillainNode") {

			// If the villain collides with a node stop
			if (col.transform.position.x == villainNodes [targetNode, 0] &&
			   col.transform.position.y == villainNodes [targetNode, 1]) {
				direction = Vector3.zero;
			}
		}

		// if(col.gameObject.name == "Link")
	}

	// END OF NEW STUFF 1
}

Leave a Reply

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