In 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
}