diff --git a/Boids/Assets/Boid.cs b/Boids/Assets/Boid.cs index bf6091a..3212928 100644 --- a/Boids/Assets/Boid.cs +++ b/Boids/Assets/Boid.cs @@ -15,6 +15,7 @@ public class Boid : MonoBehaviour { [NonSerialized] private bool _isWrappingY = false; [NonSerialized] private Renderer[] _renderers; [NonSerialized] private Vector2 _centeringVelocity; + [NonSerialized] private int _latestNeighborhoodCount = 0; private BoidController _parent; void Start() { @@ -26,14 +27,10 @@ public class Boid : MonoBehaviour { transform.name = $"Boid {transform.GetSiblingIndex()}"; // Name the Game Object so Boids can be tracked somewhat } - void OnDrawGizmos() { - // Handles.color = _isWrappingX || _isWrappingY ? Color.red : Color.white; - // Vector3 viewportPosition = _parent.cam.WorldToViewportPoint(transform.position); - // Handles.Label(transform.position, - // $"{(Vector2) viewportPosition} {(_isWrappingX ? "Y" : "N")} {(_isWrappingY ? "Y" : "N")}"); - var transform_ = transform; - Handles.Label(transform_.position, $"{transform_.name}"); - } +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,6 +44,7 @@ public class Boid : MonoBehaviour { } else { List flock = _parent.localFlocks ? GetFlock(_parent.boids, _parent.boidGroupRange) : _parent.boids; + _latestNeighborhoodCount = flock.Count; // Calculate all offsets and multiple by magnitudes given if (flock.Count > 0) { @@ -70,7 +68,7 @@ public class Boid : MonoBehaviour { if (!_parent.Space.Contains(_position)) { // Activate Wrap, Move Vector2 newPosition = transform.position; - Vector3 viewportPosition = _parent.cam.WorldToViewportPoint(newPosition); + Vector3 viewportPosition = _parent.Cam.WorldToViewportPoint(newPosition); if (!_isWrappingX && (viewportPosition.x > 1 || viewportPosition.x < 0)) { newPosition.x = -newPosition.x; @@ -101,6 +99,7 @@ public class Boid : MonoBehaviour { _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) { Vector2 vector = new Vector2(magnitude, magnitude); return Util.RotateBy(vector, Random.Range(0, 180)); diff --git a/Boids/Assets/BoidController.cs b/Boids/Assets/BoidController.cs index 29df0c9..c6da250 100644 --- a/Boids/Assets/BoidController.cs +++ b/Boids/Assets/BoidController.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using UnityEngine; +using Debug = System.Diagnostics.Debug; using Random = UnityEngine.Random; public class BoidController : MonoBehaviour { @@ -20,43 +21,43 @@ public class BoidController : MonoBehaviour { public float cohesionBias = 0.05f; public bool localFlocks = true; - // Boid Object Prefab - public GameObject boidObject; - - // Boid Objects for Updates - [NonSerialized] [HideInInspector] public List boids = new List(); - - // Used for wrapping - public Camera cam; + public Boid focusedBoid; // A focused Boid has special rendering + public GameObject boidObject; // Boid Object Prefab + [NonSerialized] public List boids = new List(); // Boid Objects for Updates + [NonSerialized] public Camera Cam; // Used for wrapping detection private void OnDrawGizmos() { // Draw a Wire Cube for the Rectangle Area Gizmos.DrawWireCube(Space.center, Space.size); - + + if (Cam == null) + return; + // Draw a Wire Cube for the Cam's Viewport Area - Vector3 screenBottomLeft = cam.ViewportToWorldPoint(new Vector3(0, 0, transform.position.z)); - Vector3 screenTopRight = cam.ViewportToWorldPoint(new Vector3(1, 1, transform.position.z)); + var 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; var screenHeight = screenTopRight.y - screenBottomLeft.y; - Gizmos.DrawWireCube(cam.transform.position, new Vector3(screenWidth, screenHeight, 1)); + Gizmos.DrawWireCube(Cam.transform.position, new Vector3(screenWidth, screenHeight, 1)); } private void Start() { // Setup Camera - cam = Camera.main; + Cam = Camera.main; // Size the Rectangle based on the Camera's Orthographic View - float height = 2f * cam.orthographicSize; - Vector2 size = new Vector2(height * cam.aspect, height); + float height = 2f * Cam.orthographicSize; + Vector2 size = new Vector2(height * Cam.aspect, height); Space = new Rect((Vector2) transform.position - size / 2, size); - // Add in Boid Objects / Spawn Boid Prefabs - for (int i = 0; i < boidCount; i++) { - // Generate a new position within the Rect boundaries (minus a little) - var position = new Vector2( - Random.Range(-Space.size.x, Space.size.x) / 2 * 0.95f, - Random.Range(-Space.size.y, Space.size.y) / 2 * 0.95f); - // Spawn a new Boid prefab + AddBoids(boidCount); + } + + public void AddBoids(int n) { + for (int i = 0; i < n; i++) { + // Instantiate a Boid prefab within the boundaries randomly + Vector2 position = RandomPosition() * 0.95f; GameObject boid = Instantiate(boidObject, position, Quaternion.identity); // Set parent, add Boid component to Boids list @@ -64,4 +65,32 @@ public class BoidController : MonoBehaviour { boids.Add(boid.GetComponent()); } } + + public void RemoveBoids(int n) { + print($"Removing {n} boids"); + while (n-- > 0 && boids.Count >= 1) { + int index = Random.Range(0, boids.Count - 1); + + // Only remove the focused Boid if it is the last one + if (boids[index] == focusedBoid) + if (boids.Count == 1) + RemoveBoid(1); + else + n++; + else + RemoveBoid(index); + } + } + + private void RemoveBoid(int index) { + Boid boid = boids[index]; + boids.RemoveAt(index); + Destroy(boid.transform.gameObject); + } + + private Vector2 RandomPosition() { + return new Vector2( + Random.Range(-Space.size.x, Space.size.x) / 2, + Random.Range(-Space.size.y, Space.size.y) / 2); + } } \ No newline at end of file diff --git a/Boids/Assets/BoidController.prefab b/Boids/Assets/BoidController.prefab new file mode 100644 index 0000000..1715918 --- /dev/null +++ b/Boids/Assets/BoidController.prefab @@ -0,0 +1,57 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &4181400676305357553 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4181400676305357555} + - component: {fileID: 4181400676305357552} + m_Layer: 0 + m_Name: BoidController + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &4181400676305357555 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4181400676305357553} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &4181400676305357552 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4181400676305357553} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 7d72224fef7a4fb4a998b0980fe0eb77, type: 3} + m_Name: + m_EditorClassIdentifier: + boidCount: 1 + boidGroupRange: 4.57 + boidStartVelocity: 0.05 + boidVelocityLimit: 0.5 + separationRange: 2.5 + separationBias: 0.05 + alignmentBias: 0.5 + cohesionBias: 0.01 + localFlocks: 1 + focusedBoid: {fileID: 0} + boidObject: {fileID: 1737515784064720040, guid: 23e1eaaf69d4ef342ac3ef9590f6c642, + type: 3} diff --git a/Boids/Assets/BoidController.prefab.meta b/Boids/Assets/BoidController.prefab.meta new file mode 100644 index 0000000..e5fa322 --- /dev/null +++ b/Boids/Assets/BoidController.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 79543cb69dabd5846a1789cccbdeefd8 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Boids/Assets/BoidControllerEditor.cs b/Boids/Assets/BoidControllerEditor.cs new file mode 100644 index 0000000..ad110fc --- /dev/null +++ b/Boids/Assets/BoidControllerEditor.cs @@ -0,0 +1,23 @@ +using System; +using UnityEditor; +using UnityEngine; + +[CustomEditor(typeof(BoidController))] +public class BoidControllerEditor : Editor { + public override void OnInspectorGUI() { + BoidController controller = (BoidController) target; + + // Boid Count update + EditorGUI.BeginChangeCheck(); + controller.boidCount = EditorGUILayout.IntSlider("Boid Count", controller.boidCount, 1, 500); + if (EditorGUI.EndChangeCheck() && Application.isPlaying) { + int diff = controller.boidCount - controller.boids.Count; + Debug.Log($"Difference: {diff}"); + if (diff > 1) + controller.AddBoids(diff); + else if (diff < 0) + controller.RemoveBoids(Mathf.Abs(diff)); + } + + } +} \ No newline at end of file diff --git a/Boids/Assets/BoidControllerEditor.cs.meta b/Boids/Assets/BoidControllerEditor.cs.meta new file mode 100644 index 0000000..2a060f1 --- /dev/null +++ b/Boids/Assets/BoidControllerEditor.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 6ec8daef16a2459989f9d8b81bb248ea +timeCreated: 1589767516 \ No newline at end of file