From 37c18be20ce066943c67f08a0cdb5dededb7bcab Mon Sep 17 00:00:00 2001 From: Xevion Date: Mon, 18 May 2020 23:15:50 -0500 Subject: [PATCH] update project for building --- Boids/Assets/Editor.meta | 8 + Boids/Assets/Editor/DisableScriptReload.cs | 37 ++ .../Assets/Editor/DisableScriptReload.cs.meta | 3 + Boids/Assets/Resources/Boid.prefab | 61 ++++ Boids/Assets/Resources/Boid.prefab.meta | 7 + Boids/Assets/Resources/BoidController.prefab | 57 +++ .../Resources/BoidController.prefab.meta | 7 + Boids/Assets/Resources/BoidController.preset | 324 ++++++++++++++++++ .../Resources/BoidController.preset.meta | 8 + Boids/Assets/Scripts.meta | 3 + Boids/Assets/Scripts/Boid.cs | 232 +++++++++++++ Boids/Assets/Scripts/Boid.cs.meta | 3 + Boids/Assets/Scripts/BoidController.cs | 131 +++++++ Boids/Assets/Scripts/BoidController.cs.meta | 11 + Boids/Assets/Scripts/BoidControllerEditor.cs | 46 +++ .../Scripts/BoidControllerEditor.cs.meta | 11 + Boids/Assets/Scripts/Triangle.cs | 43 +++ Boids/Assets/Scripts/Triangle.cs.meta | 11 + Boids/Assets/Scripts/Triangulator.cs | 126 +++++++ Boids/Assets/Scripts/Triangulator.cs.meta | 11 + Boids/Assets/Scripts/Util.cs | 23 ++ Boids/Assets/Scripts/Util.cs.meta | 11 + Boids/ProjectSettings/ProjectSettings.asset | 4 +- 23 files changed, 1176 insertions(+), 2 deletions(-) create mode 100644 Boids/Assets/Editor.meta create mode 100644 Boids/Assets/Editor/DisableScriptReload.cs create mode 100644 Boids/Assets/Editor/DisableScriptReload.cs.meta create mode 100644 Boids/Assets/Resources/Boid.prefab create mode 100644 Boids/Assets/Resources/Boid.prefab.meta create mode 100644 Boids/Assets/Resources/BoidController.prefab create mode 100644 Boids/Assets/Resources/BoidController.prefab.meta create mode 100644 Boids/Assets/Resources/BoidController.preset create mode 100644 Boids/Assets/Resources/BoidController.preset.meta create mode 100644 Boids/Assets/Scripts.meta create mode 100644 Boids/Assets/Scripts/Boid.cs create mode 100644 Boids/Assets/Scripts/Boid.cs.meta create mode 100644 Boids/Assets/Scripts/BoidController.cs create mode 100644 Boids/Assets/Scripts/BoidController.cs.meta create mode 100644 Boids/Assets/Scripts/BoidControllerEditor.cs create mode 100644 Boids/Assets/Scripts/BoidControllerEditor.cs.meta create mode 100644 Boids/Assets/Scripts/Triangle.cs create mode 100644 Boids/Assets/Scripts/Triangle.cs.meta create mode 100644 Boids/Assets/Scripts/Triangulator.cs create mode 100644 Boids/Assets/Scripts/Triangulator.cs.meta create mode 100644 Boids/Assets/Scripts/Util.cs create mode 100644 Boids/Assets/Scripts/Util.cs.meta diff --git a/Boids/Assets/Editor.meta b/Boids/Assets/Editor.meta new file mode 100644 index 0000000..1c500e2 --- /dev/null +++ b/Boids/Assets/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1e2b3fe7393f42a592dad4e4c8b3bcf3 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Boids/Assets/Editor/DisableScriptReload.cs b/Boids/Assets/Editor/DisableScriptReload.cs new file mode 100644 index 0000000..35b0b4b --- /dev/null +++ b/Boids/Assets/Editor/DisableScriptReload.cs @@ -0,0 +1,37 @@ + using UnityEditor; + using UnityEngine; + + /// + /// Prevents script compilation and reload while in play mode. + /// The editor will show a the spinning reload icon if there are unapplied changes but will not actually + /// apply them until playmode is exited. + /// Note: Script compile errors will not be shown while in play mode. + /// Derived from the instructions here: + /// https://support.unity3d.com/hc/en-us/articles/210452343-How-to-stop-automatic-assembly-compilation-from-script + /// + [InitializeOnLoad] + public class DisableScriptReload + { + static DisableScriptReload() + { + EditorApplication.playModeStateChanged + += OnPlayModeStateChanged; + } + + static void OnPlayModeStateChanged(PlayModeStateChange stateChange) + { + switch (stateChange) { + case (PlayModeStateChange.EnteredPlayMode): { + EditorApplication.LockReloadAssemblies(); + Debug.Log ("Assembly Reload locked as entering play mode"); + break; + } + case (PlayModeStateChange.ExitingPlayMode): { + Debug.Log ("Assembly Reload unlocked as exiting play mode"); + EditorApplication.UnlockReloadAssemblies(); + break; + } + } + } + + } \ No newline at end of file diff --git a/Boids/Assets/Editor/DisableScriptReload.cs.meta b/Boids/Assets/Editor/DisableScriptReload.cs.meta new file mode 100644 index 0000000..b9ed448 --- /dev/null +++ b/Boids/Assets/Editor/DisableScriptReload.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 6a46ee76c3b4473f9aff1b62cf8674dc +timeCreated: 1589846269 \ No newline at end of file diff --git a/Boids/Assets/Resources/Boid.prefab b/Boids/Assets/Resources/Boid.prefab new file mode 100644 index 0000000..e6fab35 --- /dev/null +++ b/Boids/Assets/Resources/Boid.prefab @@ -0,0 +1,61 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &1737515784064720040 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1737515784064720086} + - component: {fileID: 1737515784064720041} + - component: {fileID: 1737515784064720087} + m_Layer: 0 + m_Name: Boid + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1737515784064720086 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1737515784064720040} + 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 &1737515784064720041 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1737515784064720040} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 39c543193832a9745a8985fb0e672377, type: 3} + m_Name: + m_EditorClassIdentifier: + fillColor: {r: 0, g: 0, b: 1, a: 1} +--- !u!114 &1737515784064720087 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1737515784064720040} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 8fa9bbc6d9eb4c368be190feba139e7c, type: 3} + m_Name: + m_EditorClassIdentifier: + position: {x: 0, y: 0} + velocity: {x: 0.05, y: 0.05} diff --git a/Boids/Assets/Resources/Boid.prefab.meta b/Boids/Assets/Resources/Boid.prefab.meta new file mode 100644 index 0000000..a906512 --- /dev/null +++ b/Boids/Assets/Resources/Boid.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 23e1eaaf69d4ef342ac3ef9590f6c642 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Boids/Assets/Resources/BoidController.prefab b/Boids/Assets/Resources/BoidController.prefab new file mode 100644 index 0000000..1715918 --- /dev/null +++ b/Boids/Assets/Resources/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/Resources/BoidController.prefab.meta b/Boids/Assets/Resources/BoidController.prefab.meta new file mode 100644 index 0000000..e5fa322 --- /dev/null +++ b/Boids/Assets/Resources/BoidController.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 79543cb69dabd5846a1789cccbdeefd8 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Boids/Assets/Resources/BoidController.preset b/Boids/Assets/Resources/BoidController.preset new file mode 100644 index 0000000..07b41c9 --- /dev/null +++ b/Boids/Assets/Resources/BoidController.preset @@ -0,0 +1,324 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!181963792 &2655988077585873504 +Preset: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: BoidController + m_TargetType: + m_NativeTypeID: 114 + m_ManagedTypePPtr: {fileID: 11500000, guid: 7d72224fef7a4fb4a998b0980fe0eb77, + type: 3} + m_ManagedTypeFallback: + m_Properties: + - target: {fileID: 0} + propertyPath: m_Enabled + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: m_EditorHideFlags + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: m_EditorClassIdentifier + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boidCount + value: 62 + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boidGroupRange + value: 3.68 + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boidStartVelocity + value: 0.05 + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boidVelocityLimit + value: 0.3 + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: separationRange + value: 2.82 + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: separationBias + value: 0.01 + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: alignmentBias + value: 0.05 + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: cohesionBias + value: 0.05 + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: localFlocks + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boidObject + value: + objectReference: {fileID: 1737515784064720040, guid: 23e1eaaf69d4ef342ac3ef9590f6c642, + type: 3} + - target: {fileID: 0} + propertyPath: boids.Array.size + value: 62 + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[0] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[1] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[2] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[3] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[4] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[5] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[6] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[7] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[8] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[9] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[10] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[11] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[12] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[13] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[14] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[15] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[16] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[17] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[18] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[19] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[20] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[21] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[22] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[23] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[24] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[25] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[26] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[27] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[28] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[29] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[30] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[31] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[32] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[33] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[34] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[35] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[36] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[37] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[38] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[39] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[40] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[41] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[42] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[43] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[44] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[45] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[46] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[47] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[48] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[49] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[50] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[51] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[52] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[53] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[54] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[55] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[56] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[57] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[58] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[59] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[60] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: boids.Array.data[61] + value: + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: cam + value: + objectReference: {fileID: 0} diff --git a/Boids/Assets/Resources/BoidController.preset.meta b/Boids/Assets/Resources/BoidController.preset.meta new file mode 100644 index 0000000..4648428 --- /dev/null +++ b/Boids/Assets/Resources/BoidController.preset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6a61920c5c2f5524dbc2bffcf29fcbc5 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2655988077585873504 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Boids/Assets/Scripts.meta b/Boids/Assets/Scripts.meta new file mode 100644 index 0000000..4021c25 --- /dev/null +++ b/Boids/Assets/Scripts.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b50106a18d564518ba59ebb42e02f619 +timeCreated: 1589861330 \ No newline at end of file diff --git a/Boids/Assets/Scripts/Boid.cs b/Boids/Assets/Scripts/Boid.cs new file mode 100644 index 0000000..4eccf38 --- /dev/null +++ b/Boids/Assets/Scripts/Boid.cs @@ -0,0 +1,232 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using UnityEngine; +using Random = UnityEngine.Random; + +// Boids are represented by a moving, rotating triangle. +// Boids should communicate with sibling Boids +public class Boid : MonoBehaviour { + [NonSerialized] private Vector2 _position = Vector2.zero; + [NonSerialized] private Vector2 _velocity; + [NonSerialized] private bool _isWrappingX = false; + [NonSerialized] private bool _isWrappingY = false; + [NonSerialized] private Renderer[] _renderers; + [NonSerialized] private Vector2 _centeringVelocity; + [NonSerialized] private int _latestNeighborhoodCount = 0; + [NonSerialized] private BoidController _parent; + [NonSerialized] public bool isFocused = false; + + void Start() { + _parent = transform.parent + .GetComponent(); // Parent used to perform physics math without caching + _renderers = transform.GetComponents(); // Acquire Renderer(s) to check for Boid visibility + _velocity = GetRandomVelocity(_parent.boidStartVelocity); // Acquire a Velocity Vector with a magnitude + _position = transform.position; // Track 2D position separately + transform.name = $"Boid {transform.GetSiblingIndex()}"; // Name the Game Object so Boids can be tracked somewhat + } + + // void OnDrawGizmos() { + // var transform_ = transform; + // Handles.Label(transform_.position, $"{transform_.name} {_latestNeighborhoodCount}"); + // } + + void Update() { + // Updates the rotation of the object based on the Velocity + transform.rotation = Quaternion.Euler(0, 0, Mathf.Rad2Deg * -Mathf.Atan2(_velocity.x, _velocity.y)); + + // Skip Flock Calculations if wrapping in progress + if (_isWrappingX || _isWrappingY) { + UpdateCenteringVelocity(); + _position += _centeringVelocity; + transform.position = _position; + } + else { + List flock = _parent.localFlocks ? GetFlock(_parent.boids, _parent.boidGroupRange) : _parent.boids; + _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) { + 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 + _velocity = Util.LimitVelocity(_velocity, _parent.boidVelocityLimit); + + _position += _velocity; + transform.position = new Vector3(_position.x, _position.y, 0); + } + + if (_parent.edgeWrapping) + Wrapping(); + } + + private void Wrapping() { + if (!_parent.Space.Contains(_position)) { + // Activate Wrap, Move + Vector2 newPosition = transform.position; + Vector3 viewportPosition = _parent.Cam.WorldToViewportPoint(newPosition); + + if (!_isWrappingX && (viewportPosition.x > 1 || viewportPosition.x < 0)) { + newPosition.x = -newPosition.x; + _isWrappingX = true; + UpdateCenteringVelocity(); + } + + if (!_isWrappingY && (viewportPosition.y > 1 || viewportPosition.y < 0)) { + newPosition.y = -newPosition.y; + _isWrappingY = true; + UpdateCenteringVelocity(); + } + + transform.position = newPosition; + _position = newPosition; + } + else { + // Within the rectangle again + _isWrappingX = false; + _isWrappingY = false; + } + } + + // When Wrapping, this Velocity directs the Boid to the center of the Rectangle + 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 + 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 + private Vector2 Rule1(List flock) { + Vector2 center = Vector2.zero; + foreach (Boid boid in flock) + center += boid._position; + center /= _parent.boids.Count; + return (center - this._position) / 100.0f; + } + + // Separation: Steer to avoid other Boids within flock + private Vector2 Rule2(List flock) { + Vector2 c = Vector2.zero; + foreach (Boid boid in flock) { + Vector2 diff = boid._position - this._position; + if (diff.sqrMagnitude < _parent.boidSeparationRange * _parent.boidSeparationRange) + c -= diff; + } + + return c; + } + + // Alignment: Steer to align with the average heading of the flock + public Vector2 Rule3(List flock) { + if (flock.Count == 0) + return Vector2.zero; + + Vector2 perceived = Vector2.zero; + foreach (Boid boid in flock) + perceived += boid._velocity; + perceived /= flock.Count; + 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 * + Mathf.InverseLerp(_parent.Boundary.xMin, _parent.Space.xMin, _position.x); + else if (_position.x > _parent.Boundary.xMax) + vector.x = -_parent.boundaryForce * + Mathf.InverseLerp(_parent.Boundary.xMax, _parent.Space.xMax, _position.x); + + // Boundary Y Force + if (_position.y < _parent.Boundary.yMin) + vector.y = _parent.boundaryForce * + Mathf.InverseLerp(_parent.Boundary.yMin, _parent.Space.yMin, _position.y); + else if (_position.y > _parent.Boundary.yMax) + vector.y = -_parent.boundaryForce * + Mathf.InverseLerp(_parent.Boundary.yMax, _parent.Space.yMax, _position.y); + return vector; + } + + // Returns a list of boids within a certain radius of the Boid, representing it's local 'flock' + private List GetFlock(List boids, float radius) { + return boids.Where(boid => boid != this && Vector2.Distance(this._position, boid._position) <= radius).ToList(); + } + + // Sets up a Boid to be 'Focused', adds Circles around object and changes color + public void enableFocusing() { + if (isFocused) { + Debug.LogWarning("enableFocusing called on previously focused Boid"); + return; + } + + isFocused = true; + + // Update Mesh Material Color + var triangle = transform.GetComponent(); + triangle.meshRenderer.material.color = Color.red; + + // Add a LineRenderer for Radius Drawing + DrawCircle(_parent.boidSeparationRange, "Separation Range Circle"); + // DrawCircle(_parent.boidGroupRange, "Group Range Circle"); + } + + // Disable focusing, removing LineRenderers and resetting color + public void DisableFocusing() { + isFocused = false; + + // Update Mesh Material Color + var oldTriangle = transform.GetComponent(); + oldTriangle.meshRenderer.material.color = new Color32(49, 61, 178, 255); + + // Destroy Line Renderers (and child GameObjects) + foreach (Transform child in transform) + Destroy(child.gameObject); + } + + private void DrawCircle(float radius, string childName) { + // Create a new child GameObject to hold the LineRenderer Component + var child = new GameObject(childName); + child.transform.SetParent(transform); + child.transform.position = transform.position; + var line = child.AddComponent(); + + _parent.circleVertexCount = 360; + + // Setup LineRenderer properties + line.useWorldSpace = false; + line.startWidth = _parent.circleWidth; + line.endWidth = _parent.circleWidth; + line.positionCount = _parent.circleVertexCount + 1; + + // Calculate points for circle + var pointCount = _parent.circleVertexCount + 1; + var points = new Vector3[pointCount]; + for (int i = 0; i < pointCount; i++) { + var rad = Mathf.Deg2Rad * (i * 360f / _parent.circleVertexCount); + points[i] = new Vector3(Mathf.Sin(rad) * radius, 0, Mathf.Cos(rad) * radius); + } + + // Add points to LineRenderer + line.SetPositions(points); + } +} \ No newline at end of file diff --git a/Boids/Assets/Scripts/Boid.cs.meta b/Boids/Assets/Scripts/Boid.cs.meta new file mode 100644 index 0000000..b08e4ca --- /dev/null +++ b/Boids/Assets/Scripts/Boid.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 8fa9bbc6d9eb4c368be190feba139e7c +timeCreated: 1589638836 \ No newline at end of file diff --git a/Boids/Assets/Scripts/BoidController.cs b/Boids/Assets/Scripts/BoidController.cs new file mode 100644 index 0000000..e912e11 --- /dev/null +++ b/Boids/Assets/Scripts/BoidController.cs @@ -0,0 +1,131 @@ +using System; +using System.Collections.Generic; +using UnityEditor; +using UnityEngine; +using Debug = System.Diagnostics.Debug; +using Random = UnityEngine.Random; + +public class BoidController : MonoBehaviour { + // Controller Attributes + [NonSerialized] public Rect Space; + [NonSerialized] public Rect Boundary; + + // Swarm Attributes + public int boidCount = 50; + public float boidGroupRange = 1.0f; + public float boidStartVelocity = 0.005f; + [SerializeField] public float boidVelocityLimit = 1.0f; + + // Boid Rules are multiplied by this to allow rule 'tweaking' + [SerializeField] public float globalBias = 1.0f; + [SerializeField] public float separationBias = 0.05f; + [SerializeField] public float alignmentBias = 0.05f; + [SerializeField] public float cohesionBias = 0.05f; + [SerializeField] public float boundaryBias = 1f; + + // Enable/Disable Boid Rules + [SerializeField] public bool enableSeparation = true; + [SerializeField] public bool enableAlignment = true; + [SerializeField] public bool enableCohesion = true; + [SerializeField] public bool enableBoundary = true; + + [SerializeField] public float boidSeparationRange = 2.3f; // Boid Separation rule's activation distance + [SerializeField] public float boundaryForce = 10f; // The force applied when a Boid hits the boundary + [SerializeField] public bool localFlocks = true; // Calculate Local 'Neighborhood' for flocks? + [SerializeField] public bool edgeWrapping = true; // Enforce Edge Wrapping + [SerializeField] public int circleVertexCount = 40; // The number of vertices for circles displayed + [SerializeField] public float circleWidth = 0.1f; // Width of circle + + + 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); + Gizmos.DrawWireCube(Boundary.center, Boundary.size); + +#if UNITY_EDITOR + if (focusedBoid != null) + Handles.DrawWireDisc(focusedBoid.transform.position, Vector3.forward, boidGroupRange); +#endif + + if (Cam == null) + return; + + // Draw a Wire Cube for the Cam's Viewport Area + 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; + var screenHeight = screenTopRight.y - screenBottomLeft.y; + Gizmos.DrawWireCube(Cam.transform.position, new Vector3(screenWidth, screenHeight, 1)); + } + + void Update() { + // Focus a different Boid + if (Input.GetKeyDown("space")) { + // Undo previous Boid's focus + if (focusedBoid != null) + focusedBoid.DisableFocusing(); + + focusedBoid = boids[Random.Range(0, boids.Count)]; + focusedBoid.enableFocusing(); + } + } + + private void Start() { + // Setup Camera + Cam = Camera.main; + + // Size the Rectangle based on the Camera's Orthographic View + float height = 2f * Cam.orthographicSize; + 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); + } + + 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 + boid.transform.parent = transform; + boids.Add(boid.GetComponent()); + } + } + + public void RemoveBoids(int n) { + 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(-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/Scripts/BoidController.cs.meta b/Boids/Assets/Scripts/BoidController.cs.meta new file mode 100644 index 0000000..8adc59b --- /dev/null +++ b/Boids/Assets/Scripts/BoidController.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7d72224fef7a4fb4a998b0980fe0eb77 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Boids/Assets/Scripts/BoidControllerEditor.cs b/Boids/Assets/Scripts/BoidControllerEditor.cs new file mode 100644 index 0000000..877237b --- /dev/null +++ b/Boids/Assets/Scripts/BoidControllerEditor.cs @@ -0,0 +1,46 @@ +using UnityEditor; +using UnityEngine; + +[CustomEditor(typeof(BoidController))] +public class BoidControllerEditor : Editor { + public override void OnInspectorGUI() { + var controller = (BoidController) target; + + // Boid Count update + EditorGUI.BeginChangeCheck(); + controller.boidCount = EditorGUILayout.IntSlider("Boid Count", controller.boidCount, 1, 500); + // Check must be performed or Boids will be added outside of gameplay + if (EditorGUI.EndChangeCheck() && Application.isPlaying) { + int diff = controller.boidCount - controller.boids.Count; + if (diff > 1) + controller.AddBoids(diff); + else if (diff < 0) + controller.RemoveBoids(Mathf.Abs(diff)); + } + + // Basic Boid Controller Attributes + controller.boidGroupRange = EditorGUILayout.Slider("Group Range", controller.boidGroupRange, 0.01f, 7.5f); + controller.boidStartVelocity = EditorGUILayout.Slider("Start Velocity", controller.boidStartVelocity, 0.01f, 5.0f); + controller.boidVelocityLimit = EditorGUILayout.Slider("Max Velocity", controller.boidVelocityLimit, 0.01f, 5.0f); + controller.boidSeparationRange = EditorGUILayout.Slider("Separation Range", controller.boidSeparationRange, 0.01f, 5.0f); + controller.boundaryForce = EditorGUILayout.Slider("Boundary Force", controller.boundaryForce, 0.25f, 1f); + + // Boid Bias Attributes + controller.alignmentBias = EditorGUILayout.Slider("Alignment Bias", controller.alignmentBias, 0.001f, 0.5f); + controller.cohesionBias = EditorGUILayout.Slider("Cohesion Bias", controller.cohesionBias, 0.001f, 0.5f); + controller.separationBias = EditorGUILayout.Slider("Separation Bias", controller.separationBias, 0.001f, 0.5f); + controller.boundaryBias = EditorGUILayout.Slider("Boundary Bias", controller.boundaryBias, 0.01f, 0.5f); + + controller.localFlocks = EditorGUILayout.Toggle("Use Groups?", controller.localFlocks); + controller.edgeWrapping = EditorGUILayout.Toggle("Enforce Wrapping?", controller.edgeWrapping); + + controller.enableAlignment = EditorGUILayout.Toggle("Enable Alignment?", controller.enableAlignment); + controller.enableCohesion = EditorGUILayout.Toggle("Enable Cohesion?", controller.enableCohesion); + controller.enableSeparation = EditorGUILayout.Toggle("Enable Separation?", controller.enableSeparation); + controller.enableBoundary = EditorGUILayout.Toggle("Enable Boundary?", controller.enableBoundary); + + // Boid Rendering + controller.circleVertexCount = EditorGUILayout.IntSlider("Circle Vertex Count", controller.circleVertexCount, 4, 360); + controller.circleWidth = EditorGUILayout.Slider("Circle Line Width", controller.circleWidth, 0.01f, 1f); + } +} \ No newline at end of file diff --git a/Boids/Assets/Scripts/BoidControllerEditor.cs.meta b/Boids/Assets/Scripts/BoidControllerEditor.cs.meta new file mode 100644 index 0000000..7c1ad9d --- /dev/null +++ b/Boids/Assets/Scripts/BoidControllerEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6ec8daef16a2459989f9d8b81bb248ea +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Boids/Assets/Scripts/Triangle.cs b/Boids/Assets/Scripts/Triangle.cs new file mode 100644 index 0000000..09cb353 --- /dev/null +++ b/Boids/Assets/Scripts/Triangle.cs @@ -0,0 +1,43 @@ +using System; +using System.Linq; +using UnityEngine; + +public class Triangle : MonoBehaviour { + [NonSerialized] public Color FillColor = Color.red; + public MeshRenderer meshRenderer; + + private void Start () { + // Create Vector2 vertices + var vertices2D = new Vector2[] { + new Vector2(0,1), + new Vector2(0.4f,0), + new Vector2(-0.4f,0), + }; + + var vertices3D = System.Array.ConvertAll(vertices2D, v => v); + + // Use the triangulator to get indices for creating triangles + var triangulator = new Triangulator(vertices2D); + var indices = triangulator.Triangulate(); + + // Generate a color for each vertex + var colors = Enumerable.Repeat(FillColor, vertices3D.Length).ToArray(); + + // Create the mesh + var mesh = new Mesh { + vertices = vertices3D, + triangles = indices, + colors = colors + }; + + mesh.RecalculateNormals(); + mesh.RecalculateBounds(); + + // Set up game object with mesh; + meshRenderer = gameObject.AddComponent(); + meshRenderer.material = (Material) Resources.Load("BoidMaterial"); + + var filter = gameObject.AddComponent(); + filter.mesh = mesh; + } +} \ No newline at end of file diff --git a/Boids/Assets/Scripts/Triangle.cs.meta b/Boids/Assets/Scripts/Triangle.cs.meta new file mode 100644 index 0000000..193ea0f --- /dev/null +++ b/Boids/Assets/Scripts/Triangle.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 39c543193832a9745a8985fb0e672377 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Boids/Assets/Scripts/Triangulator.cs b/Boids/Assets/Scripts/Triangulator.cs new file mode 100644 index 0000000..be2d244 --- /dev/null +++ b/Boids/Assets/Scripts/Triangulator.cs @@ -0,0 +1,126 @@ +using System.Collections.Generic; +using UnityEngine; + +/// +/// This script can be used to split a 2D polygon into triangles. +/// The algorithm supports concave polygons, but not polygons with holes, +/// or multiple polygons at once. +/// Taken from +/// +public class Triangulator +{ + private readonly List _mPoints; + + public Triangulator(IEnumerable points) + { + _mPoints = new List(points); + } + + public int[] Triangulate() + { + var indices = new List(); + + var n = _mPoints.Count; + if (n < 3) + return indices.ToArray(); + + var V = new int[n]; + if (Area() > 0) { + for (var v = 0; v < n; v++) + V[v] = v; + } else { + for (var v = 0; v < n; v++) + V[v] = n - 1 - v; + } + + var nv = n; + var count = 2 * nv; + for (int m = 0, v = nv - 1; nv > 2;) { + if (count-- <= 0) + return indices.ToArray(); + + var u = v; + if (nv <= u) + u = 0; + v = u + 1; + if (nv <= v) + v = 0; + var w = v + 1; + if (nv <= w) + w = 0; + + if (Snip(u, v, w, nv, V)) { + int a, b, c, s, t; + a = V[u]; + b = V[v]; + c = V[w]; + indices.Add(a); + indices.Add(b); + indices.Add(c); + m++; + for (s = v, t = v + 1; t < nv; s++, t++) + V[s] = V[t]; + nv--; + count = 2 * nv; + } + } + + indices.Reverse(); + return indices.ToArray(); + } + + private float Area() + { + var n = _mPoints.Count; + var A = 0.0f; + for (int p = n - 1, q = 0; q < n; p = q++) { + var pval = _mPoints[p]; + var qval = _mPoints[q]; + A += pval.x * qval.y - qval.x * pval.y; + } + return A * 0.5f; + } + + private bool Snip(int u, int v, int w, int n, int[] V) + { + int p; + var A = _mPoints[V[u]]; + var B = _mPoints[V[v]]; + var C = _mPoints[V[w]]; + if (Mathf.Epsilon > (B.x - A.x) * (C.y - A.y) - (B.y - A.y) * (C.x - A.x)) + return false; + for (p = 0; p < n; p++) { + if (p == u || p == v || p == w) + continue; + var P = _mPoints[V[p]]; + if (InsideTriangle(A, B, C, P)) + return false; + } + return true; + } + + private static bool InsideTriangle(Vector2 A, Vector2 B, Vector2 C, Vector2 P) + { + float ax, ay, bx, by, cx, cy, apx, apy, bpx, bpy, cpx, cpy; + float cCROSSap, bCROSScp, aCROSSbp; + + ax = C.x - B.x; + ay = C.y - B.y; + bx = A.x - C.x; + by = A.y - C.y; + cx = B.x - A.x; + cy = B.y - A.y; + apx = P.x - A.x; + apy = P.y - A.y; + bpx = P.x - B.x; + bpy = P.y - B.y; + cpx = P.x - C.x; + cpy = P.y - C.y; + + aCROSSbp = ax * bpy - ay * bpx; + cCROSSap = cx * apy - cy * apx; + bCROSScp = bx * cpy - by * cpx; + + return aCROSSbp >= 0.0f && bCROSScp >= 0.0f && cCROSSap >= 0.0f; + } +} \ No newline at end of file diff --git a/Boids/Assets/Scripts/Triangulator.cs.meta b/Boids/Assets/Scripts/Triangulator.cs.meta new file mode 100644 index 0000000..6f27df3 --- /dev/null +++ b/Boids/Assets/Scripts/Triangulator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 959398144280d9145b8e5c4ec9f36837 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Boids/Assets/Scripts/Util.cs b/Boids/Assets/Scripts/Util.cs new file mode 100644 index 0000000..4e8cf5a --- /dev/null +++ b/Boids/Assets/Scripts/Util.cs @@ -0,0 +1,23 @@ +using UnityEngine; + +public class Util { + public static Vector2 RotateBy(Vector2 v, float a) { + var ca = Mathf.Cos(a); + var sa = Mathf.Sin(a); + var rx = v.x * ca - v.y * sa; + + return new Vector2((float) rx, (float) (v.x * sa + v.y * ca)); + } + + public static Vector2 LimitVelocity(Vector2 v, float max) { + if (v.magnitude > max) { + v = (v / v.magnitude) * max; + } + + return v; + } + + public static Vector2 AbsVector(Vector2 vector) { + return new Vector2(vector.x, vector.y); + } +} \ No newline at end of file diff --git a/Boids/Assets/Scripts/Util.cs.meta b/Boids/Assets/Scripts/Util.cs.meta new file mode 100644 index 0000000..f2b1332 --- /dev/null +++ b/Boids/Assets/Scripts/Util.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9a91db8d912fd1d4dbebfaf69dad59e3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Boids/ProjectSettings/ProjectSettings.asset b/Boids/ProjectSettings/ProjectSettings.asset index 511c333..f889fde 100644 --- a/Boids/ProjectSettings/ProjectSettings.asset +++ b/Boids/ProjectSettings/ProjectSettings.asset @@ -17,7 +17,7 @@ PlayerSettings: defaultCursor: {fileID: 0} cursorHotspot: {x: 0, y: 0} m_SplashScreenBackgroundColor: {r: 0.13725491, g: 0.12156863, b: 0.1254902, a: 1} - m_ShowUnitySplashScreen: 1 + m_ShowUnitySplashScreen: 0 m_ShowUnitySplashLogo: 1 m_SplashScreenOverlayOpacity: 1 m_SplashScreenAnimation: 1 @@ -53,7 +53,7 @@ PlayerSettings: iosShowActivityIndicatorOnLoading: -1 androidShowActivityIndicatorOnLoading: -1 iosAppInBackgroundBehavior: 0 - displayResolutionDialog: 0 + displayResolutionDialog: 1 iosAllowHTTPDownload: 1 allowedAutorotateToPortrait: 1 allowedAutorotateToPortraitUpsideDown: 1