Created
May 1, 2014 17:42
-
-
Save prime31/d8b4f830eee2c8bb8051 to your computer and use it in GitHub Desktop.
Simple (unoptimized!) particle flock. Stick it on a GameObject with a ParticleSystem and play with inspector for some interesting results. Clicking anywhere on screen will set the target position for all boids.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using UnityEngine; | |
using System.Collections; | |
using System.Collections.Generic; | |
[RequireComponent( typeof( ParticleSystem ) )] | |
public class ParticleFlock : MonoBehaviour | |
{ | |
public Vector3 targetPosition; | |
public int totalBoids = 15; | |
[Range( 0.1f, 50f )] | |
public float alignMaxDistance = 0.2f; | |
[Range( 0.1f, 50f )] | |
public float desiredSeperationDistance = 5f; | |
[Range( 0.1f, 50f )] | |
public float cohesionMaxDistance = 2f; | |
[Range( 0.1f, 2f )] | |
public float maxSpeed = 0.1f; | |
[Range( 0.001f, 0.1f )] | |
public float maxForce = 0.01f; | |
[Range( 0.1f, 5f )] | |
public float alignWeight = 1f; | |
[Range( 0.1f, 5f )] | |
public float seperationWeight = 3f; | |
[Range( -10f, 3f )] | |
public float cohesionWeight = 1f; | |
[Range( 0.1f, 5f )] | |
public float tendTowardPositionWeight = 1f; | |
private ParticleSystem _particleSystem; | |
private ParticleSystem.Particle[] _particles; | |
void Awake() | |
{ | |
_particleSystem = GetComponent<ParticleSystem>(); | |
// set our total boids. we limit the value entered in the inspector by the particleSystem max | |
totalBoids = totalBoids > _particleSystem.maxParticles ? _particleSystem.maxParticles : totalBoids; | |
_particleSystem.Emit( totalBoids ); | |
_particles = new ParticleSystem.Particle[_particleSystem.particleCount]; | |
_particleSystem.GetParticles( _particles ); | |
} | |
void OnGUI() | |
{ | |
if( GUILayout.Button( "Scatter" ) ) | |
{ | |
var tempCohesionWeight = cohesionWeight; | |
var tempPositionWeight = tendTowardPositionWeight; | |
cohesionWeight = -25f; | |
tendTowardPositionWeight = 0.3f; | |
StartCoroutine( resetSomeValueAfterDelay( 1.5f, () => | |
{ | |
cohesionWeight = tempCohesionWeight; | |
tendTowardPositionWeight = tempPositionWeight;; | |
} ) ); | |
} | |
if( Event.current.isMouse && ( Event.current.type == EventType.MouseDown || Event.current.type == EventType.MouseDrag ) ) | |
{ | |
var mouse = new Vector3( Input.mousePosition.x, Input.mousePosition.y, 10f ); | |
targetPosition = Camera.main.ScreenToWorldPoint( mouse ); | |
} | |
} | |
IEnumerator resetSomeValueAfterDelay( float delay, System.Action action ) | |
{ | |
yield return new WaitForSeconds( delay ); | |
action(); | |
} | |
void Update() | |
{ | |
for( var i = 0; i < _particles.Length; i++ ) | |
{ | |
var acceleration = Vector3.zero; | |
// acquire our forces and weight them | |
var sep = separate( i ) * seperationWeight; | |
var ali = align( i ) * alignWeight; | |
var coh = cohesion( i ) * cohesionWeight; | |
var tow = tendTowardLocation( i ) * tendTowardPositionWeight; | |
// add to acceleration | |
acceleration += sep; | |
acceleration += ali; | |
acceleration += coh; | |
acceleration += tow; | |
_particles[i].velocity += acceleration; | |
// limit max velocity | |
_particles[i].velocity = limitVectorMagnitude( _particles[i].velocity, maxSpeed ); | |
// move | |
_particles[i].position += _particles[i].velocity; | |
} | |
_particleSystem.SetParticles( _particles, _particles.Length ); | |
} | |
#region boid flocking | |
Vector3 limitVectorMagnitude( Vector3 vec, float limit ) | |
{ | |
if( vec.magnitude > limit ) | |
return vec.normalized * limit; | |
return vec; | |
} | |
private Vector3 seek( int boidIndex, Vector3 target ) | |
{ | |
var desired = target - _particles[boidIndex].position; // A vector pointing from the location to the target | |
// scale to maximum speed | |
desired.Normalize(); | |
desired *= maxSpeed; | |
// Steering = Desired minus Velocity | |
var steer = desired - _particles[boidIndex].velocity; | |
// limit our force | |
if( steer.magnitude > maxForce ) | |
steer = steer.normalized * maxForce; | |
return steer; | |
} | |
// check for nearby boids and steers away | |
public Vector3 separate( int boidIndex ) | |
{ | |
var steer = Vector3.zero; | |
var count = 0; | |
for( var i = 0; i < _particles.Length; i++ ) | |
{ | |
if( i == boidIndex ) | |
continue; | |
var d = Vector3.Distance( _particles[boidIndex].position, _particles[i].position ); | |
if( d < desiredSeperationDistance ) | |
{ | |
// calculate vector pointing away from neighbor | |
var diff = _particles[boidIndex].position - _particles[i].position; | |
diff.Normalize(); | |
diff /= d; | |
steer += diff; | |
count++; // keep track of how many we are avoiding | |
} | |
} | |
// average -- divide by how many | |
if( count > 0 ) | |
steer /= count; | |
// As long as the vector is greater than 0 | |
if( steer.sqrMagnitude > 0 ) | |
{ | |
// Reynolds: Steering = Desired - Velocity | |
steer.Normalize(); | |
steer *= maxSpeed; | |
steer -= _particles[boidIndex].velocity; | |
// limit our force | |
steer = limitVectorMagnitude( steer, maxForce ); | |
} | |
return steer; | |
} | |
// for every nearby boid in the system, calculate the average velocity | |
public Vector3 align( int boidIndex ) | |
{ | |
var sum = Vector3.zero; | |
int count = 0; | |
for( var i = 0; i < _particles.Length; i++ ) | |
{ | |
if( i == boidIndex ) | |
continue; | |
var d = Vector3.Distance( _particles[boidIndex].position, _particles[i].position ); | |
if( d < alignMaxDistance ) | |
{ | |
sum += _particles[i].velocity; | |
count++; | |
} | |
} | |
if( count > 0 ) | |
{ | |
sum /= count; | |
// Reynolds: Steering = Desired - Velocity | |
sum.Normalize(); | |
sum *= maxSpeed; | |
var steer = sum - _particles[boidIndex].velocity; | |
// limit our force | |
return limitVectorMagnitude( steer, maxForce ); | |
} | |
else | |
{ | |
return Vector3.zero; | |
} | |
} | |
// for the average location (i.e. center) of all nearby boids, calculate steering vector towards that location | |
public Vector3 cohesion( int boidIndex ) | |
{ | |
var sum = Vector3.zero; // Start with empty vector to accumulate all locations | |
int count = 0; | |
for( var i = 0; i < _particles.Length; i++ ) | |
{ | |
if( i == boidIndex ) | |
continue; | |
var d = Vector3.Distance( _particles[boidIndex].position, _particles[i].position ); | |
if( d < cohesionMaxDistance ) | |
{ | |
sum += _particles[i].position; | |
count++; | |
} | |
} | |
if( count > 0 ) | |
{ | |
sum /= count; | |
return seek( boidIndex, sum ); // Steer towards the location | |
} | |
return Vector3.zero; | |
} | |
Vector3 tendTowardLocation( int boidIndex ) | |
{ | |
return ( targetPosition - _particles[boidIndex].position ) * 0.02f; | |
} | |
#endregion | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment