Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Unity 2018 Artificial Intelligence Cookbook

You're reading from   Unity 2018 Artificial Intelligence Cookbook Over 90 recipes to build and customize AI entities for your games with Unity

Arrow left icon
Product type Paperback
Published in Aug 2018
Publisher
ISBN-13 9781788626170
Length 334 pages
Edition 2nd Edition
Languages
Tools
Arrow right icon
Authors (2):
Arrow left icon
Jorge Palacios Jorge Palacios
Author Profile Icon Jorge Palacios
Jorge Palacios
Jorge Elieser P Garrido Jorge Elieser P Garrido
Author Profile Icon Jorge Elieser P Garrido
Jorge Elieser P Garrido
Arrow right icon
View More author details
Toc

Table of Contents (12) Chapters Close

Preface 1. Behaviors - Intelligent Movement FREE CHAPTER 2. Navigation 3. Decision Making 4. The New NavMesh API 5. Coordination and Tactics 6. Agent Awareness 7. Board Games and Applied Search AI 8. Learning Techniques 9. Procedural Content Generation 10. Miscellaneous 11. Other Books You May Enjoy

Following a path

There are times when we need scripted routes, and it's simply inconceivable to do it entirely by code. Imagine you're working on a stealth game. Would you code a route for every single guard? This technique will help you build a flexible path system for those situations.

Getting ready

We need to define a custom data type called PathSegment:

using UnityEngine; 
using System.Collections; 
 
public class PathSegment 
{ 
    public Vector3 a; 
    public Vector3 b; 
 
    public PathSegment () : this (Vector3.zero, Vector3.zero){} 
    public PathSegment (Vector3 a, Vector3 b) 
    { 
        this.a = a; 
        this.b = b; 
    } 
} 

How to do it...

This is a long recipe that could be regarded as a big two-step process. First, we build the Path class, which abstracts points in the path from their specific spatial representations, and then we build the PathFollower behavior that makes use of that abstraction in order to get actual spatial points to follow:

  1. Create the Path class, which consists of nodes and segments; only the nodes are public and are assigned manually:
using UnityEngine; 
using System.Collections; 
using System.Collections.Generic; 
 
public class Path : MonoBehaviour 
{ 
    public List<GameObject> nodes; 
    List<PathSegment> segments; 
} 
  1. Define the Start function to set the segments when the scene starts:
void Start() 
{ 
    segments = GetSegments(); 
} 
  1. Define the GetSegments function to build the segments from the nodes:
public List<PathSegment> GetSegments () 
{ 
    List<PathSegment> segments = new List<PathSegment>(); 
    int i; 
    for (i = 0; i < nodes.Count - 1; i++) 
    { 
        Vector3 src = nodes[i].transform.position; 
        Vector3 dst = nodes[i+1].transform.position; 
        PathSegment segment = new PathSegment(src, dst); 
        segments.Add(segment); 
    } 
    return segments; 
} 
  1. Define the first function for abstraction, which is called GetParam:
public float GetParam(Vector3 position, float lastParam) 
{ 
    // body 
} 
  1. We need to find out the segment the agent is closest to:
float param = 0f; 
PathSegment currentSegment = null; 
float tempParam = 0f; 
foreach (PathSegment ps in segments) 
{ 
    tempParam += Vector3.Distance(ps.a, ps.b); 
    if (lastParam <= tempParam) 
    { 
        currentSegment = ps; 
        break; 
    } 
} 
if (currentSegment == null) 
    return 0f; 
  1. Given the current position, we need to work out the direction to go to:
Vector3 currPos = position - currentSegment.a; 
Vector3 segmentDirection = currentSegment.b - currentSegment.a; 
segmentDirection.Normalize(); 
  1. Find the point in the segment using vector projection:
Vector3 pointInSegment = Vector3.Project(currPos, segmentDirection); 
  1. Finally, GetParam returns the next position to reach along the path:
param = tempParam - Vector3.Distance(currentSegment.a, currentSegment.b);
param += pointInSegment.magnitude; 
return param;
  1. Define the GetPosition function:
public Vector3 GetPosition(float param)  
{ 
    // body 
} 
  1. Given the current location along the path, we find the corresponding segment:
Vector3 position = Vector3.zero; 
PathSegment currentSegment = null; 
float tempParam = 0f; 
foreach (PathSegment ps in segments) 
{ 
    tempParam += Vector3.Distance(ps.a, ps.b); 
    if (param <= tempParam) 
    { 
        currentSegment = ps; 
        break; 
    } 
} 
if (currentSegment == null) 
    return Vector3.zero; 
  1. GetPosition converts the parameter as a spatial point and returns it:
Vector3 segmentDirection = currentSegment.b - currentSegment.a; 
segmentDirection.Normalize(); 
tempParam -= Vector3.Distance(currentSegment.a, currentSegment.b); 
tempParam = param - tempParam; 
position = currentSegment.a + segmentDirection * tempParam; 
return position; 
  1. Create the PathFollower behavior, which derives from Seek (remember to set the order of execution):
using UnityEngine; 
using System.Collections; 
 
public class PathFollower : Seek 
{ 
    public Path path; 
    public float pathOffset = 0.0f; 
    float currentParam; 
}

  1. Implement the Awake function to set the target:
public override void Awake() 
{ 
    base.Awake(); 
    target = new GameObject(); 
    currentParam = 0f; 
} 
  1. The final step is to define the GetSteering function that relies on the abstraction created by the Path class to set the target position and apply Seek:
public override Steering GetSteering() 
{ 
    currentParam = path.GetParam(transform.position, currentParam); 
    float targetParam = currentParam + pathOffset; 
    target.transform.position = path.GetPosition(targetParam); 
    return base.GetSteering(); 
} 

How it works...

We use the Path class in order to have a movement guideline. It is the cornerstone, because it relies on GetParam to map an offset point to follow in its internal guideline, and it also uses GetPosition to convert that referential point to a position in the three-dimensional space along the segments.

The path-following algorithm just makes use of the path's functions in order to get a new position, update the target, and apply the Seek behavior.

There's more...

It's important to take into account the order in which the nodes are linked in the inspector for the path to work as expected. A practical way to achieve this is to manually name the nodes with a reference number:

An example of a path set up in the Inspector window

Also, we could define the OnDrawGizmos function in order to have a better visual reference of the path:

void OnDrawGizmos () 
{ 
    Vector3 direction; 
    Color tmp = Gizmos.color; 
    Gizmos.color = Color.magenta;//example color 
    int i; 
    for (i = 0; i < nodes.Count - 1; i++) 
    { 
        Vector3 src = nodes[i].transform.position; 
        Vector3 dst = nodes[i+1].transform.position; 
        direction = dst - src; 
        Gizmos.DrawRay(src, direction); 
    } 
    Gizmos.color = tmp; 
} 
You have been reading a chapter from
Unity 2018 Artificial Intelligence Cookbook - Second Edition
Published in: Aug 2018
Publisher:
ISBN-13: 9781788626170
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime
Banner background image