Boid Flocking and Pathfinding in Unity, Part 2
July 5th, 2012 by Anthony Capobianchi
In my last post, we worked through the steps to create a Boid system that will keep objects together in a coherent way, and a radar class that will allow the Boids to detect each other. Our next step is to figure out how to get these objects from point “A” to point “B”, by setting a destination point.
Pathfinding -
For this example, I used Aron Granberg’s A* Pathfinding Project to handle my pathfinding. It works great and uses Unity’s CharacterController to handle movement, which helps with this example. A link to download this library can be found at http://www.arongranberg.com/unity/a-pathfinding/download/ and a guide to help you set it up in your scene can be found here: http://www.arongranberg.com/astar/docs/getstarted.php
In Boid.cs I have my path finding scripts as such:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
//Reference to A* Pathfinding Project’s Seeker class which handles the path calculation. private Seeker seeker; //Path that is going to be followed by the Boid. private Path path; //The Boid’s current point on it’s path. private int currentWayPoint = 0; //The maximum distance from the Boid to the a waypoint for it to continue to the next waypoint. private float nextWayPoint = 1; //Bool to determine if you’ve reached the end of the path. private bool journeyComplete = true; //Uses the Seeker class to calculate the path from the Boid’s current position to it’s set target. public void CalculatePath(){ if(target == null){ Debug.LogWarning("Target is null. Aborting Pathfinder..."); return; } seeker.StartPath(transform.position, target.transform.position, onPathComplete); journeyComplete = false; } //When the new path is completed calculating, set the path property to the calculated path. Then set the Boid’s current movement state to MOVING. Return out if there is an error with the path. private void onPathComplete(Path p){ if (p.error) return; path = p; currentWayPoint = 0; CurrentMovementState = MovementState.MOVING; } //This is the vector that is going to be returned to the Boid’s CharacterController.Move() function private Vector3 pathDirection; //This function calculates the value for the vector above. public Vector3 FollowPath(){ if(path == null || currentWayPoint >= path.vectorPath.Length || target == null) return Vector3.zero; if(currentWayPoint >= path.vectorPath.Length || Vector3.Distance(transform.position, target.transform.position) < 1){ journeyComplete = true; return Vector3.zero; } pathDirection = (path.vectorPath[currentWayPoint] - transform.position).normalized; pathDirection *= movementSpeed * Time.deltaTime; if(Vector3.Distance(transform.position, path.vectorPath[currentWayPoint]) < nextWayPoint){ currentWayPoint++; } return pathDirection; } |
Applying the Forces -
Once we have the calculated force of the Boid behaviors and the path finder, we have to put those together and apply that to the character controller. We use Unity’s Update function on Boid.cs to constantly apply the forces.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
//The averaged out center of all the Boid’s positions. private Vector3 center; //The vector that is returned from BoidBehaviors. private Vector3 steerForce; //The vector that is returned from CalculatePath. private Vector3 seekForce; //The final vector that will be passed to the Character Controller. private Vector3 driveForce = Vector3.zero; void Update () { center = Vector3.zero; //This calculates the center if(boids.Count > 0){ foreach(Boid b in boids){ center += b.Position; } center = center / boids.Count; } steerForce = Vector3.zero; seekForce = Vector3.zero; //We only need to add the path finding vector is we are moving and the path is not completed. if(!journeyComplete && currentMovementState == MovementState.MOVING){ seekForce = FollowPath(); } else{ if(CurrentMovementState != MovementState.ORGANIZING && CurrentMovementState != MovementState.IDLE) CurrentMovementState = MovementState.ORGANIZING; } //We only need to add the Boid behaviors if the current movement speed is ORGANIZING and the EveryoneStopped() function returns false. if(currentMovementState == MovementState.ORGANIZING || EveryoneStopped() == false){ steerForce = BoidBehaviors(); } //Add the two forces together. It is highly likely that one or the other will be a zero value. Since we don’t want the two forces to affect each other all the time (but some of the time), this is a good way to do it. driveForce = steerForce + seekForce; //Apply the force to the Character Controller controller.Move(driveForce); //This function call is extra, but no doubt something you’d want in just about every situation. Once the force is applied, you probably want to update the Boid’s forward vector to face the direction it is walking. if(!journeyComplete && currentMovementState == MovementState.MOVING) TurnToFaceMovementDirection (driveForce); } private Vector3 newForward; private void TurnToFaceMovementDirection (Vector3 newVel){ if (movementSpeed > 0 && newVel.sqrMagnitude > 0.02f) { newForward = newVel / movementSpeed; gameObject.transform.forward = newForward; } } |
In my next post, we will look at using a ray caster to set a destination point in the scene for the Boids, as well as how to organize a number of different destination points for the Boids to keep them from piling on top of each other.
Read part one here.
- No Comments »
- Posted in Android, iPhone, Tutorials, Unity3D, Web Development