Skip to content

Instantly share code, notes, and snippets.

@ecker00
Last active January 14, 2018 23:18
Show Gist options
  • Save ecker00/971ccac1210a6cb738830929a24a4262 to your computer and use it in GitHub Desktop.
Save ecker00/971ccac1210a6cb738830929a24a4262 to your computer and use it in GitHub Desktop.
Unity3D Boids script 2013

Introduction

Video: https://www.youtube.com/watch?v=QKNUkcMUD34

This code have not been tested since 2013, but the math in AgentBrain.js should work in any system. This script is just an experiment, it takes a lot of CPU and can only handle a few hundred agents.

This system works with Unity's collision system, so every argent has a collision sphere which is a "trigger zone" which is a lot larger than the agent it self. If other agents are inside this collision sphere they will be part of the crossAgent calculation. This area is how far each boid can see, and use this to look at the boids that are in close range to calculate a movment goal.

The brain script handles both player, pray and preditors AI. It works isolated on the agents without direct knowledge of other agents, so each agent have the brain script attached.

The spawner script is just added to an empty object to generate all the agents.

#pragma strict
// Customizable
var detectionRange : float = 25.0; // Set from spawn script
var updateSkip : float = 5.0;
var aSpeed : float = 70;
var hostile : boolean = false;
var player : boolean = false;
private var curVec : Vector2 = Vector2.zero;
private var instructionUpdate : int = 0;
private var lastAngle : float = 0;
private var lastSpeed : float = 0;
function Start () {
// Set player color
if (player) {
renderer.material.color = Color.green;
// Set hostile color
} else if (hostile) {
renderer.material.color = Color.red;
}
}
function Update () {
var posV3 : Vector3 = transform.position;
var posV2 : Vector2 = Vector2( posV3.x, posV3.z );
var speed : float = aSpeed;
var angle : float;
// Influence / Acceleration
var inf : float = 0.02;
if (hostile) inf = 0.05;
// Get data for surrounding agents
// -------------------------------------------
if (instructionUpdate <= 0 || player) {
instructionUpdate = updateSkip*Random.value;
// Friendly
var fNum : int = 0;
var fAverage : Vector2 = Vector2.zero;
var fDistance : float = 1000;
var fAgent : Rigidbody;
// Enemy
var eDistance : float = 1000;
var eAgent : Rigidbody;
// Get all objects touching or in range
var colliders : Collider[] = Physics.OverlapSphere( posV3, detectionRange );
// Check agents in proximity
for (var crossAgent : Collider in colliders) {
// avoid wrong references
if (!crossAgent) continue;
if (!crossAgent.rigidbody) continue;
if (rigidbody == crossAgent.rigidbody) continue;
// Get cross brain
var crossBrain : AgentBrain4 = crossAgent.GetComponent( AgentBrain4 ) as AgentBrain4;
if (!crossBrain) continue;
// Check distance
var crossDistance : float = (posV3 - crossAgent.rigidbody.position).magnitude;
// Is Hostile
if (crossBrain.hostile || crossBrain.player) {
// Closest hostile
if (crossDistance < eDistance) {
eDistance = crossDistance;
eAgent = crossAgent.rigidbody;
// Hostile too close: Suicide
if (!hostile && crossBrain.hostile && eDistance < 2) {
Destroy( gameObject );
return;
}
}
// Is Friendly
} else {
fAverage += Vector2( crossAgent.rigidbody.position.x, crossAgent.rigidbody.position.z );
fNum++;
// Closest friendly
if (crossDistance < fDistance) {
fDistance = crossDistance;
fAgent = crossAgent.rigidbody;
}
}
}
// Behavior
// -------------------------------------------
var crossPosV2 : Vector2;
fAverage.x = fAverage.x / fNum;
fAverage.y = fAverage.y / fNum;
// Player
// ------------------
if (player) {
// Stay apart
if (eDistance < 1.5) {
crossPosV2 = Vector2( eAgent.position.x, eAgent.position.z );
angle = getAngle( crossPosV2, posV2 ) +45;
speed *= 1.3;
// Go to mouse position
} else {
var ray : Ray = Camera.main.ScreenPointToRay( Input.mousePosition );
var hit : RaycastHit;
// Ray cast hit
if (Physics.Raycast( ray, hit )) {
var hitV2 : Vector2 = Vector2( hit.point.x, hit.point.z );
angle = getAngle( hitV2, posV2 ) -90;
speed *= 1.3;
inf = 0.20;
}
}
// Predator
// ------------------
} else if (hostile) {
var curSpeed : float = Mathf.Abs(rigidbody.velocity.x + rigidbody.velocity.z);
if ( fAgent ) {
// Predator: Single target
if ( fNum < 5 || fDistance < 4 ) {
// Change color
if (renderer.material.color != Color.blue) renderer.material.color = Color.blue;
// Set angle
crossPosV2 = Vector2( fAgent.position.x, fAgent.position.z );
angle = getAngle( crossPosV2, posV2 ) -90;
speed *= 1.4;
inf = 0.20;
// Predator: Multi target
} else {
// Change color
if (renderer.material.color != Color.red) renderer.material.color = Color.red;
// Set angle
angle = getAngle( fAverage, posV2 ) -90;
speed *= 1.3;
inf = 0.10;
}
// Predator: Circulate
} else {
// Set angle
angle = getAngle( Vector2.zero, posV2 ) -140; // -30;
speed *= 1.1;
// Change color
if (renderer.material.color != Color.yellow) renderer.material.color = Color.yellow;
}
// Prey
// ------------------
} else {
// Prey: Enemy nearby
if ( eDistance < 20 ) {
// Escape enemy
crossPosV2 = Vector2( eAgent.position.x, eAgent.position.z );
angle = getAngle( crossPosV2, posV2 ) +100;
speed *= 1.2;
inf = 0.10;
// Prey: Friendly nearby
} else if ( fAgent ) {
// Distance to average position
var fAvgDist : float = (posV3 - Vector3(fAverage.x, 0, fAverage.y)).magnitude;
// When close, align vectors
if (fAvgDist < 12) {
angle = getAngle( Vector2.zero, posV2 ) -45;
speed *= 0.8;
// When far apart, go closer
} else {
angle = getAngle( fAverage, posV2 ) -90;
speed *= 1.2;
}
// Prey: Circulate
} else {
// Go to zero
angle = getAngle( Vector2.zero, posV2 ) -90;
}
}
// Store instructions
lastAngle = angle;
lastSpeed = speed;
} else {
// Get last instructions
instructionUpdate--;
angle = lastAngle;
speed = lastSpeed;
}
// Movement
// -------------------------------------------
var goalVec : Vector2 = Vector2.zero;
// Goal Vector
lastAngle = lastAngle + transform.eulerAngles.y;
goalVec.x = Mathf.Sin( Mathf.Deg2Rad * lastAngle ) *lastSpeed;
goalVec.y = Mathf.Cos( Mathf.Deg2Rad * lastAngle ) *-lastSpeed;
// New Vector
curVec.x = influence(goalVec.x, curVec.x, inf);
curVec.y = influence(goalVec.y, curVec.y, inf);
var grav = 0;
if (posV3.y > 0.5) grav = -5;
else transform.position.y = 0.5;
// Apply Movment
rigidbody.velocity = transform.TransformDirection(Vector3( curVec.x, grav, curVec.y ));
// Fallen off the world, reset
if (posV3.y < -50) {
print('Resetting position');
transform.position = Vector3(0,5,0);
}
}
// -------------------------------------------
function influence( value1 : float, value2 : float, inf : float ) {
var aInf : float = 1-inf;
return (value1*inf) + (value2*aInf);
}
// -------------------------------------------
function getAngle( obj1 : Vector2, obj2 : Vector2 ) {
var deltaX : float = obj2.x - obj1.x;
var deltaY : float = obj2.y - obj1.y;
var value : float = Mathf.Atan2( deltaY, deltaX );
value = ( value * Mathf.Rad2Deg );
return value;
}
#pragma strict
var agentPrefab : Rigidbody;
var numAgents : int = 20;
var numPlayers : int = 0;
var numPreditors : int = 1;
private var totalNum : int = numAgents + numPreditors + numPlayers;
function Awake() {
var clone : Rigidbody;
var x : float;
var z : float;
var i : int;
// Agents
for(i=0; i<numAgents; i++) {
x = Random.value*(totalNum*5) + totalNum*-2.5;
z = Random.value*(totalNum*5) + totalNum*-2.5;
// Clone
clone = Instantiate( agentPrefab, Vector3( x, 15, z ), Quaternion.identity );
clone.GetComponent( AgentBrain4 ).detectionRange = 30;
clone.GetComponent( AgentBrain4 ).updateSkip = 0;
clone.GetComponent( AgentBrain4 ).hostile = false;
}
// Preditors
for(i=0; i<numPreditors; i++) {
x = Random.value*(totalNum*5) + totalNum*-2.5;
z = Random.value*(totalNum*5) + totalNum*-2.5;
// Clone
clone = Instantiate( agentPrefab, Vector3( x, 15, z ), Quaternion.identity );
clone.GetComponent( AgentBrain4 ).detectionRange = 30;
clone.GetComponent( AgentBrain4 ).updateSkip = 0;
clone.GetComponent( AgentBrain4 ).hostile = true;
}
// Players
for(i=0; i<numPlayers; i++) {
x = Random.value*(totalNum*5) + totalNum*-2.5;
z = Random.value*(totalNum*5) + totalNum*-2.5;
// Clone
clone = Instantiate( agentPrefab, Vector3( x, 15, z ), Quaternion.identity );
clone.GetComponent( AgentBrain4 ).detectionRange = 5;
clone.GetComponent( AgentBrain4 ).hostile = true;
clone.GetComponent( AgentBrain4 ).player = true;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment