diff --git a/Boids/Assets/Boid.cs b/Boids/Assets/Boid.cs index fc48e56..22841a9 100644 --- a/Boids/Assets/Boid.cs +++ b/Boids/Assets/Boid.cs @@ -27,10 +27,10 @@ public class Boid : MonoBehaviour { transform.name = $"Boid {transform.GetSiblingIndex()}"; // Name the Game Object so Boids can be tracked somewhat } -void OnDrawGizmos() { + void OnDrawGizmos() { var transform_ = transform; Handles.Label(transform_.position, $"{transform_.name} {_latestNeighborhoodCount}"); -} + } void Update() { // Updates the rotation of the object based on the Velocity @@ -47,11 +47,17 @@ void OnDrawGizmos() { _latestNeighborhoodCount = flock.Count; // Calculate all offsets and multiple by magnitudes given + Vector2 r1, r2, r3, r4; + r4 = _parent.Boundary.Contains(_position) ? Vector2.zero : RuleBound() * _parent.boundaryBias; + if (flock.Count > 0) { - Vector2 r1 = Rule1(flock) * _parent.cohesionBias; - Vector2 r2 = Rule2(flock) * _parent.separationBias; - Vector2 r3 = Rule3(flock) * _parent.alignmentBias; - _velocity += r1 + r2 + r3; + r1 = Rule1(flock) * _parent.cohesionBias; + r2 = Rule2(flock) * _parent.separationBias; + r3 = Rule3(flock) * _parent.alignmentBias; + _velocity += r1 + r2 + r3 + r4; + } + else { + _velocity += r4; } // Limit the Velocity Vector to a certain Magnitude @@ -61,10 +67,11 @@ void OnDrawGizmos() { transform.position = new Vector3(_position.x, _position.y, 0); } - Wrapping(); + if (_parent.edgeWrapping) + Wrapping(); } - void Wrapping() { + private void Wrapping() { if (!_parent.Space.Contains(_position)) { // Activate Wrap, Move Vector2 newPosition = transform.position; @@ -93,20 +100,20 @@ void OnDrawGizmos() { } // When Wrapping, this Velocity directs the Boid to the center of the Rectangle - void UpdateCenteringVelocity() { + private void UpdateCenteringVelocity() { _centeringVelocity = Util.RotateBy(new Vector2(_parent.boidVelocityLimit, _parent.boidVelocityLimit), Vector2.Angle(_position, _parent.Space.center)); _centeringVelocity = Util.LimitVelocity(_parent.Space.center - _position, _parent.boidVelocityLimit / 2.0f); } - + // Returns a velocity (Vector2) at a random angle with a specific overall magnitude - Vector2 GetRandomVelocity(float magnitude) { + private Vector2 GetRandomVelocity(float magnitude) { Vector2 vector = new Vector2(magnitude, magnitude); return Util.RotateBy(vector, Random.Range(0, 180)); } // Cohesion: Steer towards center of mass of flock - Vector2 Rule1(List flock) { + private Vector2 Rule1(List flock) { Vector2 center = Vector2.zero; foreach (Boid boid in flock) center += boid._position; @@ -115,7 +122,7 @@ void OnDrawGizmos() { } // Separation: Steer to avoid other Boids within flock - Vector2 Rule2(List flock) { + private Vector2 Rule2(List flock) { Vector2 c = Vector2.zero; foreach (Boid boid in flock) { Vector2 diff = boid._position - this._position; @@ -127,7 +134,7 @@ void OnDrawGizmos() { } // Alignment: Steer to align with the average heading of the flock - Vector3 Rule3(List flock) { + public Vector2 Rule3(List flock) { if (flock.Count == 0) return Vector2.zero; @@ -138,8 +145,27 @@ void OnDrawGizmos() { return (perceived - _velocity) / 8.0f; } + // Asks Boids to stay within the Boundaries set + private Vector2 RuleBound() { + Vector2 vector = Vector2.zero; + + // Boundary X Force + if (_position.x < _parent.Boundary.xMin) + vector.x = _parent.boundaryForce; + else if (_position.x > _parent.Boundary.xMax) + vector.x = -_parent.boundaryForce; + + // Boundary Y Force + if (_position.y < _parent.Boundary.yMin) + vector.y = _parent.boundaryForce; + else if (_position.y > _parent.Boundary.yMax) + vector.y = -_parent.boundaryForce; + + return vector; + } + // Returns a list of boids within a certain radius of the Boid, representing it's local 'flock' - List GetFlock(List boids, float radius) { + private List GetFlock(List boids, float radius) { return boids.Where(boid => boid != this && Vector2.Distance(this._position, boid._position) <= radius).ToList(); } } \ No newline at end of file diff --git a/Boids/Assets/BoidController.cs b/Boids/Assets/BoidController.cs index 7972715..80c0047 100644 --- a/Boids/Assets/BoidController.cs +++ b/Boids/Assets/BoidController.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using UnityEditor; using UnityEngine; using Debug = System.Diagnostics.Debug; using Random = UnityEngine.Random; @@ -7,6 +8,7 @@ using Random = UnityEngine.Random; public class BoidController : MonoBehaviour { // Controller Attributes [NonSerialized] public Rect Space; + [NonSerialized] public Rect Boundary; // Swarm Attributes public int boidCount = 50; @@ -16,10 +18,13 @@ public class BoidController : MonoBehaviour { public float boidSeparationRange = 2.3f; // Bias changes how different rules influence individual Boids more or less - public float separationBias = 0.05f; - public float alignmentBias = 0.05f; - public float cohesionBias = 0.05f; - public bool localFlocks = true; + [SerializeField] public float separationBias = 0.05f; + [SerializeField] public float alignmentBias = 0.05f; + [SerializeField] public float cohesionBias = 0.05f; + [SerializeField] public float boundaryBias = 1f; + [SerializeField] public float boundaryForce = 10f; + [SerializeField] public bool localFlocks = true; + [SerializeField] public bool edgeWrapping = true; public Boid focusedBoid; // A focused Boid has special rendering public GameObject boidObject; // Boid Object Prefab @@ -29,12 +34,16 @@ public class BoidController : MonoBehaviour { private void OnDrawGizmos() { // Draw a Wire Cube for the Rectangle Area Gizmos.DrawWireCube(Space.center, Space.size); + Gizmos.DrawWireCube(Boundary.center, Boundary.size); + + if (focusedBoid != null) + Handles.DrawWireDisc(focusedBoid.transform.position, Vector3.forward, boidGroupRange); if (Cam == null) return; // Draw a Wire Cube for the Cam's Viewport Area - var position = transform.position; + Vector3 position = transform.position; Vector3 screenBottomLeft = Cam.ViewportToWorldPoint(new Vector3(0, 0, position.z)); Vector3 screenTopRight = Cam.ViewportToWorldPoint(new Vector3(1, 1, position.z)); var screenWidth = screenTopRight.x - screenBottomLeft.x; @@ -48,9 +57,11 @@ public class BoidController : MonoBehaviour { // Size the Rectangle based on the Camera's Orthographic View float height = 2f * Cam.orthographicSize; - Vector2 size = new Vector2(height * Cam.aspect, height); + var size = new Vector2(height * Cam.aspect, height); Space = new Rect((Vector2) transform.position - size / 2, size); - + Boundary = new Rect(Vector2.zero, Space.size * 0.95f); + Boundary.center = Space.center; + AddBoids(boidCount); } @@ -89,7 +100,7 @@ public class BoidController : MonoBehaviour { private Vector2 RandomPosition() { return new Vector2( - Random.Range(-Space.size.x, Space.size.x) / 2, - Random.Range(-Space.size.y, Space.size.y) / 2); + Random.Range(-Boundary.size.x, Boundary.size.x) / 2, + Random.Range(-Boundary.size.y, Boundary.size.y) / 2); } } \ No newline at end of file diff --git a/Boids/Assets/BoidControllerEditor.cs b/Boids/Assets/BoidControllerEditor.cs index bdc7dcf..18f76f3 100644 --- a/Boids/Assets/BoidControllerEditor.cs +++ b/Boids/Assets/BoidControllerEditor.cs @@ -20,16 +20,19 @@ public class BoidControllerEditor : Editor { } // Basic Boid Controller Attributes - controller.boidGroupRange = EditorGUILayout.Slider("Group Range", controller.boidGroupRange, 0.01f, 25.0f); + controller.boidGroupRange = EditorGUILayout.Slider("Group Range", controller.boidGroupRange, 0.01f, 10.0f); controller.boidStartVelocity = EditorGUILayout.Slider("Start Velocity", controller.boidStartVelocity, 0.01f, 5.0f); controller.boidVelocityLimit = EditorGUILayout.Slider("Max Velocity", controller.boidVelocityLimit, 0.01f, 7.5f); - controller.boidSeparationRange = EditorGUILayout.Slider("Separation Range", controller.boidSeparationRange, 0.01f, 25.0f); + controller.boidSeparationRange = EditorGUILayout.Slider("Separation Range", controller.boidSeparationRange, 0.01f, 5.0f); + controller.boundaryForce = EditorGUILayout.Slider("Boundary Force", controller.boundaryForce, 0.5f, 5.0f); // Boid Bias Attributes - controller.alignmentBias = EditorGUILayout.Slider("Alignment Bias", controller.alignmentBias, 0.001f, 1.5f); - controller.cohesionBias = EditorGUILayout.Slider("Cohesion Bias", controller.cohesionBias, 0.001f, 1.5f); - controller.separationBias = EditorGUILayout.Slider("Separation Bias", controller.separationBias, 0.001f, 1.5f); + controller.alignmentBias = EditorGUILayout.Slider("Alignment Bias", controller.alignmentBias, 0.001f, 0.75f); + controller.cohesionBias = EditorGUILayout.Slider("Cohesion Bias", controller.cohesionBias, 0.001f, 0.75f); + controller.separationBias = EditorGUILayout.Slider("Separation Bias", controller.separationBias, 0.001f, 0.75f); + controller.boundaryBias = EditorGUILayout.Slider("Boundary Bias", controller.boundaryBias, 0.01f, 1.5f); controller.localFlocks = EditorGUILayout.Toggle("Use Groups?", controller.localFlocks); + controller.edgeWrapping = EditorGUILayout.Toggle("Enforce Wrapping?", controller.edgeWrapping); } } \ No newline at end of file diff --git a/Boids/Assets/Triangle.cs b/Boids/Assets/Triangle.cs index 0ecb795..1d36e5c 100644 --- a/Boids/Assets/Triangle.cs +++ b/Boids/Assets/Triangle.cs @@ -1,10 +1,11 @@ -using System.Collections; +using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using UnityEngine; public class Triangle : MonoBehaviour { - public Color fillColor = Color.blue; + [NonSerialized] public Color FillColor = Color.red; private void Start () { // Create Vector2 vertices @@ -21,7 +22,7 @@ public class Triangle : MonoBehaviour { var indices = triangulator.Triangulate(); // Generate a color for each vertex - var colors = Enumerable.Repeat(fillColor, vertices3D.Length).ToArray(); + var colors = Enumerable.Repeat(FillColor, vertices3D.Length).ToArray(); // Create the mesh var mesh = new Mesh {