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:
- Create the
Jump
class deriving fromVelocityMatch
, 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 }
- Implement the
Isolate
method. It disables all the agent behaviors, except for theJump
component:public void Isolate(bool state) { foreach (AgentBehaviour b in behaviours) b.enabled = !state; this.enabled = state; }
- 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); }
- 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; } }
- 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; }
- Override the
Awake
member function. The most important thing here is caching the references to other attached behaviors, soIsolate
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); } }
- 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.
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