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
Unity 2018 Artificial Intelligence Cookbook
Unity 2018 Artificial Intelligence Cookbook

Unity 2018 Artificial Intelligence Cookbook: Over 90 recipes to build and customize AI entities for your games with Unity , Second Edition

Arrow left icon
Profile Icon Jorge Elieser P Garrido Profile Icon Jorge Palacios
Arrow right icon
$9.99 $39.99
Full star icon Full star icon Half star icon Empty star icon Empty star icon 2.4 (5 Ratings)
eBook Aug 2018 334 pages 2nd Edition
eBook
$9.99 $39.99
Paperback
$48.99
Subscription
Free Trial
Renews at $19.99p/m
Arrow left icon
Profile Icon Jorge Elieser P Garrido Profile Icon Jorge Palacios
Arrow right icon
$9.99 $39.99
Full star icon Full star icon Half star icon Empty star icon Empty star icon 2.4 (5 Ratings)
eBook Aug 2018 334 pages 2nd Edition
eBook
$9.99 $39.99
Paperback
$48.99
Subscription
Free Trial
Renews at $19.99p/m
eBook
$9.99 $39.99
Paperback
$48.99
Subscription
Free Trial
Renews at $19.99p/m

What do you get with eBook?

Product feature icon Instant access to your Digital eBook purchase
Product feature icon Download this book in EPUB and PDF formats
Product feature icon Access this title in our online reader with advanced features
Product feature icon DRM FREE - Read whenever, wherever and however you want
OR
Modal Close icon
Payment Processing...
tick Completed

Billing Address

Table of content icon View table of contents Preview book icon Preview Book

Unity 2018 Artificial Intelligence Cookbook

Behaviors - Intelligent Movement

In this chapter, we will develop AI algorithms for movement by covering the following recipes:

  • Creating the behaviors template
  • Pursuing and evading
  • Adjusting the agent for physics
  • Arriving and leaving
  • Facing objects
  • Wandering around
  • Following a path
  • Avoiding agents
  • Avoiding walls
  • Blending behaviors by weight
  • Blending behaviors by priority
  • Shooting a projectile
  • Predicting a projectile's landing spot
  • Targeting a projectile
  • Creating a jump system

Introduction

Unity has been one of the most popular game engines for quite a while now, and it's probably the de facto game development tool for indie developers, not only because of its business model, which has a low entry barrier, but also because of its robust project editor, year-by-year technological improvements, and, most importantly, its ease of use and an ever-growing community of developers around the globe.

Thanks to Unity's heavy lifting behind the scenes (rendering, physics, integration, and cross-platform deployment, to name but a few) it's possible for us to focus on creating the AI systems that will bring our games to life, creating great real-time experiences in the blink of an eye.

The goal of this book is to give you the tools to build great AI, create better enemies, polish that final boss, or even build your own customized AI engine.

In this chapter, we will start by exploring some of the most interesting movement algorithms based on the steering behavior principles developed by Craig Reynolds, along with work from Ian Millington. These recipes are the stepping stones for most of the AI used in advanced games and other algorithms that rely on movement, such as the family of path-finding algorithms.

Creating the behaviors template

Before creating our behaviors, we need to code the stepping stones that help us to create not only intelligent movement, but also help us to build a modular system to change and add these behaviors. We will create custom data types and base classes for most of the algorithms covered in this chapter.

Getting ready

Our first step is to remember the update functions' order of execution:

  • Update
  • LateUpdate

How to do it...

We need to create three classes, Steering, AgentBehaviour, and Agent:

  1. Steering serves as a custom data type for storing the movement and rotation of the agent:
using UnityEngine; 
public class Steering 
{ 
  public float angular; 
  public Vector3 linear; 
  public Steering () 
  { 
    angular = 0.0f; 
    linear = new Vector3(); 
  } 
} 
  1. AgentBehaviour is the template class for most of the behaviors covered in this chapter:
using UnityEngine; 
public class AgentBehaviour : MonoBehaviour 
{ 
  public GameObject target; 
  protected Agent agent; 
  public virtual void Awake () 
  { 
    agent = gameObject.GetComponent<Agent>(); 
  } 
  public virtual void Update () 
  { 
      agent.SetSteering(GetSteering()); 
  } 
  public virtual Steering GetSteering () 
  { 
    return new Steering(); 
  } 
} 
  1. Finally, Agent is the main component, and it makes use of behaviors in order to create intelligent movement. Create the file and its bare bones:
using UnityEngine; 
using System.Collections; 
public class Agent : MonoBehaviour 
{ 
    public float maxSpeed; 
    public float maxAccel; 
    public float orientation; 
    public float rotation; 
    public Vector3 velocity; 
    protected Steering steering; 
    void Start () 
    { 
        velocity = Vector3.zero; 
        steering = new Steering(); 
    } 
    public void SetSteering (Steering steering) 
    { 
        this.steering = steering; 
    } 
} 
  1. Next, we code the Update function, which handles the movement according to the current value:
public virtual void Update () 
{ 
    Vector3 displacement = velocity * Time.deltaTime; 
    orientation += rotation * Time.deltaTime; 
    // we need to limit the orientation values 
    // to be in the range (0 - 360) 
    if (orientation < 0.0f) 
        orientation += 360.0f; 
    else if (orientation > 360.0f) 
        orientation -= 360.0f; 
    transform.Translate(displacement, Space.World); 
    transform.rotation = new Quaternion(); 
    transform.Rotate(Vector3.up, orientation); 
} 
  1. Finally, we implement the LateUpdate function, which takes care of updating the steering for the next frame according to the current frame's calculations:
public virtual void LateUpdate () 
{ 
    velocity += steering.linear * Time.deltaTime; 
    rotation += steering.angular * Time.deltaTime; 
    if (velocity.magnitude > maxSpeed) 
    { 
        velocity.Normalize(); 
        velocity = velocity * maxSpeed; 
    } 
    if (steering.angular == 0.0f) 
    { 
        rotation = 0.0f; 
    } 
    if (steering.linear.sqrMagnitude == 0.0f) 
    { 
        velocity = Vector3.zero; 
    } 
    steering = new Steering(); 
} 

How it works...

The idea is to be able to delegate the movement's logic inside the GetSteering() function on the behaviors that we will later build, simplifying our agent's class to a main calculation based on those.

Besides, we are guaranteed to be able to set the agent's steering value before it is used thanks to Unity script and function execution orders.

There's more...

This is a component-based approach, which means that we must remember to always have an Agent script attached to GameObject for the behaviors to work as expected.

See also

Pursuing and evading

Pursuing and evading are great behaviors to start with because they rely on the most basic behaviors and extend their functionality by predicting the target's next step.

Getting ready

We need a couple of basic behaviors called Seek and Flee; place them right after the Agent class in the scripts' execution order.

The following is the code for the Seek behavior:

using UnityEngine; 
using System.Collections; 
public class Seek : AgentBehaviour 
{ 
    public override Steering GetSteering() 
    { 
        Steering steering = new Steering(); 
        steering.linear = target.transform.position - transform.position; 
        steering.linear.Normalize(); 
        steering.linear = steering.linear * agent.maxAccel; 
        return steering; 
    } 
} 

Also, we need to implement the Flee behavior:

using UnityEngine; 
using System.Collections; 
public class Flee : AgentBehaviour 
{ 
    public override Steering GetSteering() 
    { 
        Steering steering = new Steering(); 
        steering.linear = transform.position - target.transform.position; 
        steering.linear.Normalize(); 
        steering.linear = steering.linear * agent.maxAccel; 
        return steering; 
    } 
}

How to do it...

Pursue and Evade are essentially the same algorithm, but differ in terms of the base class they derive from:

  1. Create the Pursue class, derived from Seek, and add the attributes for the prediction:
using UnityEngine; 
using System.Collections; 
 
public class Pursue : Seek 
{ 
    public float maxPrediction; 
    private GameObject targetAux; 
    private Agent targetAgent; 
} 
  1. Implement the Awake function in order to set up everything according to the real target:
public override void Awake() { base.Awake(); targetAgent = target.GetComponent<Agent>(); targetAux = target; target = new GameObject(); } 
  1. Implement the OnDestroy function for properly handling the internal object:
void OnDestroy () 
{ 
    Destroy(targetAux); 
} 
  1. Implement the GetSteering function:
public override Steering GetSteering() 
{ 
    Vector3 direction = targetAux.transform.position - transform.position; 
    float distance = direction.magnitude; 
    float speed = agent.velocity.magnitude; 
    float prediction; 
    if (speed <= distance / maxPrediction) 
        prediction = maxPrediction; 
    else 
        prediction = distance / speed; 
    target.transform.position = targetAux.transform.position; 
    target.transform.position += targetAgent.velocity * prediction; 
    return base.GetSteering(); 
} 
  1. To create the Evade behavior, the procedure is just the same, but it takes into account the fact that Flee is the parent class:
public class Evade : Flee 
{ 
    // everything stays the same 
} 

How it works...

These behaviors rely on Seek and Flee and take into consideration the target's velocity in order to predict where it will go next, and they aim at that position using an internal extra object.

Adjusting the agent for physics

We learned how to implement simple behaviors for our agents. However, we also need to take into consideration that our games will probably need the help of the physics engine in Unity. In that case, we need to take into consideration whether our agents have the RigidBody component attached to them, and adjust our implementation accordingly.

Getting ready

Our first step is to remember the execution order of event functions, but now we also consider FixedUpdate, because we're handling behaviors on top of the physics engine:

  • FixedUpdate
  • Update
  • LateUpdate

How to do it...

This recipe entails adding changes to our Agent class.

  1. Go to the Agent class.
  2. Add a member variable for storing the rigid body component's reference:
private Rigidbody aRigidBody; 
  1. Get the reference to the rigid body component in the Start function:
aRigidBody = GetComponent<Rigidbody>(); 
  1. Implement a function for transforming an orientation value to a vector:
public Vector3 OriToVec(float orientation) 
{ 
  Vector3 vector = Vector3.zero; 
  vector.x = Mathf.Sin(orientation * Mathf.Deg2Rad) * 1.0f; 
  vector.z = Mathf.Cos(orientation * Mathf.Deg2Rad) * 1.0f; 
  return vector.normalized; 
} 
  1. Add the following two lines at the beginning of the Update function:
public virtual void Update () 
{ 
  if (aRigidBody == null) 
    return; 
  // ... previous code 
  1. Define the FixedUpdate function:
public virtual void FixedUpdate() 
{ 
  if (aRigidBody == null) 
    return; 
  // next step 
} 
  1. Continue implementing the FixedUpdate function:
Vector3 displacement = velocity * Time.deltaTime; 
orientation += rotation * Time.deltaTime; 
if (orientation < 0.0f) 
  orientation += 360.0f; 
else if (orientation > 360.0f) 
  orientation -= 360.0f; 
// The ForceMode will depend on what you want to achieve 
// We are using VelocityChange for illustration purposes 
aRigidBody.AddForce(displacement, ForceMode.VelocityChange); 
Vector3 orientationVector = OriToVec(orientation); 
aRigidBody.rotation = Quaternion.LookRotation(orientationVector, Vector3.up); 

How it works...

We added a member variable for storing the reference to a possible rigid body component, and also implemented FixedUpdate, similar to Update, but taking into consideration that we need to apply force to the rigid body instead of translating the object ourselves, because we're working on top of Unity's physics engine.

Finally, we created a simple validation at the beginning of each function so they're called only when it applies.

See also

Arriving and leaving

Similar to Seek and Flee, the idea behind these algorithms is to apply the same principles and extend the functionality to a point where the agent stops automatically after a condition is met, either being close to its destination (arrive), or far enough from a dangerous point (leave).

Getting ready

We need to create one file for the Arrive and Leave algorithms respectively, and remember to set their custom execution order.

How to do it...

They use the same approach, but in terms of implementation, the name of the member variables change, as well as some computations in the first half of the GetSteering function:

  1. First, implement the Arrive behavior with its member variables to define the radius for stopping (target) and slowing down:
using UnityEngine; 
using System.Collections; 
 
public class Arrive : AgentBehaviour 
{ 
    public float targetRadius; 
    public float slowRadius; 
    public float timeToTarget = 0.1f; 
} 
  1. Create the GetSteering function:
public override Steering GetSteering() 
{ 
    // code in next steps 
} 
  1. Define the first half of the GetSteering function, in which we compute the desired speed depending on the distance from the target according to the radii variables:
Steering steering = new Steering(); 
Vector3 direction = target.transform.position - transform.position; 
float distance = direction.magnitude; 
float targetSpeed; 
if (distance < targetRadius) 
    return steering; 
if (distance > slowRadius) 
    targetSpeed = agent.maxSpeed; 
else 
    targetSpeed = agent.maxSpeed * distance / slowRadius; 
  1. Define the second half of the GetSteering function, in which we set the steering value and clamp it according to the maximum speed:
Vector3 desiredVelocity = direction; 
desiredVelocity.Normalize(); 
desiredVelocity *= targetSpeed; 
steering.linear = desiredVelocity - agent.velocity; 
steering.linear /= timeToTarget; 
if (steering.linear.magnitude > agent.maxAccel) 
{ 
    steering.linear.Normalize(); 
    steering.linear *= agent.maxAccel; 
} 
return steering; 
  1. To implement Leave, change the name of the member variables:
using UnityEngine; 
using System.Collections; 
 
public class Leave : AgentBehaviour 
{ 
    public float escapeRadius; 
    public float dangerRadius; 
    public float timeToTarget = 0.1f; 
} 
  1. Define the first half of the GetSteering function:
Steering steering = new Steering(); 
Vector3 direction = transform.position - target.transform.position; 
float distance = direction.magnitude; 
if (distance > dangerRadius) 
    return steering; 
float reduce; 
if (distance < escapeRadius) 
    reduce = 0f; 
else 
    reduce = distance / dangerRadius * agent.maxSpeed; 
float targetSpeed = agent.maxSpeed - reduce; 
  1. And finally, the second half of GetSteering stays just the same.

How it works...

After calculating the direction to go in, the next calculations are based on two radii distances in order to know when to go full throttle, slow down, and stop; that's why we have several if statements. In the Arrive behavior, when the agent is too far, we aim for full throttle, progressively slow down when inside the proper radius, and finally stop when close enough to the target. The inverse method applies to Leave:

A visual reference for the Arrive and Leave behaviors

Facing objects

Real-world aiming, just like in combat simulators, works a little differently to the widely-used automatic aiming process in almost every game. Imagine that you need to implement an agent controlling a tank turret or a humanized sniper; that's when this recipe comes in handy.

Getting ready

We need to make some modifications to our AgentBehaviour class:

  1. Add new member values to limit some of the existing ones:
public float maxSpeed; 
public float maxAccel; 
public float maxRotation; 
public float maxAngularAccel;

  1. Add a function called MapToRange. This function helps in finding the actual direction of rotation after two orientation values are subtracted:
public float MapToRange (float rotation) { 
    rotation %= 360.0f; 
    if (Mathf.Abs(rotation) > 180.0f) { 
        if (rotation < 0.0f) 
            rotation += 360.0f; 
        else 
            rotation -= 360.0f; 
    } 
    return rotation; 
} 
  1. Also, we need to create a basic behavior called Align that is the stepping stone for the facing algorithm. It uses the same principle as Arrive, but only in terms of rotation:
using UnityEngine; 
using System.Collections; 
 
public class Align : AgentBehaviour 
{ 
    public float targetRadius; 
    public float slowRadius; 
    public float timeToTarget = 0.1f; 
 
    public override Steering GetSteering() 
    { 
        Steering steering = new Steering(); 
        float targetOrientation = target.GetComponent<Agent>().orientation; 
        float rotation = targetOrientation - agent.orientation; 
        rotation = MapToRange(rotation); 
        float rotationSize = Mathf.Abs(rotation); 
        if (rotationSize < targetRadius) 
            return steering; 
        float targetRotation; 
        if (rotationSize > slowRadius) 
            targetRotation = agent.maxRotation; 
        else 
            targetRotation = agent.maxRotation * rotationSize / slowRadius; 
        targetRotation *= rotation / rotationSize; 
        steering.angular = targetRotation - agent.rotation; 
        steering.angular /= timeToTarget; 
        float angularAccel = Mathf.Abs(steering.angular); 
        if (angularAccel > agent.maxAngularAccel) 
        { 
            steering.angular /= angularAccel; 
            steering.angular *= agent.maxAngularAccel; 
        } 
        return steering; 
    } 
} 

How to do it...

We can now proceed to implement our facing algorithm that derives from Align:

  1. Create the Face class along with a private auxiliary target member variable:
using UnityEngine; 
using System.Collections; 
 
public class Face : Align 
{ 
    protected GameObject targetAux; 
} 
  1. Override the Awake function to set up everything and swap references:
public override void Awake() 
{ 
    base.Awake(); 
    targetAux = target; 
    target = new GameObject(); 
    target.AddComponent<Agent>(); 
} 
  1. Also, implement the OnDestroy function to handle references and avoid memory issues:
void OnDestroy () 
{ 
    Destroy(target); 
}

  1. Finally, define the GetSteering function:
public override Steering GetSteering() 
{ 
    Vector3 direction = targetAux.transform.position - transform.position; 
    if (direction.magnitude > 0.0f) 
    { 
        float targetOrientation = Mathf.Atan2(direction.x, direction.z); 
        targetOrientation *= Mathf.Rad2Deg; 
        target.GetComponent<Agent>().orientation = targetOrientation; 
    } 
    return base.GetSteering(); 
}

How it works...

The algorithm computes the internal target orientation according to the vector between the agent and the real target. Then, it just delegates the work to its parent class.

Wandering around

This technique works like a charm for random crowd simulations, animals, and almost any kind of NPC that requires random movement when idle.

Getting ready

We need to add another function to our AgentBehaviour class called OriToVec, which converts an orientation value to a vector:

public Vector3 GetOriAsVec (float orientation) { 
    Vector3 vector  = Vector3.zero; 
    vector.x = Mathf.Sin(orientation * Mathf.Deg2Rad) * 1.0f; 
    vector.z = Mathf.Cos(orientation * Mathf.Deg2Rad) * 1.0f; 
    return vector.normalized; 
} 

How to do it...

We can regard it as a big three-step process in which we first manipulate the internal target position in a parameterized random way, face that position, and move accordingly:

  1. Create the Wander class deriving from Face:
using UnityEngine; 
using System.Collections; 
 
public class Wander : Face 
{ 
    public float offset; 
    public float radius; 
    public float rate; 
} 
  1. Define the Awake function in order to set up the internal target:
public override void Awake() 
{ 
    target = new GameObject(); 
    target.transform.position = transform.position; 
    base.Awake(); 
} 
  1. Define the GetSteering function:
public override Steering GetSteering() 
{ 
    Steering steering = new Steering(); 
    float wanderOrientation = Random.Range(-1.0f, 1.0f) * rate; 
    float targetOrientation = wanderOrientation + agent.orientation; 
    Vector3 orientationVec = OriToVec(agent.orientation); 
    Vector3 targetPosition = (offset * orientationVec) + transform.position; 
    targetPosition = targetPosition + (OriToVec(targetOrientation) * radius); 
    targetAux.transform.position = targetPosition; 
    steering = base.GetSteering(); 
    steering.linear = targetAux.transform.position - transform.position; 
    steering.linear.Normalize(); 
    steering.linear *= agent.maxAccel; 
    return steering; 
} 

How it works...

The behavior takes two radii in order into consideration to get a random position to go next, looks toward that random point, and converts the computed orientation into a direction vector in order to advance:

A visual description of the parameters for creating the Wander behavior

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

Avoiding agents

In crowd-simulation games, it would be unnatural to see agents behaving entirely like particles in a physics-based system. The goal of this recipe is to create an agent capable of mimicking our peer-evasion movement.

Getting ready

We need to create a tag called Agent and assign it to those game objects that we would like to avoid, and we also need to have the Agent script component attached to them:

Example of how the Inspector window of a dummy agent should appear

Take a look on the following:

  • Tag: Agent (created by us)
  • Agent component is attached (the one created by us)

How to do it...

This recipe will entail creating a new agent behavior:

  1. Create the AvoidAgent behavior, which is composed of a collision avoidance radius and the list of agents to avoid:
using UnityEngine; 
using System.Collections; 
using System.Collections.Generic; 
 
public class AvoidAgent : AgentBehaviour 
{ 
    public float collisionRadius = 0.4f; 
    GameObject[] targets; 
} 
  1. Implement the Start function in order to set the list of agents according to the tag we created earlier:
void Start () 
{ 
    targets = GameObject.FindGameObjectsWithTag("Agent"); 
} 
  1. Define the GetSteering function:
public override Steering GetSteering() 
{ 
    // body 
} 
  1. Add the following variables to the compute distances and velocities from agents that are nearby:
Steering steering = new Steering(); 
float shortestTime = Mathf.Infinity; 
GameObject firstTarget = null; 
float firstMinSeparation = 0.0f; 
float firstDistance = 0.0f; 
Vector3 firstRelativePos = Vector3.zero; 
Vector3 firstRelativeVel = Vector3.zero; 
  1. Find the closest agent that is prone to collision with the current one:
foreach (GameObject t in targets) 
{ 
    Vector3 relativePos; 
    Agent targetAgent = t.GetComponent<Agent>(); 
    relativePos = t.transform.position - transform.position; 
    Vector3 relativeVel = targetAgent.velocity - agent.velocity; 
    float relativeSpeed = relativeVel.magnitude; 
    float timeToCollision = Vector3.Dot(relativePos, relativeVel); 
    timeToCollision /= relativeSpeed * relativeSpeed * -1; 
    float distance = relativePos.magnitude; 
    float minSeparation = distance - relativeSpeed * timeToCollision; 
    if (minSeparation > 2 * collisionRadius) 
        continue; 
    if (timeToCollision > 0.0f && timeToCollision < shortestTime) 
    { 
        shortestTime = timeToCollision; 
        firstTarget = t; 
        firstMinSeparation = minSeparation; 
        firstRelativePos = relativePos; 
        firstRelativeVel = relativeVel; 
    } 
} 
  1. If there is one that is prone to collision, then move away:
if (firstTarget == null) 
    return steering; 
if (firstMinSeparation <= 0.0f || firstDistance < 2 * collisionRadius) 
    firstRelativePos = firstTarget.transform.position; 
else 
    firstRelativePos += firstRelativeVel * shortestTime; 
firstRelativePos.Normalize(); 
steering.linear = -firstRelativePos * agent.maxAccel; 
return steering; 

How it works...

Given a list of agents, we take into consideration which one is closest, and, if it is close enough, we make it so that the agent tries to escape from the expected route of that first one according to its current velocity, so that they don't collide.

There's more...

This behavior works well when combined with other behaviors using blending techniques (some are included in this chapter); otherwise, it's a starting point for your own collision avoidance algorithms.

Avoiding walls

In this recipe, we will implement a behavior that imitates our own ability to evade walls. That is, seeing what we have in front of us that could be considered as a wall or obstacle, and walk around it using a safety margin, trying to maintain our principal direction at the same time.

Getting ready

This technique uses the RaycastHit structure and the Raycast function from the physics engine, so it's recommended that you look at the documents for a refresher in case you're a little rusty on the subject.

How to do it...

Thanks to our previous hard work, this recipe is a short one:

  1. Create the AvoidWall behavior derived from Seek:
using UnityEngine; 
using System.Collections; 
 
public class AvoidWall : Seek 
{ 
    // body 
} 
  1. Include the member variables for defining the safety margin and the length of the ray to cast:
public float avoidDistance; 
public float lookAhead; 
  1. Define the Awake function to set up the target:
public override void Awake() 
{ 
    base.Awake(); 
    target = new GameObject(); 
} 
  1. Define the GetSteering function required for future steps:
public override Steering GetSteering() 
{ 
    // body 
}

  1. Declare and set the variable needed for ray casting:
Steering steering = new Steering(); 
Vector3 position = transform.position; 
Vector3 rayVector = agent.velocity.normalized * lookAhead; 
Vector3 direction = rayVector; 
RaycastHit hit; 
  1. Cast the ray and make the proper calculations if a wall is hit:
if (Physics.Raycast(position, direction, out hit, lookAhead)) 
{ 
    position = hit.point + hit.normal * avoidDistance; 
    target.transform.position = position; 
    steering = base.GetSteering(); 
} 
return steering; 

How it works...

We cast a ray in front of the agent, and when the ray collides with a wall, the target object is placed in a new position, with consideration given to its distance from the wall and the safety distance declared, delegating the steering calculations to the Seek behavior; this creates the illusion of the agent avoiding the wall.

There's more...

We could extend this behavior by adding more rays, such as whiskers, in order to achieve better accuracy. Also, it is usually paired with other movement behaviors, such as Pursue, using blending:

The original ray cast and possible extensions for more precise wall avoidance

See also

Blending behaviors by weight

The blending techniques allow you to add behaviors and mix them without creating new scripts every time you need a new type of hybrid agent.

This is one of the most powerful techniques in this chapter, and it's probably the most widely-used behavior-blending approach because of its power and the low cost of implementation.

Getting ready

We must add a new member variable to our AgentBehaviour class called weight and preferably assign a default value. In this case, this is 1.0f. We should refactor the Update function to incorporate weight as a parameter for the Agent class's SetSteering function. All in all, the new AgentBehaviour class should look something like this:

public class AgentBehaviour : MonoBehaviour 
{ 
    public float weight = 1.0f; 
 
    // ... the rest of the class 
 
    public virtual void Update () 
    { 
        agent.SetSteering(GetSteering(), weight); 
   } 
}

How to do it...

We just need to change the SetSteering function's signature and definition:

public void SetSteering (Steering steering, float weight) 
{ 
    this.steering.linear += (weight * steering.linear); 
    this.steering.angular += (weight * steering.angular); 
} 

How it works...

The weights are used to amplify the steering behavior result, and they're added to the main steering structure.

There's more...

The weights don't necessarily need to add up to 1.0f. The weight parameter is a reference for defining the relevance that the steering behavior will have among other parameters.

See also

In this project, there is an example of avoiding walls, which is worked out using weighted blending.

Blending behaviors by priority

Sometimes, weighted blending is not enough because heavyweight behaviors dilute the contribution of the lightweights, but those behaviors need to do their part too. That's when priority-based blending comes into play, applying a cascading effect from high-priority to low-priority behaviors.

Getting ready

This approach is very similar to the one used in the previous recipe. We must add a new member variable to our AgentBehaviour class. We should also refactor the Update function to incorporate priority as a parameter to the Agent class's SetSteering function. The new AgentBehaviour class should look something like this:

public class AgentBehaviour : MonoBehaviour 
{ 
    public int priority = 1; 
    // ... everything else stays the same 
    public virtual void Update () 
    { 
        agent.SetSteering(GetSteering(), priority); 
    } 
} 

How to do it...

Now, we need to make some changes to the Agent class:

  1. Add a new namespace from the library:
using System.Collections.Generic; 
  1. Add the member variable for the minimum steering value to consider a group of behaviors:
public float priorityThreshold = 0.2f; 
  1. Add the member variable for holding the group of behavior results:
private Dictionary<int, List<Steering>> groups; 
  1. Initialize the variable in the Start function:
groups = new Dictionary<int, List<Steering>>(); 
  1. Modify the LateUpdate function so that the steering variable is set by calling GetPrioritySteering:
public virtual void LateUpdate () 
{ 
    //  funnelled steering through priorities 
    steering = GetPrioritySteering(); 
    groups.Clear(); 
    // ... the rest of the computations stay the same 
    steering = new Steering(); 
} 
  1. Modify the SetSteering function's signature and definition to store the steering values in their corresponding priority groups:
public void SetSteering (Steering steering, int priority) 
{ 
    if (!groups.ContainsKey(priority)) 
    { 
        groups.Add(priority, new List<Steering>()); 
    } 
    groups[priority].Add(steering); 
} 
  1. Finally, implement the GetPrioritySteering function to funnel the steering group:
private Steering GetPrioritySteering () 
{ 
    Steering steering = new Steering(); 
    float sqrThreshold = priorityThreshold * priorityThreshold; 
    foreach (List<Steering> group in groups.Values) 
    { 
        steering = new Steering(); 
        foreach (Steering singleSteering in group) 
        { 
            steering.linear += singleSteering.linear; 
            steering.angular += singleSteering.angular; 
        } 
        if (steering.linear.sqrMagnitude > sqrThreshold || 
                Mathf.Abs(steering.angular) > priorityThreshold) 
        { 
            return steering; 
        } 
    } 
    return steering; 
}

How it works...

By creating priority groups, we blend behaviors that are common to one another, and the first group, in which the steering value exceeds the threshold, is selected. Otherwise, steering from the lowest-priority group is chosen.

There's more...

We could extend this approach by mixing it with weighted blending, so we would have a more robust architecture, achieving extra precision in the way the behaviors impact on the agent at every priority level:

foreach (Steering singleSteering in group) 
{ 
    steering.linear += singleSteering.linear * weight; 
    steering.angular += singleSteering.angular * weight; 
} 

See also

There is an example of avoiding walls using priority-based blending in this project.

Shooting a projectile

This is the stepping stone for scenarios where we want to have control over gravity-reliant objects, such as balls and grenades, so we can then predict the projectile's landing spot or be able to effectively shoot a projectile at a given target.

Getting ready

This recipe differs a little bit as it doesn't rely on the base AgentBehaviour class.

How to do it...

  1. Create the Projectile class, along with its member variables, to handle the physics:
using UnityEngine; 
using System.Collections; 
 
public class Projectile : MonoBehaviour 
{ 
    private bool set = false; 
    private Vector3 firePos; 
    private Vector3 direction; 
    private float speed; 
    private float timeElapsed; 
} 
  1. Define the Update function:
void Update () 
{ 
    if (!set) 
        return; 
    timeElapsed += Time.deltaTime; 
    transform.position = firePos + direction * speed * timeElapsed; 
    transform.position += Physics.gravity * (timeElapsed * timeElapsed) / 2.0f; 
    // extra validation for cleaning the scene 
    if (transform.position.y < -1.0f) 
        Destroy(this.gameObject);// or set = false; and hide it 
} 
  1. Finally, implement the Set function in order to fire the game object (for example, calling it after it is instantiated in the scene):
public void Set (Vector3 firePos, Vector3 direction, float speed) 
{ 
    this.firePos = firePos; 
    this.direction = direction.normalized; 
    this.speed = speed; 
    transform.position = firePos; 
    set = true; 
} 

How it works...

This behavior uses high school physics in order to generate the parabolic movement.

There's more...

We could also take another approach: implementing public properties in the script or declaring member variables as public, and instead of calling the Set function, having the script disabled by default in the prefab and enabling it after all the properties have been set. That way, we could easily apply the object pool pattern.

See also

Predicting a projectile's landing spot

After a projectile is shot by the player, agents (our AI) need to either avoid it or look for it. For example, the agents need to run from a grenade to be kept alive, or run towards a soccer ball to take control. In either case, it's important for the agents to predict the projectile's landing spot to make decisions.

In this recipe, we will learn how to calculate such landing spots.

Getting ready

Before we get into predicting the landing position, it's important to know the time left before it hits the ground (or reaches a certain position). Thus, instead of creating new behaviors, we need to update the Projectile class.

How to do it...

  1. First, we need to add the GetLandingTime function to compute the landing time:
public float GetLandingTime (float height = 0.0f) 
{ 
    Vector3 position = transform.position; 
    float time = 0.0f; 
    float valueInt = (direction.y * direction.y) * (speed * speed); 
    valueInt = valueInt - (Physics.gravity.y * 2 * (position.y - height)); 
    valueInt = Mathf.Sqrt(valueInt); 
    float valueAdd = (-direction.y) * speed; 
    float valueSub = (-direction.y) * speed; 
    valueAdd = (valueAdd + valueInt) / Physics.gravity.y; 
    valueSub = (valueSub - valueInt) / Physics.gravity.y; 
    if (float.IsNaN(valueAdd) && !float.IsNaN(valueSub)) 
        return valueSub; 
    else if (!float.IsNaN(valueAdd) && float.IsNaN(valueSub)) 
        return valueAdd; 
    else if (float.IsNaN(valueAdd) && float.IsNaN(valueSub)) 
        return -1.0f; 
    time = Mathf.Max(valueAdd, valueSub); 
    return time; 
} 
  1. Now, we add the GetLandingPos function to predict the landing spot:
public Vector3 GetLandingPos (float height = 0.0f) 
{ 
    Vector3 landingPos = Vector3.zero; 
    float time = GetLandingTime(); 
    if (time < 0.0f) 
        return landingPos; 
    landingPos.y = height; 
    landingPos.x = firePos.x + direction.x * speed * time; 
    landingPos.z = firePos.z + direction.z * speed * time; 
    return landingPos; 
}

How it works...

First, we are solving the equation from the previous recipe for a fixed height, and, given the projectile's current position and speed, we are able to get the time at which the projectile will reach the given height.

There's more...

Remember to take into account the NaN validation. It's placed that way because there may be one, two, or no solutions to the equation. Furthermore, when the landing time is less than zero, it means the projectile won't be able to reach the target height.

Targeting a projectile

Just as it's important to predict a projectile's landing point, it's also important to develop intelligent agents capable of aiming projectiles. It wouldn't be fun if our rugby-player agents weren't capable of passing the ball.

Getting ready

Just as in the previous recipe, we only need to expand the Projectile class.

How to do it...

Thanks to our previous hard work, this recipe is a real piece of cake:

  1. Create the GetFireDirection function:
public static Vector3 GetFireDirection (Vector3 startPos, Vector3 endPos, float speed) 
{ 
    // body 
}

  1. Solve the corresponding quadratic equation:
Vector3 direction = Vector3.zero; 
Vector3 delta = endPos - startPos; 
float a = Vector3.Dot(Physics.gravity, Physics.gravity); 
float b = -4 * (Vector3.Dot(Physics.gravity, delta) + speed * speed); 
float c = 4 * Vector3.Dot(delta, delta); 
if (4 * a * c > b * b) 
    return direction; 
float time0 = Mathf.Sqrt((-b + Mathf.Sqrt(b * b - 4 * a * c)) / (2*a)); 
float time1 = Mathf.Sqrt((-b - Mathf.Sqrt(b * b - 4 * a * c)) / (2*a)); 
  1. If shooting the projectile is feasible given the parameters, return a non-zero direction vector:
float time; 
if (time0 < 0.0f) 
{ 
    if (time1 < 0) 
        return direction; 
    time = time1; 
} 
else 
{ 
    if (time1 < 0) 
        time = time0; 
    else 
        time = Mathf.Min(time0, time1); 
} 
direction = 2 * delta - Physics.gravity * (time * time); 
direction = direction / (2 * speed * time); 
return direction; 

How it works...

Given a fixed speed, we solve the corresponding quadratic equation in order to obtain the desired direction (when at least a single one-time value is available), which doesn't need to be normalized because we already normalized the vector while setting up the projectile.

There's more...

Take account of the fact that we are returning a blank direction when the time is negative; it means that the speed is not sufficient. One way to overcome this is to define a function that tests different speeds and then shoot the projectile.

Another relevant improvement is to add an extra parameter of the type bool for those cases when we have two valid times (which means two possible arcs), and we need to shoot over an obstacle such as a wall:

if (isWall) 
    time = Mathf.Max(time0, time1); 
else 
    time = Mathf.Min(time0, time1); 

Creating a jump system

Imagine that we're developing a cool action game where the player is capable of escaping using cliffs and rooftops. In that case, the enemies need to be able to chase the player and be smart enough to discern whether to take the jump and gauge how to do it.

Getting ready

We need to create a basic matching-velocity algorithm and the notion of jump pads and landing pads in order to emulate velocity math so that we can reach them.

The following is the code for the VelocityMatch behavior:

using UnityEngine; 
using System.Collections; 
 
public class VelocityMatch : AgentBehaviour { 
     
    public float timeToTarget = 0.1f; 
 
    public override Steering GetSteering() 
    { 
        Steering steering = new Steering(); 
        steering.linear = target.GetComponent<Agent>().velocity - agent.velocity; 
        steering.linear /= timeToTarget; 
        if (steering.linear.magnitude > agent.maxAccel) 
            steering.linear = steering.linear.normalized * agent.maxAccel; 
 
        steering.angular = 0.0f; 
        return steering; 
    } 
} 

Also, it's important to create a data type called JumpPoint:

using UnityEngine; 
using System.Collections; 
 
public class JumpPoint  
{ 
    public Vector3 jumpLocation; 
    public Vector3 landingLocation; 
 
    //The change in position from jump to landing 
    public Vector3 deltaPosition; 
 
    public JumpPoint () : this (Vector3.zero, Vector3.zero) 
    { 
    } 
 
    public JumpPoint(Vector3 a, Vector3 b) 
    { 
        this.jumpLocation = a; 
        this.landingLocation = b; 
        this.deltaPosition = this.landingLocation - this.jumpLocation; 
    } 
} 

How to do it...

  1. Create the Jump script along with its member variables:
using UnityEngine; 
using System.Collections; 
 
public class Jump : VelocityMatch 
{ 
    public JumpPoint jumpPoint; 
    public float maxYVelocity; 
    public Vector3 gravity = new Vector3(0, -9.8f, 0); 
    bool canAchieve = false; 
}

  1. Implement the SetJumpPoint function:
public void SetJumpPoint(Transform jumpPad, Transform landingPad) 
{ 
    jumpPoint = new JumpPoint(jumpPad.position, landingPad.position); 
} 
  1. Add a function to calculate the target:
protected void CalculateTarget() 
{ 
    target = new GameObject(); 
    target.AddComponent<Agent>(); 
    target.transform.position = jumpPoint.jumpLocation; 
    //Calculate the first jump time 
    float sqrtTerm = Mathf.Sqrt(2f * gravity.y * jumpPoint.deltaPosition.y + maxYVelocity * agent.maxSpeed); 
    float time = (maxYVelocity - sqrtTerm) / gravity.y; 
    //Check if we can use it, otherwise try the other time 
    if (!CheckJumpTime(time)) 
    { 
        time = (maxYVelocity + sqrtTerm) / gravity.y; 
    } 
} 
  1. Implement the CheckJumpTime function, to decide whether it's worth taking the jump:
private bool CheckJumpTime(float time) 
{ 
    //Calculate the planar speed 
    float vx = jumpPoint.deltaPosition.x / time; 
    float vz = jumpPoint.deltaPosition.z / time; 
    float speedSq = vx * vx + vz * vz; 
    //Check it to see if we have a valid solution 
    if (speedSq < agent.maxSpeed * agent.maxSpeed) 
    { 
        target.GetComponent<Agent>().velocity = new Vector3(vx, 0f, vz); 
        canAchieve = true; 
        return true; 
    } 
    return false; 
}

  1. Finally, define the GetSteering function:
public override Steering GetSteering() 
{ 
    Steering steering = new Steering(); 
    if (target == null) 
    { 
        CalculateTarget(); 
    } 
    if (!canAchieve) 
    { 
        return steering; 
    } 
    //Check if we've hit the jump point 
    if (Mathf.Approximately((transform.position - target.transform.position).magnitude, 0f) && 
        Mathf.Approximately((agent.velocity - target.GetComponent<Agent>().velocity).magnitude, 0f)) 
    { 
        // call a jump method based on the Projectile behaviour 
        return steering; 
    } 
    return base.GetSteering(); 
} 

How it works...

The algorithm takes into account the agent's velocity and calculates whether it can reach the landing pad or not. If it judges that the agent can, it tries to match the vertical velocity while seeking the landing pad's position.

Left arrow icon Right arrow icon

Key benefits

  • Explore different algorithms for creating decision-making agents that go beyond simple behaviors and movement
  • Discover the latest features of the NavMesh API for scripting intelligent behaviour in your game characters
  • Create games that are non-predictable and dynamic and have a high replayability factor

Description

Interactive and engaging games come with intelligent enemies, and this intellectual behavior is combined with a variety of techniques collectively referred to as Artificial Intelligence. Exploring Unity's API, or its built-in features, allows limitless possibilities when it comes to creating your game's worlds and characters. This cookbook covers both essential and niche techniques to help you take your AI programming to the next level. To start with, you’ll quickly run through the essential building blocks of working with an agent, programming movement, and navigation in a game environment, followed by improving your agent's decision-making and coordination mechanisms – all through hands-on examples using easily customizable techniques. You’ll then discover how to emulate the vision and hearing capabilities of your agent for natural and humanlike AI behavior, and later improve the agents with the help of graphs. This book also covers the new navigational mesh with improved AI and pathfinding tools introduced in the Unity 2018 update. You’ll empower your AI with decision-making functions by programming simple board games, such as tic-tac-toe and checkers, and orchestrate agent coordination to get your AIs working together as one. By the end of this book, you’ll have gained expertise in AI programming and developed creative and interactive games.

Who is this book for?

The Unity 2018 Artificial Intelligence Cookbook is for you if you are eager to get more tools under your belt to solve AI- and gameplay-related problems. Basic knowledge of Unity and prior knowledge of C# is an advantage.

What you will learn

  • Create intelligent pathfinding agents with popular AI techniques such as A and Ambush
  • Implement different algorithms for adding coordination between agents and tactical algorithms for different purposes
  • Simulate senses so agents can make better decisions, taking account of the environment
  • Explore different algorithms for creating decision-making agents that go beyond simple behaviors and movement
  • Create coordination between agents and orchestrate tactics when dealing with a graph or terrain
  • Implement waypoints by making a manual selector

Product Details

Country selected
Publication date, Length, Edition, Language, ISBN-13
Publication date : Aug 28, 2018
Length: 334 pages
Edition : 2nd
Language : English
ISBN-13 : 9781788625227
Vendor :
Unity Technologies
Languages :
Tools :

What do you get with eBook?

Product feature icon Instant access to your Digital eBook purchase
Product feature icon Download this book in EPUB and PDF formats
Product feature icon Access this title in our online reader with advanced features
Product feature icon DRM FREE - Read whenever, wherever and however you want
OR
Modal Close icon
Payment Processing...
tick Completed

Billing Address

Product Details

Publication date : Aug 28, 2018
Length: 334 pages
Edition : 2nd
Language : English
ISBN-13 : 9781788625227
Vendor :
Unity Technologies
Languages :
Tools :

Packt Subscriptions

See our plans and pricing
Modal Close icon
$19.99 billed monthly
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Simple pricing, no contract
$199.99 billed annually
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Choose a DRM-free eBook or Video every month to keep
Feature tick icon PLUS own as many other DRM-free eBooks or Videos as you like for just $5 each
Feature tick icon Exclusive print discounts
$279.99 billed in 18 months
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Choose a DRM-free eBook or Video every month to keep
Feature tick icon PLUS own as many other DRM-free eBooks or Videos as you like for just $5 each
Feature tick icon Exclusive print discounts

Frequently bought together


Stars icon
Total $ 158.97
Unity 2018 Artificial Intelligence Cookbook
$48.99
Unity 2018 By Example
$48.99
Unity 2018 Cookbook
$60.99
Total $ 158.97 Stars icon
Banner background image

Table of Contents

11 Chapters
Behaviors - Intelligent Movement Chevron down icon Chevron up icon
Navigation Chevron down icon Chevron up icon
Decision Making Chevron down icon Chevron up icon
The New NavMesh API Chevron down icon Chevron up icon
Coordination and Tactics Chevron down icon Chevron up icon
Agent Awareness Chevron down icon Chevron up icon
Board Games and Applied Search AI Chevron down icon Chevron up icon
Learning Techniques Chevron down icon Chevron up icon
Procedural Content Generation Chevron down icon Chevron up icon
Miscellaneous Chevron down icon Chevron up icon
Other Books You May Enjoy Chevron down icon Chevron up icon

Customer reviews

Rating distribution
Full star icon Full star icon Half star icon Empty star icon Empty star icon 2.4
(5 Ratings)
5 star 20%
4 star 0%
3 star 0%
2 star 60%
1 star 20%
Fernando Lovera Oct 12, 2018
Full star icon Full star icon Full star icon Full star icon Full star icon 5
I had a lot of fun reading this book, very interesting and simple explained, which shows good understanding of the author about these subjects.
Amazon Verified review Amazon
Brian Feb 19, 2019
Full star icon Full star icon Empty star icon Empty star icon Empty star icon 2
I simply can not recommend this book, especially for the price. It may be worth buying used for around $15 to serve as a code base. For a cookbook, it does a very poor job of explaining the specific problem that a particular chunk of code is designed to solve. This is especially true at the start of the book. I found myself reading through a lot of code that was just thrown at me with very little introduction... wondering what type of game world and what specific problem in that game world is this code designed to address. Usually cookbooks are organized by more specific objectives. The other side of the code block (where one normally expects an explanation of the code, reasoning behind why something was done this way or that, and how it may need to be modified to fit certain situations) was equally barren. With all of that said, there is some good example code in there if you are willing to do most of the hard work yourself in understanding how to implement it in your specific game project (hence the two star rating as opposed to one). But that is the key problem, example code and basic patterns can be found for free all over the Internet for free. A book is supposed to help to clarify things, put it in the clear context of an overarching theme, and help the reader adapt things to match their specific needs.
Amazon Verified review Amazon
Gabriel Campbell Mar 09, 2019
Full star icon Full star icon Empty star icon Empty star icon Empty star icon 2
this guy just copy pasted the AI chapters he wrote for the Complete Unity 2018 Game Development book, which is about the cost of this book alone. Or maybe it went the other direction Either way, just getting that book is a much better deal. His examples are all pretty basic stuff, and for an AI focused book really doesn't get into anything advanced or useful on the subject. His examples are mostly without any greater context, and often without full implementation. There will be a lot of code samples where the method is just empty except one comment that says "//TODO implement your code here". Most of the book is taken up with re-implementing your own version of state machines, agent steering and the like, rather than making use of existing stuff in Unity that would let you jump into more advanced ideas. It could be useful if you are very new to both c# and unity and want a basic intro to the idea behind navigation and FSMs and so on, but even then there are better, free ai tutorials online.
Amazon Verified review Amazon
Gir the Game Designer Feb 23, 2020
Full star icon Full star icon Empty star icon Empty star icon Empty star icon 2
Doesn't explain anything in detail and turns out that you can get the same tech done with Behaviour Designer on the Asset Store
Amazon Verified review Amazon
Ariadne Feb 25, 2019
Full star icon Empty star icon Empty star icon Empty star icon Empty star icon 1
I bought this book when there was just one (positive) review for it, and I wish I hadn’t. Each chapter consists of pages of code with no description about what to do with the scripts to make a working project. I downloaded the sample files and tried to run the project for the first chapter. It did run, but only because there are lots of other scripts in the project that are not mentioned in the book.The download itself is just a dump of all the scripts and prefabs for the whole book which makes it extremely difficult to work out which parts are needed for which chapter.It is a very discouraging book, not for a beginner or for anybody really. I am not new to AI (I have a MSc and a PhD in the subject) and I have made several Unity projects using other books and online resources, but this book has defeated me. I feel frustrated, disappointed and cheated and I cannot recommend this book.
Amazon Verified review Amazon
Get free access to Packt library with over 7500+ books and video courses for 7 days!
Start Free Trial

FAQs

How do I buy and download an eBook? Chevron down icon Chevron up icon

Where there is an eBook version of a title available, you can buy it from the book details for that title. Add either the standalone eBook or the eBook and print book bundle to your shopping cart. Your eBook will show in your cart as a product on its own. After completing checkout and payment in the normal way, you will receive your receipt on the screen containing a link to a personalised PDF download file. This link will remain active for 30 days. You can download backup copies of the file by logging in to your account at any time.

If you already have Adobe reader installed, then clicking on the link will download and open the PDF file directly. If you don't, then save the PDF file on your machine and download the Reader to view it.

Please Note: Packt eBooks are non-returnable and non-refundable.

Packt eBook and Licensing When you buy an eBook from Packt Publishing, completing your purchase means you accept the terms of our licence agreement. Please read the full text of the agreement. In it we have tried to balance the need for the ebook to be usable for you the reader with our needs to protect the rights of us as Publishers and of our authors. In summary, the agreement says:

  • You may make copies of your eBook for your own use onto any machine
  • You may not pass copies of the eBook on to anyone else
How can I make a purchase on your website? Chevron down icon Chevron up icon

If you want to purchase a video course, eBook or Bundle (Print+eBook) please follow below steps:

  1. Register on our website using your email address and the password.
  2. Search for the title by name or ISBN using the search option.
  3. Select the title you want to purchase.
  4. Choose the format you wish to purchase the title in; if you order the Print Book, you get a free eBook copy of the same title. 
  5. Proceed with the checkout process (payment to be made using Credit Card, Debit Cart, or PayPal)
Where can I access support around an eBook? Chevron down icon Chevron up icon
  • If you experience a problem with using or installing Adobe Reader, the contact Adobe directly.
  • To view the errata for the book, see www.packtpub.com/support and view the pages for the title you have.
  • To view your account details or to download a new copy of the book go to www.packtpub.com/account
  • To contact us directly if a problem is not resolved, use www.packtpub.com/contact-us
What eBook formats do Packt support? Chevron down icon Chevron up icon

Our eBooks are currently available in a variety of formats such as PDF and ePubs. In the future, this may well change with trends and development in technology, but please note that our PDFs are not Adobe eBook Reader format, which has greater restrictions on security.

You will need to use Adobe Reader v9 or later in order to read Packt's PDF eBooks.

What are the benefits of eBooks? Chevron down icon Chevron up icon
  • You can get the information you need immediately
  • You can easily take them with you on a laptop
  • You can download them an unlimited number of times
  • You can print them out
  • They are copy-paste enabled
  • They are searchable
  • There is no password protection
  • They are lower price than print
  • They save resources and space
What is an eBook? Chevron down icon Chevron up icon

Packt eBooks are a complete electronic version of the print edition, available in PDF and ePub formats. Every piece of content down to the page numbering is the same. Because we save the costs of printing and shipping the book to you, we are able to offer eBooks at a lower cost than print editions.

When you have purchased an eBook, simply login to your account and click on the link in Your Download Area. We recommend you saving the file to your hard drive before opening it.

For optimal viewing of our eBooks, we recommend you download and install the free Adobe Reader version 9.