Search icon CANCEL
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Unity 5.x Game AI Programming Cookbook

You're reading from   Unity 5.x Game AI Programming Cookbook Build and customize a wide range of powerful Unity AI systems with over 70 hands-on recipes and techniques

Arrow left icon
Product type Paperback
Published in Mar 2016
Publisher Packt
ISBN-13 9781783553570
Length 278 pages
Edition 1st Edition
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 (10) Chapters Close

Preface 1. Behaviors – Intelligent Movement FREE CHAPTER 2. Navigation 3. Decision Making 4. Coordination and Tactics 5. Agent Awareness 6. Board Games AI 7. Learning Techniques 8. Miscellaneous Index

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 a velocity math so that we can reach them.

Also, the agents must have the tag Agent, the main object must have a Collider component marked as trigger. Depending on your game, the agent or the pads will need the Rigidbody component attached.

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;

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...

We will learn how to implement the Jump behavior:

  1. Create the Jump class deriving from VelocityMatch, with its member variables:
    using UnityEngine;
    using System.Collections.Generic;
    
    public class Jump : VelocityMatch
    {
        public JumpPoint jumpPoint;
        //Keeps track of whether the jump is achievable
        bool canAchieve = false;
        //Holds the maximum vertical jump velocity
        public float maxYVelocity;
        public Vector3 gravity = new Vector3(0, -9.8f, 0);
        private Projectile projectile;
        private List<AgentBehaviour> behaviours;
        
        // next steps
    }
  2. Implement the Isolate method. It disables all the agent behaviors, except for the Jump component:
    public void Isolate(bool state)
    {
        foreach (AgentBehaviour b in behaviours)
            b.enabled = !state;
        this.enabled = state;
    }
  3. Define the function for calling the jumping effect, using the projectile behavior we learned before:
    public void DoJump()
    {
        projectile.enabled = true;
        Vector3 direction;
        direction = Projectile.GetFireDirection(jumpPoint.jumpLocation, jumpPoint.landingLocation, agent.maxSpeed);
        projectile.Set(jumpPoint.jumpLocation, direction, agent.maxSpeed, false);
    }
  4. Implement the member function for setting up the behaviors' target for matching its velocity:
    protected void CalculateTarget()
    {
        target = new GameObject();
        target.AddComponent<Agent>();
    
        //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;
        }
    }
  5. Implement the function for computing the time:
    //Private helper method for the CalculateTarget function
    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;
    }
  6. Override the Awake member function. The most important thing here is caching the references to other attached behaviors, so Isolate function makes sense:
    public override void Awake()
    {
        base.Awake();
        this.enabled = false;
        projectile = gameObject.AddComponent<Projectile>();
        behaviours = new List<AgentBehaviour>();
        AgentBehaviour[] abs;
        abs = gameObject.GetComponents<AgentBehaviour>();
        foreach (AgentBehaviour b in abs)
        {
            if (b == this)
                continue;
            behaviours.Add(b);
        }
    }
  7. Override the GetSteering member function:
    public override Steering GetSteering()
    {
        Steering steering = new Steering();
        
        // Check if we have a trajectory, and create one if not.
        if (jumpPoint != null && target == null)
        {
            CalculateTarget();
        }
        //Check if the trajectory is zero. If not, we have no acceleration.
        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))
        {
            DoJump();
            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. The behavior's target is the one responsible for executing the jump, and if it judges that the agent can, it tries to match the targets' vertical velocity while seeking the landing pad's position.

There is more

We will need a jump pad and a landing pad in order to have a complete jumping system. Both the jump and landing pads need the Collider component marked as trigger. Also, as stated before, they will probably need to have a Rigidbody component, too, as seen in the image below.

There is more

The pads we will need a MonoBehaviour script attached as explained below.

The following code is to be attached to the jump pad:

using UnityEngine;

public class JumpLocation : MonoBehaviour
{
    public LandingLocation landingLocation;

    public void OnTriggerEnter(Collider other)
    {
        if (!other.gameObject.CompareTag("Agent"))
            return;
        Agent agent = other.GetComponent<Agent>();
        Jump jump = other.GetComponent<Jump>();
        if (agent == null || jump == null)
            return;
        Vector3 originPos = transform.position;
        Vector3 targetPos = landingLocation.transform.position;
        jump.Isolate(true);
        jump.jumpPoint = new JumpPoint(originPos, targetPos);
        jump.DoJump();
    }
}

The following code is to be attached to the landing pad:

using UnityEngine;

public class LandingLocation : MonoBehaviour
{
    public void OnTriggerEnter(Collider other)
    {
        if (!other.gameObject.CompareTag("Agent"))
            return;
        Agent agent = other.GetComponent<Agent>();
        Jump jump = other.GetComponent<Jump>();
        if (agent == null || jump == null)
            return;
        jump.Isolate(false);
        jump.jumpPoint = null;
    }
}

See Also

The Shooting a projectile recipe

You have been reading a chapter from
Unity 5.x Game AI Programming Cookbook
Published in: Mar 2016
Publisher: Packt
ISBN-13: 9781783553570
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 €18.99/month. Cancel anytime