├── .gitignore ├── README.md ├── README.md.meta ├── Resources.meta ├── Resources ├── InControlManager.prefab ├── InControlManager.prefab.meta ├── MusicPlayer.prefab ├── MusicPlayer.prefab.meta ├── MusicStack.prefab ├── MusicStack.prefab.meta ├── SoundPlayer.prefab └── SoundPlayer.prefab.meta ├── Scripts.meta └── Scripts ├── Asserts.cs ├── Asserts.cs.meta ├── Audio.meta ├── Audio ├── AmbientSounds.cs ├── AmbientSounds.cs.meta ├── FadeOutAudioSource.cs ├── FadeOutAudioSource.cs.meta ├── MusicPlayer.cs ├── MusicPlayer.cs.meta ├── MusicStack.meta ├── MusicStack │ ├── AudioClipMusicAsset.cs │ ├── AudioClipMusicAsset.cs.meta │ ├── BasicMusicStackElement.cs │ ├── BasicMusicStackElement.cs.meta │ ├── MusicStack.cs │ ├── MusicStack.cs.meta │ ├── SceneBackgroundMusic.cs │ ├── SceneBackgroundMusic.cs.meta │ ├── SmoothLoopMusicAsset.cs │ └── SmoothLoopMusicAsset.cs.meta ├── SmoothLoopAudioClip.cs ├── SmoothLoopAudioClip.cs.meta ├── SmoothLoopAudioSource.cs ├── SmoothLoopAudioSource.cs.meta ├── SoundPlayer.cs └── SoundPlayer.cs.meta ├── CameraShake.cs ├── CameraShake.cs.meta ├── CheatCodeDetector.cs ├── CheatCodeDetector.cs.meta ├── CleanUpParticles.cs ├── CleanUpParticles.cs.meta ├── Editor.meta ├── Editor ├── CustomMenuItems.cs ├── CustomMenuItems.cs.meta ├── EnumFlagPropertyDrawer.cs ├── EnumFlagPropertyDrawer.cs.meta ├── InspectorNavigator.cs ├── InspectorNavigator.cs.meta ├── KeywordReplacer.cs ├── KeywordReplacer.cs.meta ├── RandomRangeDrawer.cs └── RandomRangeDrawer.cs.meta ├── EnumFlagAttribute.cs ├── EnumFlagAttribute.cs.meta ├── EventList.cs ├── EventList.cs.meta ├── ExportableScene.meta ├── ExportableScene ├── Editor.meta ├── Editor │ ├── ExportableScenePropertyDrawer.cs │ └── ExportableScenePropertyDrawer.cs.meta ├── ExportableScene.cs └── ExportableScene.cs.meta ├── Extensions.meta ├── Extensions ├── QuickUnityExtensions.cs ├── QuickUnityExtensions.cs.meta ├── QuickUnityMathExtensions.cs └── QuickUnityMathExtensions.cs.meta ├── Physics.meta ├── Physics ├── NavigatorMovement.cs ├── NavigatorMovement.cs.meta ├── PhysicsMovement.cs ├── PhysicsMovement.cs.meta ├── StabilizeTorque.cs ├── StabilizeTorque.cs.meta ├── StandUpStraight.cs └── StandUpStraight.cs.meta ├── PrioritySortingKey.cs ├── PrioritySortingKey.cs.meta ├── RandomRange.cs ├── RandomRange.cs.meta ├── Services.meta ├── Services ├── ServiceLocator.cs ├── ServiceLocator.cs.meta ├── ServiceMonoBehaviour.cs └── ServiceMonoBehaviour.cs.meta ├── Singleton.meta ├── Singleton ├── ResourceSingletonAttribute.cs ├── ResourceSingletonAttribute.cs.meta ├── SceneSingleton.cs ├── SceneSingleton.cs.meta ├── Singleton.cs └── Singleton.cs.meta ├── TimeScaleController.cs ├── TimeScaleController.cs.meta ├── Timer.meta ├── Timer ├── Timer.cs ├── Timer.cs.meta ├── TimerExtensions.cs ├── TimerExtensions.cs.meta ├── TimerManager.cs └── TimerManager.cs.meta ├── TrackerCameraMovement.cs ├── TrackerCameraMovement.cs.meta ├── TriggerCollidersTracker.cs ├── TriggerCollidersTracker.cs.meta ├── Vector2Int.cs ├── Vector2Int.cs.meta ├── WireSphereGizmo.cs └── WireSphereGizmo.cs.meta /.gitignore: -------------------------------------------------------------------------------- 1 | [Ll]ibrary/ 2 | [Tt]emp/ 3 | [Oo]bj/ 4 | [Bb]uild/ 5 | 6 | # Autogenerated VS/MD solution and project files 7 | *.csproj 8 | *.unityproj 9 | *.sln 10 | *.suo 11 | *.tmp 12 | *.user 13 | *.userprefs 14 | *.pidb 15 | *.booproj 16 | 17 | # Unity3D generated meta files 18 | *.pidb.meta 19 | 20 | # Unity3D Generated File On Crash Reports 21 | sysinfo.txt 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | I'm retiring this repo after making so many different branches of this locally. 2 | I might create a new version of this at some point. 3 | 4 | # QuickUnityTools 5 | 6 | A bunch of scripts I use when working with Unity to do things faster! (Handy for game jams!) 7 | 8 | It's about time I finally made a submodule out of all the scripts I have lying around... 9 | Feel free to use any of these scripts in your games. A reference would be appreciated but it's not necessary. 10 | 11 | This stuff might change without notice as I add new tools and improve any APIs... 12 | I'll be creating documention whenever it's convenient, but feel free to ask me about these scripts on Twitter: @adamgryu 13 | 14 | Happy Game Development :) 15 | -------------------------------------------------------------------------------- /README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e8bb8c5e06c93fe4284afa2d323eaee7 3 | timeCreated: 1450127766 4 | licenseType: Free 5 | DefaultImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Resources.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: bee93a86a9ab83f4ca3e28ec4e5002c3 3 | folderAsset: yes 4 | timeCreated: 1454097959 5 | licenseType: Free 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Resources/InControlManager.prefab: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!1 &121382 4 | GameObject: 5 | m_ObjectHideFlags: 0 6 | m_PrefabParentObject: {fileID: 0} 7 | m_PrefabInternal: {fileID: 100100000} 8 | serializedVersion: 5 9 | m_Component: 10 | - component: {fileID: 440894} 11 | - component: {fileID: 11439814} 12 | m_Layer: 8 13 | m_Name: InControlManager 14 | m_TagString: Untagged 15 | m_Icon: {fileID: 0} 16 | m_NavMeshLayer: 0 17 | m_StaticEditorFlags: 0 18 | m_IsActive: 1 19 | --- !u!4 &440894 20 | Transform: 21 | m_ObjectHideFlags: 1 22 | m_PrefabParentObject: {fileID: 0} 23 | m_PrefabInternal: {fileID: 100100000} 24 | m_GameObject: {fileID: 121382} 25 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 26 | m_LocalPosition: {x: 0, y: 1.37, z: 0} 27 | m_LocalScale: {x: 1, y: 1, z: 1} 28 | m_Children: [] 29 | m_Father: {fileID: 0} 30 | m_RootOrder: 0 31 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 32 | --- !u!114 &11439814 33 | MonoBehaviour: 34 | m_ObjectHideFlags: 1 35 | m_PrefabParentObject: {fileID: 0} 36 | m_PrefabInternal: {fileID: 100100000} 37 | m_GameObject: {fileID: 121382} 38 | m_Enabled: 1 39 | m_EditorHideFlags: 0 40 | m_Script: {fileID: 11500000, guid: c7b5e8de77c5b4597ae7fbfb49a95e22, type: 3} 41 | m_Name: 42 | m_EditorClassIdentifier: 43 | logDebugInfo: 0 44 | invertYAxis: 0 45 | enableXInput: 1 46 | useFixedUpdate: 0 47 | dontDestroyOnLoad: 1 48 | customProfiles: 49 | - KeyboardAndMouseProfile 50 | --- !u!1001 &100100000 51 | Prefab: 52 | m_ObjectHideFlags: 1 53 | serializedVersion: 2 54 | m_Modification: 55 | m_TransformParent: {fileID: 0} 56 | m_Modifications: [] 57 | m_RemovedComponents: [] 58 | m_ParentPrefab: {fileID: 0} 59 | m_RootGameObject: {fileID: 121382} 60 | m_IsPrefabParent: 1 61 | -------------------------------------------------------------------------------- /Resources/InControlManager.prefab.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: bbe22400a0ad2fc4e9d46726507a1acc 3 | timeCreated: 1454125742 4 | licenseType: Free 5 | NativeFormatImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Resources/MusicPlayer.prefab: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!1 &129508 4 | GameObject: 5 | m_ObjectHideFlags: 0 6 | m_PrefabParentObject: {fileID: 0} 7 | m_PrefabInternal: {fileID: 100100000} 8 | serializedVersion: 5 9 | m_Component: 10 | - component: {fileID: 433064} 11 | - component: {fileID: 11494380} 12 | m_Layer: 0 13 | m_Name: MusicPlayer 14 | m_TagString: Untagged 15 | m_Icon: {fileID: 0} 16 | m_NavMeshLayer: 0 17 | m_StaticEditorFlags: 0 18 | m_IsActive: 1 19 | --- !u!4 &433064 20 | Transform: 21 | m_ObjectHideFlags: 1 22 | m_PrefabParentObject: {fileID: 0} 23 | m_PrefabInternal: {fileID: 100100000} 24 | m_GameObject: {fileID: 129508} 25 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 26 | m_LocalPosition: {x: -109.49736, y: 60.066124, z: -8.0625} 27 | m_LocalScale: {x: 1, y: 1, z: 1} 28 | m_Children: [] 29 | m_Father: {fileID: 0} 30 | m_RootOrder: 0 31 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 32 | --- !u!114 &11494380 33 | MonoBehaviour: 34 | m_ObjectHideFlags: 1 35 | m_PrefabParentObject: {fileID: 0} 36 | m_PrefabInternal: {fileID: 100100000} 37 | m_GameObject: {fileID: 129508} 38 | m_Enabled: 1 39 | m_EditorHideFlags: 0 40 | m_Script: {fileID: 11500000, guid: 10da20cb8d3d60a4eb7500cbe9b7bf05, type: 3} 41 | m_Name: 42 | m_EditorClassIdentifier: 43 | musicMixerGroup: {fileID: 243763981611373278, guid: 8fd65192e26cb5b44b53cdcec90e6088, 44 | type: 2} 45 | fadeInRate: 0.25 46 | fadeOutRate: 0.5 47 | --- !u!1001 &100100000 48 | Prefab: 49 | m_ObjectHideFlags: 1 50 | serializedVersion: 2 51 | m_Modification: 52 | m_TransformParent: {fileID: 0} 53 | m_Modifications: [] 54 | m_RemovedComponents: [] 55 | m_ParentPrefab: {fileID: 0} 56 | m_RootGameObject: {fileID: 129508} 57 | m_IsPrefabParent: 1 58 | -------------------------------------------------------------------------------- /Resources/MusicPlayer.prefab.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b053506bd7975f74b8fe2281d9bdad1a 3 | timeCreated: 1450215009 4 | licenseType: Free 5 | NativeFormatImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Resources/MusicStack.prefab: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!1 &129508 4 | GameObject: 5 | m_ObjectHideFlags: 0 6 | m_PrefabParentObject: {fileID: 0} 7 | m_PrefabInternal: {fileID: 100100000} 8 | serializedVersion: 5 9 | m_Component: 10 | - component: {fileID: 433064} 11 | - component: {fileID: 114330149919376814} 12 | m_Layer: 0 13 | m_Name: MusicStack 14 | m_TagString: Untagged 15 | m_Icon: {fileID: 0} 16 | m_NavMeshLayer: 0 17 | m_StaticEditorFlags: 0 18 | m_IsActive: 1 19 | --- !u!4 &433064 20 | Transform: 21 | m_ObjectHideFlags: 1 22 | m_PrefabParentObject: {fileID: 0} 23 | m_PrefabInternal: {fileID: 100100000} 24 | m_GameObject: {fileID: 129508} 25 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 26 | m_LocalPosition: {x: -109.49736, y: 60.066124, z: -8.0625} 27 | m_LocalScale: {x: 1, y: 1, z: 1} 28 | m_Children: [] 29 | m_Father: {fileID: 0} 30 | m_RootOrder: 0 31 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 32 | --- !u!1001 &100100000 33 | Prefab: 34 | m_ObjectHideFlags: 1 35 | serializedVersion: 2 36 | m_Modification: 37 | m_TransformParent: {fileID: 0} 38 | m_Modifications: [] 39 | m_RemovedComponents: [] 40 | m_ParentPrefab: {fileID: 0} 41 | m_RootGameObject: {fileID: 129508} 42 | m_IsPrefabParent: 1 43 | --- !u!114 &114330149919376814 44 | MonoBehaviour: 45 | m_ObjectHideFlags: 1 46 | m_PrefabParentObject: {fileID: 0} 47 | m_PrefabInternal: {fileID: 100100000} 48 | m_GameObject: {fileID: 129508} 49 | m_Enabled: 1 50 | m_EditorHideFlags: 0 51 | m_Script: {fileID: 11500000, guid: 92ec81ff692086c458fc2e96242b11ea, type: 3} 52 | m_Name: 53 | m_EditorClassIdentifier: 54 | mixerGroup: {fileID: 243763981611373278, guid: 8fd65192e26cb5b44b53cdcec90e6088, 55 | type: 2} 56 | -------------------------------------------------------------------------------- /Resources/MusicStack.prefab.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 41308c390179b6e469618a8b3be2e238 3 | timeCreated: 1450215009 4 | licenseType: Free 5 | NativeFormatImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Resources/SoundPlayer.prefab: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!1 &185932 4 | GameObject: 5 | m_ObjectHideFlags: 0 6 | m_PrefabParentObject: {fileID: 0} 7 | m_PrefabInternal: {fileID: 100100000} 8 | serializedVersion: 5 9 | m_Component: 10 | - component: {fileID: 492442} 11 | - component: {fileID: 11456836} 12 | m_Layer: 0 13 | m_Name: SoundPlayer 14 | m_TagString: Untagged 15 | m_Icon: {fileID: 0} 16 | m_NavMeshLayer: 0 17 | m_StaticEditorFlags: 0 18 | m_IsActive: 1 19 | --- !u!4 &492442 20 | Transform: 21 | m_ObjectHideFlags: 1 22 | m_PrefabParentObject: {fileID: 0} 23 | m_PrefabInternal: {fileID: 100100000} 24 | m_GameObject: {fileID: 185932} 25 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 26 | m_LocalPosition: {x: 0.14774781, y: -0.8848554, z: 0} 27 | m_LocalScale: {x: 1, y: 1, z: 1} 28 | m_Children: [] 29 | m_Father: {fileID: 0} 30 | m_RootOrder: 0 31 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 32 | --- !u!114 &11456836 33 | MonoBehaviour: 34 | m_ObjectHideFlags: 1 35 | m_PrefabParentObject: {fileID: 0} 36 | m_PrefabInternal: {fileID: 100100000} 37 | m_GameObject: {fileID: 185932} 38 | m_Enabled: 1 39 | m_EditorHideFlags: 0 40 | m_Script: {fileID: 11500000, guid: 0ac27dde6773e2048a3af87551f31828, type: 3} 41 | m_Name: 42 | m_EditorClassIdentifier: 43 | soundMixerGroup: {fileID: 243265969154359278, guid: 8fd65192e26cb5b44b53cdcec90e6088, 44 | type: 2} 45 | --- !u!1001 &100100000 46 | Prefab: 47 | m_ObjectHideFlags: 1 48 | serializedVersion: 2 49 | m_Modification: 50 | m_TransformParent: {fileID: 0} 51 | m_Modifications: 52 | - target: {fileID: 0} 53 | propertyPath: soundMixerGroup 54 | value: 55 | objectReference: {fileID: 24378234, guid: d9e41bc711dbe3e40b3a332b205a6329, 56 | type: 2} 57 | - target: {fileID: 0} 58 | propertyPath: musicMixerGroup 59 | value: 60 | objectReference: {fileID: 24315298, guid: d9e41bc711dbe3e40b3a332b205a6329, 61 | type: 2} 62 | m_RemovedComponents: [] 63 | m_ParentPrefab: {fileID: 0} 64 | m_RootGameObject: {fileID: 185932} 65 | m_IsPrefabParent: 1 66 | -------------------------------------------------------------------------------- /Resources/SoundPlayer.prefab.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0c372e0fd7fb3b8489342af6a50a2327 3 | timeCreated: 1450133544 4 | licenseType: Free 5 | NativeFormatImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Scripts.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a62a20f8847b03f469aebfe151e5fbf7 3 | folderAsset: yes 4 | timeCreated: 1450128518 5 | licenseType: Free 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Scripts/Asserts.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using UnityEngine; 6 | 7 | public static class Asserts { 8 | public static void NotNull(object obj, string failureMessage = "") { 9 | if (obj == null) { 10 | throw new NullReferenceException("Not Null Assertion failed: " + failureMessage); 11 | } 12 | } 13 | 14 | public static void Null(object obj, string failureMessage = "") { 15 | if (obj != null) { 16 | throw new Exception("Null Assertion failed: " + failureMessage); 17 | } 18 | } 19 | 20 | public static void AssertTrue(bool expression, string failureMessage) { 21 | if (!expression) { 22 | throw new Exception("Assertion failed: " + failureMessage); 23 | } 24 | } 25 | 26 | public static void WeakAssertTrue(bool expression, string failureMessage) { 27 | if (!expression) { 28 | Debug.LogError("Weak Assertion failed: " + failureMessage); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Scripts/Asserts.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 79e3444fe5bfd654998bb1e40e188585 3 | timeCreated: 1507766112 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Scripts/Audio.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 858abbca84eee314c9b2a25e7c49a567 3 | folderAsset: yes 4 | timeCreated: 1450140351 5 | licenseType: Free 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Scripts/Audio/AmbientSounds.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using UnityEngine; 6 | using UnityEngine.Audio; 7 | using UnityEngine.SceneManagement; 8 | 9 | namespace QuickUnityTools.Audio { 10 | public class AmbientSounds : ServiceMonoBehaviour { 11 | 12 | [Serializable] 13 | public class AmbientSound { 14 | public AudioClip clip; 15 | public float volume = 1; 16 | public bool fadeIn = true; 17 | public AudioSource existingSource; 18 | } 19 | 20 | public AmbientSound[] soundsInScene; 21 | public AudioMixerGroup mixerGroup; 22 | 23 | private void Awake() { 24 | AmbientSoundManager.instance.Initalize(this); 25 | } 26 | 27 | protected override void OnEnable() { 28 | base.OnEnable(); 29 | AmbientSoundManager.instance.UpdateSounds(soundsInScene); 30 | } 31 | 32 | public void SetSounds(AmbientSound[] sounds) { 33 | soundsInScene = sounds; 34 | AmbientSoundManager.instance.UpdateSounds(sounds); 35 | } 36 | 37 | private class AmbientSoundManager : Singleton { 38 | private AudioMixerGroup mixerGroup; 39 | private float fadeTime = 1f; 40 | private Dictionary playingSounds = new Dictionary(); 41 | private List soundFades = new List(); 42 | private bool initalized = false; 43 | 44 | public void Initalize(AmbientSounds soundGroup) { 45 | if (!initalized) { 46 | mixerGroup = soundGroup.mixerGroup; 47 | UpdateSounds(soundGroup.soundsInScene); 48 | initalized = true; 49 | } 50 | } 51 | 52 | /** 53 | * This is the old way of triggering 54 | private void Awake() { 55 | SceneManager.activeSceneChanged += OnActiveSceneChanged; 56 | } 57 | 58 | private void OnActiveSceneChanged(Scene oldScene, Scene newScene) { 59 | var soundGroups = GameObject.FindObjectsOfType(); 60 | if (soundGroups.Length > 1) { 61 | Debug.LogWarning("Duplicate ambient sounds in this scene! There should only be one."); 62 | } 63 | var sounds = soundGroups.FirstOrDefault(); 64 | UpdateSounds(sounds != null ? sounds.soundsInScene : new AmbientSound[0]); 65 | } 66 | */ 67 | 68 | public void UpdateSounds(AmbientSound[] newSounds) { 69 | CancelPreviousFades(); 70 | 71 | // Create sources or update sources for the sounds in the new set. 72 | foreach (AmbientSound sound in newSounds) { 73 | if (!playingSounds.ContainsKey(sound.clip)) { 74 | var newSource = sound.existingSource != null ? sound.existingSource : new GameObject(sound.clip.name).AddComponent(); 75 | newSource.transform.parent = this.transform; 76 | newSource.clip = sound.clip; 77 | newSource.volume = sound.fadeIn ? 0 : sound.volume; 78 | newSource.loop = true; 79 | newSource.outputAudioMixerGroup = mixerGroup; 80 | newSource.Play(); 81 | playingSounds[sound.clip] = newSource; 82 | } 83 | var source = playingSounds[sound.clip]; 84 | if (source.volume != sound.volume) { 85 | FadeTowardsVolume(source, sound.volume); 86 | } 87 | } 88 | 89 | // Remove sounds that are not in the new set. 90 | var soundsNotInScene = playingSounds.Where(playingSound => !newSounds.Any(newSound => newSound.clip == playingSound.Key)).Select(s => s.Value); 91 | foreach (AudioSource source in soundsNotInScene.ToArray()) { 92 | source.gameObject.AddComponent(); 93 | playingSounds.Remove(source.clip); 94 | } 95 | } 96 | 97 | private void FadeTowardsVolume(AudioSource source, float volume) { 98 | soundFades.Add(StartCoroutine(FadeSourceVolumeRoutine(source, volume, fadeTime))); 99 | } 100 | 101 | private IEnumerator FadeSourceVolumeRoutine(AudioSource source, float targetVolume, float fadeTime) { 102 | float originalVolume = source.volume; 103 | float startTime = Time.time; 104 | while (Time.time - startTime < fadeTime) { 105 | source.volume = Mathf.Lerp(originalVolume, targetVolume, (Time.time - startTime) / fadeTime); 106 | yield return null; 107 | } 108 | source.volume = targetVolume; 109 | } 110 | 111 | private void CancelPreviousFades() { 112 | foreach (var fade in soundFades) { 113 | StopCoroutine(fade); 114 | } 115 | } 116 | } 117 | } 118 | } -------------------------------------------------------------------------------- /Scripts/Audio/AmbientSounds.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: adab7e3f85f17c045949a02f087ab71b 3 | timeCreated: 1515447441 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Scripts/Audio/FadeOutAudioSource.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | 4 | namespace QuickUnityTools.Audio { 5 | 6 | public class FadeOutAudioSource : MonoBehaviour { 7 | 8 | public float fadeOutTime = 1f; 9 | public bool destroyGameObjectOnFinish = true; 10 | 11 | private Timer fadeOutTimer; 12 | private AudioSource audioSource; 13 | private float startVolume; 14 | 15 | private void Start() { 16 | audioSource = GetComponent(); 17 | startVolume = audioSource.volume; 18 | fadeOutTimer = this.RegisterTimer(fadeOutTime, () => { 19 | if (destroyGameObjectOnFinish) { 20 | GameObject.Destroy(gameObject); 21 | } 22 | }); 23 | } 24 | 25 | private void Update() { 26 | audioSource.volume = Mathf.Lerp(startVolume, 0, fadeOutTimer.GetPercentageComplete()); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Scripts/Audio/FadeOutAudioSource.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: bd8d4212d60602e4e818efaaaee64605 3 | timeCreated: 1450140475 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Scripts/Audio/MusicPlayer.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | using UnityEngine.Audio; 4 | using System; 5 | 6 | namespace QuickUnityTools.Audio { 7 | /// 8 | /// A class for managing the currently playing song. 9 | /// 10 | [ResourceSingleton("MusicPlayer")] 11 | [Obsolete("The MusicPlayerStack is the newer version of this class.")] 12 | public class MusicPlayer : Singleton { 13 | 14 | public AudioSource currentAudio { get; private set; } 15 | private AudioSource fadeOutAudio = null; 16 | private bool isFadingOutAudioClip { get { return this.fadeOutAudio != null; } } 17 | 18 | public AudioMixerGroup musicMixerGroup; 19 | public float fadeInRate = 0.25f; 20 | public float fadeOutRate = 0.5f; 21 | public float musicVolume { 22 | get { return this._musicVolume; } 23 | set { 24 | this._musicVolume = value; 25 | this.currentAudio.volume = this._musicVolume; 26 | this.fadeOutAudio.volume = 0; 27 | } 28 | } 29 | private float _musicVolume = 0.45f; 30 | 31 | private void Update() { 32 | if (this.fadeOutAudio != null) { 33 | // If the old audio is not null, fade it out. 34 | if (this.fadeOutAudio.volume > 0.1f * this.musicVolume) { 35 | this.fadeOutAudio.volume -= fadeOutRate * this.musicVolume * Time.deltaTime; 36 | } else { 37 | // Destroy audio once we are done fading it out. 38 | GameObject.Destroy(fadeOutAudio); 39 | this.fadeOutAudio = null; 40 | } 41 | } 42 | 43 | if (this.currentAudio != null) { 44 | // If the current audio is low volume, turn it up! 45 | if (this.currentAudio.volume < this.musicVolume) { 46 | this.currentAudio.volume = Mathf.MoveTowards(this.currentAudio.volume, this.musicVolume, this.fadeInRate * Time.deltaTime); 47 | } 48 | } 49 | } 50 | 51 | /// 52 | /// Pass null to fade out current music. 53 | /// 54 | public void TransitionToMusic(AudioClip newMusic) { 55 | // Don't transition if the music is the same. 56 | if (this.currentAudio != null && this.currentAudio.clip == newMusic) { 57 | return; 58 | } 59 | 60 | // Always fade out the current music. 61 | bool musicWasPlayingBeforeTransition = this.currentAudio != null; 62 | if (this.currentAudio != null) { 63 | this.BeginMusicFadeOut(); 64 | } 65 | 66 | // Don't play any new music if none is specified. 67 | if (newMusic == null) { 68 | return; 69 | } 70 | 71 | // Play the new audio. 72 | this.currentAudio = this.gameObject.AddComponent(); 73 | this.currentAudio.clip = newMusic; 74 | this.currentAudio.Play(); 75 | this.currentAudio.outputAudioMixerGroup = musicMixerGroup; 76 | this.currentAudio.loop = true; 77 | 78 | if (musicWasPlayingBeforeTransition) { 79 | // Fade in the new music. 80 | this.currentAudio.volume = 0.1f * this.musicVolume; 81 | } 82 | } 83 | 84 | /// 85 | /// Fades out the current music. 86 | /// 87 | public void FadeOutMusic() { 88 | this.TransitionToMusic(null); 89 | } 90 | 91 | /// 92 | /// Stops the current music without fading it out. 93 | /// 94 | public void StopMusic() { 95 | if (this.currentAudio != null) { 96 | GameObject.Destroy(this.currentAudio); 97 | this.currentAudio = null; 98 | } 99 | if (this.isFadingOutAudioClip) { 100 | // Destroy the previous audio clip that hasn't finished fading out. 101 | GameObject.Destroy(this.fadeOutAudio); 102 | this.fadeOutAudio = null; 103 | } 104 | } 105 | 106 | private void BeginMusicFadeOut() { 107 | if (this.isFadingOutAudioClip) { 108 | // Destroy the previous audio clip that hasn't finished fading out. 109 | GameObject.Destroy(this.fadeOutAudio); 110 | this.fadeOutAudio = null; 111 | } 112 | this.fadeOutAudio = this.currentAudio; 113 | this.currentAudio = null; 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /Scripts/Audio/MusicPlayer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 10da20cb8d3d60a4eb7500cbe9b7bf05 3 | timeCreated: 1450140186 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Scripts/Audio/MusicStack.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8584a91d0342a0a44b285f16c4359406 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Scripts/Audio/MusicStack/AudioClipMusicAsset.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | using UnityEngine.Audio; 4 | 5 | namespace QuickUnityTools.Audio { 6 | 7 | /// 8 | /// An adapter for so they can be used as a music stack asset. 9 | /// 10 | public class AudioClipMusicAsset : MusicStack.IMusicAsset { 11 | 12 | private AudioClip clip; 13 | 14 | public AudioClipMusicAsset(AudioClip clip) { 15 | this.clip = clip; 16 | } 17 | 18 | public int GetMusicID() { 19 | return clip.GetInstanceID(); 20 | } 21 | 22 | public MusicStack.IMusicPlayer CreatePlayer() { 23 | return new AudioClipMusicPlayer(clip); 24 | } 25 | 26 | /// 27 | /// Creates an audio source to play the related audio clip on the music stack. 28 | /// 29 | public class AudioClipMusicPlayer : MusicStack.IMusicPlayer { 30 | 31 | private AudioSource source; 32 | 33 | public AudioClipMusicPlayer(AudioClip clip) { 34 | GameObject obj = new GameObject("AudioClipMusicPlayer (" + clip + ")"); 35 | AudioSource source = obj.AddComponent(); 36 | source.clip = clip; 37 | source.loop = true; 38 | this.source = source; 39 | UnityEngine.Object.DontDestroyOnLoad(obj); 40 | } 41 | 42 | public float volume { 43 | get { return source.volume; } 44 | set { source.volume = value; } 45 | } 46 | 47 | public bool isPlaying { get { return source.isPlaying; } } 48 | 49 | public AudioMixerGroup outputAudioMixerGroup { 50 | set { source.outputAudioMixerGroup = value; } 51 | } 52 | 53 | public void CleanUp() { 54 | GameObject.Destroy(source.gameObject); 55 | } 56 | 57 | public void Play() { 58 | source.Play(); 59 | } 60 | 61 | public void Stop() { 62 | source.Stop(); 63 | } 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /Scripts/Audio/MusicStack/AudioClipMusicAsset.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 63eb07f36aef94b4bbbabf68ff582e01 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Scripts/Audio/MusicStack/BasicMusicStackElement.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | using System; 4 | 5 | namespace QuickUnityTools.Audio { 6 | 7 | [Serializable] 8 | public class BasicMusicStackElement : IMusicStackElement { 9 | 10 | public AudioClip music; 11 | public SmoothLoopAudioClip smoothLoopMusic; 12 | public MusicStackPriorty priority = MusicStackPriorty.Low; 13 | public MusicStack.Transition transitionIn = MusicStack.Transition.CROSS_FADE; 14 | public MusicStack.Transition transitionOut = MusicStack.Transition.CROSS_FADE; 15 | public float desiredVolume = 1; 16 | 17 | public BasicMusicStackElement(AudioClip music) { 18 | this.music = music; 19 | } 20 | 21 | public MusicStack.IMusicAsset GetMusicAsset() { 22 | if (smoothLoopMusic != null) { 23 | return new SmoothLoopMusicAsset(smoothLoopMusic); 24 | } 25 | return music != null ? new AudioClipMusicAsset(music) : null; 26 | } 27 | 28 | public MusicStack.Transition GetReleaseControlTransition() { 29 | return transitionOut; 30 | } 31 | 32 | public MusicStack.Transition GetTakeControlTransition() { 33 | return transitionIn; 34 | } 35 | 36 | public float GetDesiredVolume() { 37 | return desiredVolume; 38 | } 39 | 40 | public MusicStackPriorty GetPriority() { 41 | return priority; 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /Scripts/Audio/MusicStack/BasicMusicStackElement.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 62e4a2834bd078446b000b16f4d9d4ff 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Scripts/Audio/MusicStack/MusicStack.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | using UnityEngine.Audio; 4 | using System.Collections.Generic; 5 | using System; 6 | 7 | namespace QuickUnityTools.Audio { 8 | 9 | /// 10 | /// Music on the music stack is sorted by priority and then timestamp. 11 | /// 12 | public enum MusicStackPriorty { 13 | Low = 0, 14 | Normal = 50, 15 | High = 100, 16 | } 17 | 18 | /// 19 | /// Represents a class that can register itself into the music stack. A music stack element can 20 | /// define the it wishes to play, the volume that audio should be played 21 | /// at, and how the music transitions when the element takes or loses control. 22 | /// 23 | public interface IMusicStackElement { 24 | MusicStack.IMusicAsset GetMusicAsset(); 25 | MusicStack.Transition GetTakeControlTransition(); 26 | MusicStack.Transition GetReleaseControlTransition(); 27 | float GetDesiredVolume(); 28 | MusicStackPriorty GetPriority(); 29 | } 30 | 31 | /// 32 | /// Manages a sorted stack of songs that should be playing for any given moment. 33 | /// The element on the top of the stack is the one that should be currently playing. 34 | /// 35 | [ResourceSingleton("MusicStack")] 36 | public class MusicStack : Singleton { 37 | 38 | // Links 39 | public AudioMixerGroup mixerGroup; 40 | 41 | // State 42 | private SortedList musicStack = new SortedList(); 43 | private Dictionary playerControllers = new Dictionary(); 44 | 45 | // Helpers 46 | private IMusicStackElement currentMusic { get { return musicStackView.Count == 0 ? null : musicStackView[0]; } } 47 | private IList musicStackView; 48 | private Dictionary.ValueCollection musicControllersView; 49 | 50 | private void Awake() { 51 | // Avoid creating garbage. 52 | musicStackView = musicStack.Values; 53 | musicControllersView = playerControllers.Values; 54 | } 55 | 56 | /// 57 | /// Adds an elment to the music stack. If this becomes the top element, it will transition to these music settings. 58 | /// 59 | public PrioritySortingKey AddToMusicStack(IMusicStackElement addedMusic) { 60 | var prevMusic = currentMusic; 61 | 62 | var newKey = new PrioritySortingKey((int)addedMusic.GetPriority()); 63 | musicStack.Add(newKey, addedMusic); 64 | 65 | if (currentMusic != prevMusic) { 66 | Asserts.AssertTrue(addedMusic == currentMusic, "Somehow we added to the music stack and yet a different music rose to the top?"); 67 | TransitionToMusic(prevMusic, currentMusic, currentMusic.GetTakeControlTransition()); 68 | } 69 | return newKey; 70 | } 71 | 72 | /// 73 | /// Removes an element from the music stack. If this was the top element, this will trigger a transition to the next element. 74 | /// 75 | public void RemoveFromMusicStack(PrioritySortingKey key) { 76 | Asserts.AssertTrue(musicStack.ContainsKey(key), "This key is not in the music stack!"); 77 | var keyMusic = musicStack[key]; 78 | bool wasPlayingKeyMusic = keyMusic == currentMusic; 79 | 80 | musicStack.Remove(key); 81 | if (wasPlayingKeyMusic) { 82 | TransitionToMusic(keyMusic, currentMusic, keyMusic.GetReleaseControlTransition()); 83 | } 84 | } 85 | 86 | /// 87 | /// Transitions from one music configuration to another, using the specified transition. 88 | /// 89 | /// MusicStack elements that share the same can reuse the existing audio player 90 | /// when transitioning between them. This lets the music keep going. 91 | /// 92 | private void TransitionToMusic(IMusicStackElement oldElement, IMusicStackElement newElement, Transition transition) { 93 | // We get the music data associated with each stack element. 94 | var oldMusicData = GetAudioData(oldElement); 95 | var newMusicData = GetAudioData(newElement); 96 | 97 | if (oldMusicData != null) { 98 | playerControllers[oldMusicData.GetMusicID()].StopMusic(transition.fadeOutTime); 99 | } 100 | 101 | if (newMusicData != null) { 102 | var id = newMusicData.GetMusicID(); 103 | if (playerControllers.ContainsKey(id)) { 104 | // Reuse an existing player if one exists. 105 | playerControllers[id].StartMusic(transition.fadeInTime, transition.fadeInDelay, newElement.GetDesiredVolume()); 106 | } else { 107 | // Create a new player 108 | var player = newMusicData.CreatePlayer(); 109 | player.outputAudioMixerGroup = mixerGroup; 110 | var controller = new MusicPlayerController(player); 111 | playerControllers.Add(id, controller); 112 | controller.StartMusic(transition.fadeInTime, transition.fadeInDelay, newElement.GetDesiredVolume()); 113 | } 114 | } 115 | } 116 | 117 | private void Update() { 118 | bool cleanUp = false; 119 | foreach (var controller in musicControllersView) { 120 | controller.ManualUpdate(); 121 | cleanUp |= controller.canCleanUp; 122 | } 123 | if (cleanUp) { 124 | playerControllers.BufferedForEach(pair => pair.Value.canCleanUp, pair => { 125 | pair.Value.CleanUp(); 126 | playerControllers.Remove(pair.Key); 127 | }); 128 | } 129 | } 130 | 131 | private static IMusicAsset GetAudioData(IMusicStackElement element) { 132 | return element == null ? null : element.GetMusicAsset(); 133 | } 134 | 135 | /// 136 | /// Provides an API for controling a music player that includes methods for starting, stopping, and fading the music. 137 | /// 138 | public class MusicPlayerController { 139 | 140 | public bool canCleanUp { get { return !isPlaying && player.volume == 0; } } 141 | 142 | private IMusicPlayer player; 143 | private float normalVolume; 144 | 145 | private float fadeDelayCountdown; 146 | private float destinationVolume; 147 | private float? fadeSpeed = null; 148 | 149 | private bool isPlaying { get { return destinationVolume > 0; } } 150 | 151 | public MusicPlayerController(IMusicPlayer player) { 152 | this.player = player; 153 | this.normalVolume = player.volume; 154 | player.volume = 0; 155 | } 156 | 157 | public void StartMusic(float fadeInTime, float fadeInDelay, float? newVolume) { 158 | if (newVolume.HasValue) { 159 | normalVolume = newVolume.Value; 160 | } 161 | FadeTowardsVolume(normalVolume, fadeInTime, fadeInDelay); 162 | } 163 | 164 | public void StopMusic(float fadeOutTime) { 165 | FadeTowardsVolume(0, fadeOutTime, 0); 166 | } 167 | 168 | private void FadeTowardsVolume(float toVolume, float fadeTime, float fadeDelay) { 169 | fadeDelayCountdown = fadeDelay; 170 | destinationVolume = toVolume; 171 | fadeSpeed = fadeTime > 0 ? Mathf.Abs(destinationVolume - player.volume) / fadeTime : (float?)null; 172 | } 173 | 174 | public void ManualUpdate() { 175 | if (fadeDelayCountdown > 0) { 176 | fadeDelayCountdown -= Time.unscaledDeltaTime; 177 | } else { 178 | if (isPlaying && !player.isPlaying) { 179 | player.Play(); 180 | } 181 | if (!isPlaying && player.isPlaying && player.volume <= 0) { 182 | player.Stop(); 183 | } 184 | float volumeDelta = fadeSpeed.HasValue ? Time.unscaledDeltaTime * fadeSpeed.Value : 1000f; 185 | player.volume = Mathf.MoveTowards(player.volume, destinationVolume, volumeDelta); 186 | } 187 | } 188 | 189 | public void CleanUp() { 190 | player.CleanUp(); 191 | } 192 | } 193 | 194 | /// 195 | /// Represents a Unity music asset that can be played and has a unqiue ID. 196 | /// 197 | public interface IMusicAsset { 198 | 199 | /// 200 | /// Creates a Unity object that will play this music asset. 201 | /// 202 | IMusicPlayer CreatePlayer(); 203 | 204 | /// 205 | /// Returns the unique music ID that the music stack uses to detect if two stack elements are playing the same music. 206 | /// 207 | int GetMusicID(); 208 | } 209 | 210 | /// 211 | /// Represents a Unity object that plays an associated . 212 | /// 213 | public interface IMusicPlayer { 214 | AudioMixerGroup outputAudioMixerGroup { set; } 215 | float volume { get; set; } 216 | bool isPlaying { get; } 217 | void Play(); 218 | void Stop(); 219 | void CleanUp(); 220 | } 221 | 222 | /// 223 | /// Defines how the current music ends and the new music starts. 224 | /// 225 | [Serializable] 226 | public struct Transition { 227 | public float fadeOutTime; 228 | public float fadeInTime; 229 | public float fadeInDelay; 230 | 231 | public readonly static Transition INSTANT = new Transition() { }; 232 | public readonly static Transition CROSS_FADE = new Transition() { fadeOutTime = 2f, fadeInTime = 2 }; 233 | public static readonly Transition QUICK_OUT = new Transition() { fadeOutTime = 0.1f, fadeInDelay = 0.5f }; 234 | } 235 | } 236 | } -------------------------------------------------------------------------------- /Scripts/Audio/MusicStack/MusicStack.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 92ec81ff692086c458fc2e96242b11ea 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Scripts/Audio/MusicStack/SceneBackgroundMusic.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | using UnityEngine.SceneManagement; 4 | using System; 5 | 6 | namespace QuickUnityTools.Audio { 7 | 8 | /// 9 | /// Music that plays in the background of the scene. It is added to the MusicStack for the 10 | /// scene's lifetime. 11 | /// 12 | public class SceneBackgroundMusic : MonoBehaviour { 13 | 14 | public BasicMusicStackElement musicConfig; 15 | 16 | private PrioritySortingKey musicStackKey; 17 | 18 | private void Start() { 19 | musicStackKey = MusicStack.instance.AddToMusicStack(musicConfig); 20 | } 21 | 22 | private void OnDestroy() { 23 | if (MusicStack.instance != null) { 24 | MusicStack.instance.RemoveFromMusicStack(musicStackKey); 25 | } 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /Scripts/Audio/MusicStack/SceneBackgroundMusic.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f13443b5f8d41a749b492814a3cf8e54 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Scripts/Audio/MusicStack/SmoothLoopMusicAsset.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | using UnityEngine.Audio; 4 | 5 | namespace QuickUnityTools.Audio { 6 | public class SmoothLoopMusicAsset : MusicStack.IMusicAsset { 7 | 8 | private SmoothLoopAudioClip clip; 9 | 10 | public SmoothLoopMusicAsset(SmoothLoopAudioClip clip) { 11 | this.clip = clip; 12 | } 13 | 14 | public MusicStack.IMusicPlayer CreatePlayer() { 15 | return new SmoothLoopMusicPlayer(clip); 16 | } 17 | 18 | public int GetMusicID() { 19 | return clip.GetInstanceID(); 20 | } 21 | } 22 | 23 | public class SmoothLoopMusicPlayer : MusicStack.IMusicPlayer { 24 | private SmoothLoopAudioSource source; 25 | 26 | public SmoothLoopMusicPlayer(SmoothLoopAudioClip clip) { 27 | GameObject obj = new GameObject("SmoothLoopMusicPlayer (" + clip + ")"); 28 | SmoothLoopAudioSource source = obj.AddComponent(); 29 | source.music = clip; 30 | this.source = source; 31 | UnityEngine.Object.DontDestroyOnLoad(obj); 32 | } 33 | 34 | public AudioMixerGroup outputAudioMixerGroup { 35 | set { source.mixerGroup = value; } 36 | } 37 | 38 | public float volume { 39 | get { return source.volume; } 40 | set { source.volume = value; } 41 | } 42 | 43 | public bool isPlaying { get { return source.isPlaying; } } 44 | 45 | public void CleanUp() { 46 | GameObject.Destroy(source.gameObject); 47 | } 48 | 49 | public void Play() { 50 | source.Play(); 51 | } 52 | 53 | public void Stop() { 54 | source.Stop(); 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /Scripts/Audio/MusicStack/SmoothLoopMusicAsset.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8db6f165c62b77942bf7e1f394dacb21 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Scripts/Audio/SmoothLoopAudioClip.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | namespace QuickUnityTools.Audio { 6 | /// 7 | /// An audio clip and metadata required for smooth looping. 8 | /// 9 | [CreateAssetMenu] 10 | public class SmoothLoopAudioClip : ScriptableObject { 11 | public AudioClip clip; 12 | public float introMeasures = 1; 13 | public float beatsPerMeasure = 4; 14 | public float beatsPerMinute = 130; 15 | public float length { get { return clip.length; } } 16 | } 17 | } -------------------------------------------------------------------------------- /Scripts/Audio/SmoothLoopAudioClip.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0916717ac71643b4dac82db1c88a7860 3 | timeCreated: 1502924398 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Scripts/Audio/SmoothLoopAudioSource.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using UnityEngine; 5 | using UnityEngine.Audio; 6 | using UnityEngine.SceneManagement; 7 | 8 | namespace QuickUnityTools.Audio { 9 | 10 | /// 11 | /// A special audio source that allows an audio clip to smoothly loop from some point other than the beginning of the song. 12 | /// 13 | public class SmoothLoopAudioSource : MonoBehaviour { 14 | 15 | private const int SECONDS_IN_MINUTE = 60; 16 | 17 | public SmoothLoopAudioClip music; 18 | 19 | public AudioMixerGroup mixerGroup { 20 | get { return _mixerGroup; } 21 | set { 22 | _mixerGroup = value; 23 | if (audioSources != null) { 24 | for (int i = 0; i < audioSources.Length; i++) { 25 | audioSources[i].outputAudioMixerGroup = mixerGroup; 26 | } 27 | } 28 | } 29 | } 30 | private AudioMixerGroup _mixerGroup; 31 | 32 | public float volume { 33 | get { return _volume; } 34 | set { 35 | _volume = value; 36 | if (audioSources != null) { 37 | foreach (AudioSource source in audioSources) { source.volume = value; } 38 | } 39 | } 40 | } 41 | private float _volume = 1; 42 | 43 | public bool isPlaying { get; private set; } 44 | 45 | private AudioSource[] audioSources; 46 | private float introTime { get { return ((music.beatsPerMeasure * music.introMeasures) / music.beatsPerMinute) * SECONDS_IN_MINUTE; } } 47 | private float loopTime { get { return music.length - introTime; } } 48 | 49 | private double startDpsTime; 50 | private int nextAudioSourceIndex; 51 | private int numberOfLoopsScheduled; 52 | private Timer loopTimer; 53 | 54 | private void Start() { 55 | audioSources = new AudioSource[2]; 56 | for (int i = 0; i < audioSources.Length; i++) { 57 | audioSources[i] = gameObject.AddComponent(); 58 | audioSources[i].clip = music.clip; 59 | audioSources[i].outputAudioMixerGroup = mixerGroup; 60 | audioSources[i].volume = volume; 61 | } 62 | } 63 | 64 | public void Play() { 65 | // Reset the play state. 66 | Stop(); 67 | startDpsTime = AudioSettings.dspTime; 68 | isPlaying = true; 69 | 70 | // Play the full track once. 71 | audioSources[0].Play(); 72 | 73 | // Schedule the track to play again from its looping position. 74 | audioSources[1].PlayScheduled(startDpsTime + music.length); 75 | audioSources[1].time = introTime; 76 | 77 | nextAudioSourceIndex = 0; 78 | numberOfLoopsScheduled = 1; 79 | 80 | // After the first run of the track finishes, schedule the second loop while the first loop is playing. 81 | loopTimer = this.RegisterTimer(music.length, ScheduleNextLoop); 82 | } 83 | 84 | public void Stop() { 85 | for (int i = 0; i < audioSources.Length; i++) { 86 | audioSources[i].Stop(); 87 | } 88 | if (loopTimer != null) { 89 | loopTimer.Cancel(); 90 | } 91 | isPlaying = false; 92 | } 93 | 94 | /// 95 | /// Schedules the next loop to play. When this function executes, the last scheduled loop 96 | /// should just be beginning to play. 97 | /// 98 | /// This method will call itself to schedule another loop after each loop finishes. 99 | /// 100 | private void ScheduleNextLoop() { 101 | audioSources[nextAudioSourceIndex].PlayScheduled(startDpsTime + music.length + loopTime * numberOfLoopsScheduled); 102 | audioSources[nextAudioSourceIndex].time = introTime; 103 | 104 | nextAudioSourceIndex = (nextAudioSourceIndex + 1) % audioSources.Length; 105 | numberOfLoopsScheduled++; 106 | 107 | loopTimer = this.RegisterTimer(loopTime, ScheduleNextLoop); 108 | } 109 | } 110 | } -------------------------------------------------------------------------------- /Scripts/Audio/SmoothLoopAudioSource.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0a8731744aa8dc947997ef5e310ba3f0 3 | timeCreated: 1502918664 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Scripts/Audio/SoundPlayer.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using UnityEngine.Audio; 5 | using System; 6 | 7 | namespace QuickUnityTools.Audio { 8 | 9 | /// 10 | /// A class for quickly importing and playing one-off sounds. 11 | /// 12 | [ResourceSingleton("SoundPlayer")] 13 | public class SoundPlayer : Singleton { 14 | 15 | public AudioMixerGroup soundMixerGroup; 16 | 17 | private Dictionary loadedSounds = new Dictionary(); 18 | 19 | public AudioSource PlayVaried(AudioClip soundName, float spread = 0.3f) { 20 | AudioSource source = this.Play(soundName); 21 | source.pitch += UnityEngine.Random.Range(-spread / 2, spread / 2); 22 | return source; 23 | } 24 | 25 | public AudioSource Play(AudioClip clip, float volume = 1) { 26 | return this.Play(clip, this.transform.position, volume); 27 | } 28 | 29 | public AudioSource Play(AudioClip clip, Vector3 position, float volume = 1, float spatialBlend = 0) { 30 | return this.PlayOneOff(clip, position, volume, spatialBlend); 31 | } 32 | 33 | public AudioSource PlayLooped(AudioClip clip, Vector3 position) { 34 | AudioSource source = this.CreateAudioObject(clip, position); 35 | source.loop = true; 36 | source.Play(); 37 | return source; 38 | } 39 | 40 | private AudioSource PlayOneOff(AudioClip clip, Vector3 position, float volume = 1, float spatialBlend = 0) { 41 | if (clip == null) { 42 | Debug.LogWarning("Audio clip is not assigned to a value!"); 43 | return null; 44 | } 45 | 46 | AudioSource audioSource = this.CreateAudioObject(clip, position); 47 | audioSource.volume = volume; 48 | audioSource.spatialBlend = spatialBlend; 49 | audioSource.outputAudioMixerGroup = this.soundMixerGroup; 50 | 51 | audioSource.Play(); 52 | 53 | Timer.Register(clip.length + 0.1f, () => { 54 | if (audioSource != null) { 55 | GameObject.Destroy(audioSource.gameObject); 56 | } 57 | }, false, true); 58 | return audioSource; 59 | } 60 | 61 | private AudioSource CreateAudioObject(AudioClip clip, Vector3 position) { 62 | GameObject soundGameObject = new GameObject("AudioSource (Temp)"); 63 | soundGameObject.transform.position = position; 64 | AudioSource audioSource = soundGameObject.AddComponent(); 65 | audioSource.clip = clip; 66 | return audioSource; 67 | } 68 | 69 | [Obsolete] 70 | public AudioSource Play(string soundName) { 71 | return this.Play(this.LookUpAudioClip(soundName)); 72 | } 73 | 74 | [Obsolete] 75 | public AudioSource PlayVaried(string soundName, float spread = 0.3f) { 76 | return this.PlayVaried(this.LookUpAudioClip(soundName), spread); 77 | } 78 | 79 | /// 80 | /// Looks up an audio clip and loads it into memory. 81 | /// Currently, there's no way of unloading this clip in the API. 82 | /// 83 | private AudioClip LookUpAudioClip(string soundName) { 84 | if (loadedSounds.ContainsKey(soundName)) { 85 | return this.loadedSounds[soundName]; 86 | } else { 87 | AudioClip clip = Resources.Load(soundName); 88 | if (clip == null) { 89 | Debug.LogWarning("Tried to play sound from string, but failed: " + soundName); 90 | return null; 91 | } else { 92 | this.loadedSounds.Add(soundName, clip); 93 | return clip; 94 | } 95 | } 96 | } 97 | } 98 | 99 | public static class Volume { 100 | 101 | public enum Channel { 102 | Master, 103 | Music, 104 | Ambience, 105 | SoundEffects, 106 | } 107 | 108 | private static float GetDefaultChannelDB(Channel channel) { 109 | switch (channel) { 110 | case Channel.Master: return -6; 111 | case Channel.Music: return -5; 112 | } 113 | return 0; 114 | } 115 | 116 | public static void SetVolume(Channel channel, float percent) { 117 | float normal = GetDefaultChannelDB(channel); 118 | SetVolume(channel.ToString() + "Volume", percent, normal); 119 | } 120 | 121 | private static void SetVolume(string parameter, float percent, float normal) { 122 | float a = (-80 - normal) / Mathf.Log(0.01f); 123 | float db = a * Mathf.Log(Mathf.Max(0.01f, percent)) + normal; 124 | SoundPlayer.instance.soundMixerGroup.audioMixer.SetFloat(parameter, db); 125 | } 126 | 127 | public static float GetVolume(Channel channel) { 128 | string parameter = channel.ToString() + "Volume"; 129 | 130 | float db; 131 | SoundPlayer.instance.soundMixerGroup.audioMixer.GetFloat(parameter, out db); 132 | 133 | float normal = GetDefaultChannelDB(channel); 134 | float a = (-80 - normal) / Mathf.Log(0.01f); 135 | float percent = Mathf.Exp((db - normal) / a); 136 | return percent; 137 | } 138 | } 139 | 140 | public static class SoundPlayerExtensions { 141 | public static AudioSource Play(this AudioClip clip) { 142 | return SoundPlayer.instance.Play(clip); 143 | } 144 | } 145 | } -------------------------------------------------------------------------------- /Scripts/Audio/SoundPlayer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0ac27dde6773e2048a3af87551f31828 3 | timeCreated: 1450126781 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Scripts/CameraShake.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | /// 7 | /// Adds a shaking effect to the camera which can be triggered through code. A random vector is 8 | /// added to the camera position on OnPreRender() if a shake is occuring. 9 | /// 10 | [RequireComponent(typeof(Camera))] 11 | public class CameraShake : MonoBehaviour { 12 | 13 | [Serializable] 14 | public class ShakeConfiguration { 15 | [Tooltip("The maximum distance the camera can move from its normal position while shaking.")] 16 | public float maxShakeDistance = 0.3f; 17 | 18 | [Tooltip("The time (in seconds) it takes for the camera to stop shaking.")] 19 | public float shakeTime = 1; 20 | 21 | [Tooltip("The strength of the camera shake over the course of its cooldown time.")] 22 | public AnimationCurve shakeDistanceOverTime = AnimationCurve.EaseInOut(0, 0, 1, 1); 23 | } 24 | 25 | public enum Intensity { 26 | Tiny, 27 | Gentle, 28 | Moderate, 29 | Heavy, 30 | Extreme, 31 | } 32 | 33 | public ShakeConfiguration tinyShake; 34 | public ShakeConfiguration gentleShake; 35 | public ShakeConfiguration moderateShake; 36 | public ShakeConfiguration heavyShake; 37 | public ShakeConfiguration extremeShake; 38 | 39 | public bool isShaking { get { return this.shakeCooldown > 0.05f; } } 40 | 41 | private Dictionary shakeConfigs; 42 | private Intensity lastIntensity; 43 | private ShakeConfiguration shakeConfig; 44 | 45 | private float shakeCooldown; 46 | private Vector3 lastShakeVector; 47 | 48 | private void Awake() { 49 | shakeConfigs = new Dictionary(); 50 | shakeConfigs[Intensity.Tiny] = tinyShake; 51 | shakeConfigs[Intensity.Gentle] = gentleShake; 52 | shakeConfigs[Intensity.Moderate] = moderateShake; 53 | shakeConfigs[Intensity.Heavy] = heavyShake; 54 | shakeConfigs[Intensity.Extreme] = extremeShake; 55 | } 56 | 57 | /// 58 | /// Shakes the camera. 59 | /// 60 | public void Shake(Intensity intensity) { 61 | if (!isShaking || intensity > lastIntensity) { 62 | lastIntensity = intensity; 63 | shakeConfig = shakeConfigs[intensity]; 64 | shakeCooldown = shakeConfig.shakeTime; 65 | } 66 | } 67 | 68 | /// 69 | /// Shakes all cameras in the scene. 70 | /// 71 | public static void ShakeAllCameras(Intensity intensity) { 72 | foreach (Camera camera in Camera.allCameras) { 73 | CameraShake shake = camera.GetComponent(); 74 | if (shake != null) { 75 | shake.Shake(intensity); 76 | } 77 | } 78 | } 79 | 80 | private void Update() { 81 | if (isShaking) { 82 | shakeCooldown = Mathf.MoveTowards(shakeCooldown, 0, Time.unscaledDeltaTime); 83 | } 84 | 85 | // Debug shakes by holding R, T, Y, and then pressing a number from 1 to 5. 86 | if (Input.GetKey(KeyCode.R) && Input.GetKey(KeyCode.T) && Input.GetKey(KeyCode.Y)) { 87 | for (KeyCode key = KeyCode.Alpha1; key <= KeyCode.Alpha5; key++) { 88 | if (Input.GetKeyDown(key)) { 89 | Shake((Intensity)(int)(key - KeyCode.Alpha1)); 90 | } 91 | } 92 | } 93 | } 94 | 95 | private void OnPreRender() { 96 | if (isShaking) { 97 | float shakePercent = shakeCooldown / shakeConfig.shakeTime; 98 | this.lastShakeVector = UnityEngine.Random.insideUnitSphere 99 | * shakeConfig.shakeDistanceOverTime.Evaluate(shakePercent) 100 | * shakeConfig.maxShakeDistance; 101 | this.transform.position += this.lastShakeVector; 102 | } 103 | } 104 | 105 | private void OnPostRender() { 106 | if (isShaking) { 107 | this.transform.position -= this.lastShakeVector; 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /Scripts/CameraShake.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 23690bdcbcda53c4fab48258975110e7 3 | timeCreated: 1449944241 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Scripts/CheatCodeDetector.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System; 5 | 6 | public abstract class CheatCodeDetector : MonoBehaviour { 7 | 8 | private class Cheat { 9 | public string cheatCode { get; private set; } 10 | private Action onActivate; 11 | private int cheatTypeIndex; 12 | private CheatCodeDetector controller; 13 | 14 | public Cheat(string cheatCode, Action onActivate, CheatCodeDetector controller) { 15 | this.cheatCode = cheatCode; 16 | this.onActivate = onActivate; 17 | this.cheatTypeIndex = 0; 18 | this.controller = controller; 19 | } 20 | 21 | public void Update() { 22 | if (Input.inputString != "") { 23 | if (Input.inputString[0] == this.cheatCode[this.cheatTypeIndex]) { 24 | this.cheatTypeIndex++; 25 | if (this.cheatTypeIndex == this.cheatCode.Length) { 26 | this.cheatTypeIndex = 0; 27 | Activate(); 28 | } 29 | } else { 30 | this.cheatTypeIndex = 0; 31 | } 32 | } 33 | } 34 | 35 | public void Activate() { 36 | this.onActivate(); 37 | this.controller.OnCheatActivation(); 38 | } 39 | } 40 | 41 | private List cheats = new List(); 42 | 43 | protected virtual void Update() { 44 | foreach (Cheat cheat in this.cheats) { 45 | cheat.Update(); 46 | } 47 | } 48 | 49 | public void RegisterCheat(string cheatCode, Action onActivate) { 50 | this.cheats.Add(new Cheat(cheatCode, onActivate, this)); 51 | } 52 | 53 | public void TriggerCheat(string name) { 54 | var cheat = cheats.FirstOrDefault(c => c.cheatCode == name); 55 | if (cheat != null) { 56 | cheat.Activate(); 57 | } 58 | } 59 | 60 | protected virtual void OnCheatActivation() { 61 | // Triggers on every cheat activation. 62 | } 63 | } -------------------------------------------------------------------------------- /Scripts/CheatCodeDetector.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2be00708972437842b247e59ae179ff8 3 | timeCreated: 1459904989 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Scripts/CleanUpParticles.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | 4 | /// 5 | /// Creates a specified number of particles and cleans up the object after. 6 | /// 7 | /// NOTE: This will override Emission settings on the system. 8 | /// 9 | [RequireComponent(typeof(ParticleSystem))] 10 | public class CleanUpParticles : MonoBehaviour { 11 | 12 | private ParticleSystem system; 13 | 14 | void Start () { 15 | this.system = this.GetComponent(); 16 | } 17 | 18 | void Update () { 19 | if (!this.system.IsAlive()) { 20 | GameObject.Destroy(this.gameObject); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Scripts/CleanUpParticles.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 751b0a58eaa9f2346aedd36bac2a8e46 3 | timeCreated: 1449951287 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Scripts/Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 772a169d2b4d6b3438521ea7435d1fa7 3 | folderAsset: yes 4 | timeCreated: 1458948878 5 | licenseType: Free 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Scripts/Editor/CustomMenuItems.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEditor; 3 | using System.Linq; 4 | using System.Collections.Generic; 5 | 6 | /// 7 | /// A collection of useful shortcuts for manipulating the hierarchy. 8 | /// Some of this was written by me, some of it was collected from random Unity 9 | /// forums and StackOverflow posts. 10 | /// 11 | public class CustomMenuItems { 12 | [MenuItem("GameObject/Hierarchy/Select First Child Each", false, 10)] 13 | private static void SelectChildren() { 14 | Object[] newSelection = Selection.gameObjects.Select(obj => obj.transform.childCount > 0 ? obj.transform.GetChild(0).gameObject : obj).Cast().ToArray(); 15 | Debug.Log(newSelection.Length); 16 | Selection.objects = newSelection; 17 | } 18 | 19 | [MenuItem("GameObject/Hierarchy/Collapse All [Alt + Q] &q", false, -10)] 20 | private static void CollapseAll() { 21 | foreach (GameObject obj in SceneRoots()) { 22 | SetExpandedRecursive(obj, false); 23 | } 24 | } 25 | 26 | [MenuItem("GameObject/Hierarchy/Un-Parent And Collapse All %q", false, 0)] 27 | private static void UnparentAndCollapse() { 28 | foreach (GameObject obj in Selection.gameObjects) { 29 | obj.transform.parent = null; 30 | } 31 | CollapseAll(); 32 | } 33 | 34 | [MenuItem("GameObject/Hierarchy/Place Selection In New Container #%q", false, -5)] 35 | private static void PlaceInNewContainer() { 36 | if (Selection.gameObjects.Length <= 0) { 37 | return; 38 | } 39 | 40 | GameObject container = new GameObject("GameObject"); 41 | 42 | foreach(var gameObj in GetRootSelectedGameObjects()) { 43 | gameObj.transform.parent = container.transform; 44 | } 45 | 46 | Selection.objects = new Object[] { }; 47 | CollapseAll(); 48 | } 49 | 50 | private static IEnumerable GetRootSelectedGameObjects() { 51 | foreach (GameObject obj in Selection.gameObjects) { 52 | // Search up through the objects parents. 53 | bool isChild = false; 54 | Transform nextParent = obj.transform.parent; 55 | while (nextParent != null) { 56 | if (Selection.gameObjects.Any(o => o.gameObject == nextParent.gameObject)) { 57 | isChild = true; 58 | break; 59 | } 60 | nextParent = nextParent.transform.parent; 61 | } 62 | 63 | if (!isChild) { 64 | yield return obj; 65 | } 66 | } 67 | } 68 | 69 | [MenuItem("GameObject/Organize/Decor", false, -5)] 70 | private static void PlaceInDecor() { 71 | PlaceInObject("Decor"); 72 | } 73 | 74 | [MenuItem("GameObject/Organize/Background", false, -5)] 75 | private static void PlaceInBackground() { 76 | PlaceInObject("Background"); 77 | } 78 | 79 | [MenuItem("GameObject/Organize/Buildings", false, -5)] 80 | private static void PlaceInBuildings() { 81 | PlaceInObject("Buildings"); 82 | } 83 | 84 | [MenuItem("GameObject/Organize/Structures", false, -5)] 85 | private static void PlaceInStructures() { 86 | PlaceInObject("Structures"); 87 | } 88 | 89 | [MenuItem("GameObject/Organize/LevelObjects", false, -5)] 90 | private static void PlaceInLevelObjects() { 91 | PlaceInObject("LevelObjects"); 92 | } 93 | 94 | private static void PlaceInObject(string name) { 95 | if (Selection.gameObjects.Length <= 0) { 96 | return; 97 | } 98 | var gameObj = GameObject.Find(name); 99 | if (!gameObj) { 100 | gameObj = new GameObject(name); 101 | return; 102 | } 103 | 104 | foreach (GameObject obj in Selection.gameObjects) { 105 | obj.transform.parent = gameObj.transform; 106 | } 107 | } 108 | 109 | #region CodeFromWeb 110 | 111 | [MenuItem("GameObject/Hierarchy/Sort Children By Name &#%q", false, 1)] 112 | public static void SortGameObjectsByName(MenuCommand menuCommand) { 113 | if (menuCommand.context == null || menuCommand.context.GetType() != typeof(GameObject)) { 114 | EditorUtility.DisplayDialog("Error", "You must select an item to sort in the frame", "Okay"); 115 | return; 116 | } 117 | 118 | GameObject parentObject = (GameObject)menuCommand.context; 119 | 120 | // Build a list of all the Transforms in this player's hierarchy 121 | Transform[] objectTransforms = new Transform[parentObject.transform.childCount]; 122 | for (int i = 0; i < objectTransforms.Length; i++) 123 | objectTransforms[i] = parentObject.transform.GetChild(i); 124 | 125 | int sortTime = System.Environment.TickCount; 126 | 127 | bool sorted = false; 128 | // Perform a bubble sort on the objects 129 | while (sorted == false) { 130 | sorted = true; 131 | for (int i = 0; i < objectTransforms.Length - 1; i++) { 132 | // Compare the two strings to see which is sooner 133 | int comparison = objectTransforms[i].name.CompareTo(objectTransforms[i + 1].name); 134 | 135 | if (comparison > 0) // 1 means that the current value is larger than the last value 136 | { 137 | objectTransforms[i].transform.SetSiblingIndex(objectTransforms[i + 1].GetSiblingIndex()); 138 | sorted = false; 139 | } 140 | } 141 | 142 | // resort the list to get the new layout 143 | for (int i = 0; i < objectTransforms.Length; i++) 144 | objectTransforms[i] = parentObject.transform.GetChild(i); 145 | } 146 | 147 | Debug.Log("Sort took " + (System.Environment.TickCount - sortTime) + " milliseconds"); 148 | } 149 | 150 | public static IEnumerable SceneRoots() { 151 | return UnityEngine.SceneManagement.SceneManager.GetActiveScene().GetRootGameObjects(); 152 | } 153 | 154 | public static void Collapse(GameObject gameObject, bool collapse = true) { 155 | // Bail out immediately if the go doesn't have children. 156 | if (gameObject.transform.childCount == 0) return; 157 | // Get a reference to the hierarchy window. 158 | var hierarchy = GetFocusedWindow("Hierarchy"); 159 | // Select our GameObject. 160 | SelectObject(gameObject); 161 | // Create a new key event (RightArrow for collapsing, LeftArrow for folding) 162 | var key = new Event { keyCode = collapse ? KeyCode.RightArrow : KeyCode.LeftArrow, type = EventType.KeyDown }; 163 | // Finally, send the window the event. 164 | hierarchy.SendEvent(key); 165 | } 166 | 167 | public static void SetExpandedRecursive(GameObject go, bool expand) { 168 | var type = typeof(EditorWindow).Assembly.GetType("UnityEditor.SceneHierarchyWindow"); 169 | var methodInfo = type.GetMethod("SetExpandedRecursive"); 170 | 171 | EditorApplication.ExecuteMenuItem("Window/Hierarchy"); 172 | var window = EditorWindow.focusedWindow; 173 | 174 | methodInfo.Invoke(window, new object[] { go.GetInstanceID(), expand }); 175 | } 176 | 177 | public static void SelectObject(Object obj) { 178 | Selection.activeObject = obj; 179 | } 180 | 181 | public static EditorWindow GetFocusedWindow(string window) { 182 | FocusOnWindow(window); 183 | return EditorWindow.focusedWindow; 184 | } 185 | 186 | public static void FocusOnWindow(string window) { 187 | EditorApplication.ExecuteMenuItem("Window/" + window); 188 | } 189 | 190 | #endregion 191 | } -------------------------------------------------------------------------------- /Scripts/Editor/CustomMenuItems.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f6107f6848a4e6b489afb3a75a509604 3 | timeCreated: 1458948869 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Scripts/Editor/EnumFlagPropertyDrawer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Reflection; 5 | using UnityEditor; 6 | using UnityEngine; 7 | 8 | /// 9 | /// From the Unity wiki. 10 | /// 11 | [CustomPropertyDrawer(typeof(EnumFlagAttribute))] 12 | public class EnumFlagDrawer : PropertyDrawer { 13 | public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { 14 | EnumFlagAttribute flagSettings = (EnumFlagAttribute)attribute; 15 | Enum targetEnum = GetBaseProperty(property); 16 | 17 | string propName = flagSettings.enumName; 18 | if (string.IsNullOrEmpty(propName)) 19 | propName = property.name; 20 | 21 | EditorGUI.BeginProperty(position, label, property); 22 | Enum enumNew = EditorGUI.EnumFlagsField(position, ObjectNames.NicifyVariableName(propName), targetEnum); 23 | property.intValue = (int)Convert.ChangeType(enumNew, targetEnum.GetType()); 24 | EditorGUI.EndProperty(); 25 | } 26 | 27 | static T GetBaseProperty(SerializedProperty prop) { 28 | // Separate the steps it takes to get to this property 29 | string[] separatedPaths = prop.propertyPath.Split('.'); 30 | 31 | // Go down to the root of this serialized property 32 | System.Object reflectionTarget = prop.serializedObject.targetObject as object; 33 | // Walk down the path to get the target object 34 | foreach (var path in separatedPaths) { 35 | FieldInfo fieldInfo = reflectionTarget.GetType().GetField(path); 36 | reflectionTarget = fieldInfo.GetValue(reflectionTarget); 37 | } 38 | return (T)reflectionTarget; 39 | } 40 | } -------------------------------------------------------------------------------- /Scripts/Editor/EnumFlagPropertyDrawer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ef7ca956c9187224cb4ab34993dfe221 3 | timeCreated: 1513997544 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Scripts/Editor/InspectorNavigator.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEditor; 3 | using System.Collections; 4 | using System.Collections.Generic; 5 | using System; 6 | using System.Linq; 7 | 8 | public class InspectorNavigator : EditorWindow { 9 | 10 | private static InspectorNavigator openedInstance; 11 | 12 | private List backList = new List(); 13 | private List forwardList = new List(); 14 | 15 | private int[] previousSelection; 16 | private bool isGoingBack; 17 | private bool isGoingForward; 18 | 19 | [MenuItem("Window/Inspector Navigator/Open")] 20 | private static void Create() { 21 | if (openedInstance) { 22 | Debug.LogWarning("The inspector navigator is already open! Restarting the window..."); 23 | openedInstance.Close(); 24 | openedInstance = null; 25 | } 26 | 27 | InspectorNavigator window = (InspectorNavigator)GetWindow(typeof(InspectorNavigator)); 28 | window.Show(); 29 | window.titleContent = new GUIContent("Navigator"); 30 | window.minSize = new Vector2(10f, EditorGUIUtility.singleLineHeight + 5f); 31 | 32 | openedInstance = window; 33 | } 34 | 35 | [MenuItem("Window/Inspector Navigator/Back %LEFT")] 36 | private static void BackShortcut() { 37 | if (!openedInstance) { 38 | Debug.LogWarning("Open the navigator window first..."); 39 | return; 40 | } 41 | openedInstance.Back(); 42 | } 43 | 44 | [MenuItem("Window/Inspector Navigator/Forward %RIGHT")] 45 | private static void ForwardShortcut() { 46 | if (!openedInstance) { 47 | Debug.LogWarning("Open the navigator window first..."); 48 | return; 49 | } 50 | openedInstance.Forward(); 51 | } 52 | 53 | private void OnSelectionChange() { 54 | if (Selection.instanceIDs != previousSelection) { 55 | if (isGoingBack && backList.Count > 0) { 56 | if (previousSelection != null) { 57 | forwardList.Add(previousSelection); 58 | } 59 | backList.RemoveAt(backList.Count - 1); 60 | } else if (isGoingForward && forwardList.Count > 0) { 61 | if (previousSelection != null) { 62 | backList.Add(previousSelection); 63 | } 64 | forwardList.RemoveAt(forwardList.Count - 1); 65 | } else if (previousSelection != null) { 66 | backList.Add(previousSelection); 67 | forwardList.Clear(); 68 | } 69 | } 70 | 71 | if (backList.Count > 64) { 72 | backList.RemoveAt(0); 73 | } 74 | 75 | isGoingBack = false; 76 | isGoingForward = false; 77 | previousSelection = Selection.instanceIDs; 78 | } 79 | 80 | private void OnDestroy() { 81 | openedInstance = null; 82 | } 83 | 84 | protected virtual void OnGUI() { 85 | GUILayout.BeginHorizontal(); 86 | DrawGUIToolbar(); 87 | GUILayout.EndHorizontal(); 88 | } 89 | 90 | protected virtual void DrawGUIToolbar() { 91 | if (GUILayout.Button("◄")) { 92 | Back(); 93 | } 94 | if (GUILayout.Button("►")) { 95 | Forward(); 96 | } 97 | } 98 | 99 | private void Forward() { 100 | if (forwardList.Count == 0) { 101 | Debug.LogWarning("Nothing to go forward to..."); 102 | return; 103 | } 104 | 105 | isGoingForward = true; 106 | Selection.instanceIDs = forwardList.Last(); 107 | } 108 | 109 | private void Back() { 110 | if (backList.Count == 0) { 111 | Debug.LogWarning("Nothing to go back to..."); 112 | return; 113 | } 114 | 115 | isGoingBack = true; 116 | Selection.instanceIDs = backList.Last(); 117 | } 118 | } -------------------------------------------------------------------------------- /Scripts/Editor/InspectorNavigator.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1aad66bd125102b49bd5a6383102a3f9 3 | timeCreated: 1523309883 4 | licenseType: Free 5 | MonoImporter: 6 | externalObjects: {} 7 | serializedVersion: 2 8 | defaultReferences: [] 9 | executionOrder: 0 10 | icon: {instanceID: 0} 11 | userData: 12 | assetBundleName: 13 | assetBundleVariant: 14 | -------------------------------------------------------------------------------- /Scripts/Editor/KeywordReplacer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using UnityEditor; 5 | using UnityEngine; 6 | 7 | namespace QuickUnityTools.Editor { 8 | public class KeywordReplace : UnityEditor.AssetModificationProcessor { 9 | 10 | public static void OnWillCreateAsset(string path) { 11 | path = path.Replace(".meta", ""); 12 | int index = path.LastIndexOf("."); 13 | if (index < 0) 14 | return; 15 | 16 | string file = path.Substring(index); 17 | if (file != ".cs" && file != ".js" && file != ".boo") 18 | return; 19 | 20 | index = Application.dataPath.LastIndexOf("Assets"); 21 | path = Application.dataPath.Substring(0, index) + path; 22 | if (!System.IO.File.Exists(path)) 23 | return; 24 | 25 | string fileContent = System.IO.File.ReadAllText(path); 26 | fileContent = fileContent.Replace("#NAMESPACE#", GetNamespaceForPath(path)); 27 | 28 | System.IO.File.WriteAllText(path, fileContent); 29 | AssetDatabase.Refresh(); 30 | } 31 | 32 | public static string GetNamespaceForPath(string path) { 33 | return Application.productName; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Scripts/Editor/KeywordReplacer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4b0640a141e28cf4c8674885c77a19ce 3 | timeCreated: 1503510259 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Scripts/Editor/RandomRangeDrawer.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | using System; 4 | using UnityEditor; 5 | 6 | [CustomPropertyDrawer(typeof(RandomRange))] 7 | public class RandomRangeDrawer : PropertyDrawer { 8 | 9 | public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { 10 | // Using BeginProperty / EndProperty on the parent property means that 11 | // prefab override logic works on the entire property. 12 | EditorGUI.BeginProperty(position, label, property); 13 | 14 | // Draw label 15 | position = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label); 16 | 17 | // Don't make child fields be indented 18 | var indent = EditorGUI.indentLevel; 19 | EditorGUI.indentLevel = 0; 20 | 21 | // Calculate rects 22 | var amountRect = new Rect(position.x, position.y, position.width / 2, position.height); 23 | var unitRect = new Rect(position.x + position.width / 2, position.y, position.width / 2, position.height); 24 | 25 | // Draw fields - pass GUIContent.none to each so they are drawn without labels 26 | EditorGUI.PropertyField(amountRect, property.FindPropertyRelative("min"), GUIContent.none); 27 | EditorGUI.PropertyField(unitRect, property.FindPropertyRelative("max"), GUIContent.none); 28 | 29 | // Set indent back to what it was 30 | EditorGUI.indentLevel = indent; 31 | 32 | EditorGUI.EndProperty(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Scripts/Editor/RandomRangeDrawer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 23ec05976adfc4942bb5a944462dca0b 3 | timeCreated: 1459026938 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Scripts/EnumFlagAttribute.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class EnumFlagAttribute : PropertyAttribute { 6 | public string enumName; 7 | 8 | public EnumFlagAttribute() { } 9 | 10 | public EnumFlagAttribute(string name) { 11 | enumName = name; 12 | } 13 | } -------------------------------------------------------------------------------- /Scripts/EnumFlagAttribute.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 74ee393729f60ba4f99f8482891a7cfb 3 | timeCreated: 1513997800 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Scripts/EventList.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using System; 4 | 5 | public class EventList : IEnumerable { 6 | 7 | public event Action onModified; 8 | 9 | private List list; 10 | 11 | public EventList() { 12 | this.list = new List(); 13 | } 14 | 15 | public void Add(T item) { 16 | list.Add(item); 17 | if (onModified != null) { 18 | onModified(); 19 | } 20 | } 21 | 22 | public void Remove(T item) { 23 | list.Remove(item); 24 | if (onModified != null) { 25 | onModified(); 26 | } 27 | } 28 | 29 | public IEnumerator GetEnumerator() { 30 | return list.GetEnumerator(); 31 | } 32 | 33 | IEnumerator IEnumerable.GetEnumerator() { 34 | return list.GetEnumerator(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Scripts/EventList.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d8af5c388f5dcd84aaece79caad251ff 3 | timeCreated: 1522269848 4 | licenseType: Free 5 | MonoImporter: 6 | externalObjects: {} 7 | serializedVersion: 2 8 | defaultReferences: [] 9 | executionOrder: 0 10 | icon: {instanceID: 0} 11 | userData: 12 | assetBundleName: 13 | assetBundleVariant: 14 | -------------------------------------------------------------------------------- /Scripts/ExportableScene.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 427a34ad507b04a46aeeb82ac22ce793 3 | folderAsset: yes 4 | timeCreated: 1454094860 5 | licenseType: Free 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Scripts/ExportableScene/Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0f44ad36eb1c10d40ad71ca834f39ee7 3 | folderAsset: yes 4 | timeCreated: 1454094852 5 | licenseType: Free 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Scripts/ExportableScene/Editor/ExportableScenePropertyDrawer.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | using UnityEditor; 4 | 5 | [CustomPropertyDrawer(typeof(ExportableScene))] 6 | public class ExportableSceneDrawer : PropertyDrawer { 7 | 8 | private bool verified = false; 9 | 10 | public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { 11 | label = EditorGUI.BeginProperty(position, label, property); 12 | Rect contentPosition = EditorGUI.PrefixLabel(position, label); 13 | 14 | var sceneReference = property.FindPropertyRelative("sceneReference"); 15 | Object lastSceneObj = sceneReference.objectReferenceValue; 16 | Object sceneObj = EditorGUI.ObjectField(contentPosition, sceneReference.objectReferenceValue, typeof(SceneAsset), false); 17 | sceneReference.objectReferenceValue = sceneObj; 18 | 19 | EditorGUI.EndProperty(); 20 | 21 | if (lastSceneObj != sceneObj || !verified) { 22 | verified = true; 23 | 24 | // Update the scene name. 25 | string name = sceneObj == null ? "" : sceneObj.name; 26 | var sceneNameProperty = property.FindPropertyRelative("sceneName"); 27 | if (sceneNameProperty.stringValue != name) { 28 | Debug.Log("Setting the exportable scene from " + sceneNameProperty.stringValue + " to " + name); 29 | sceneNameProperty.stringValue = name; 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Scripts/ExportableScene/Editor/ExportableScenePropertyDrawer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9d235b5f5020ce94e8d00b1ef7ce1a43 3 | timeCreated: 1454094869 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Scripts/ExportableScene/ExportableScene.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | using System; 4 | using System.Linq; 5 | 6 | #if UNITY_EDITOR 7 | using UnityEditor; 8 | #endif 9 | 10 | [Serializable] 11 | public class ExportableScene { 12 | [SerializeField] 13 | private UnityEngine.Object sceneReference; // These are SceneAsset objects that disappear in an actual build. 14 | 15 | [SerializeField] 16 | private string sceneName = ""; 17 | 18 | /// 19 | /// Please call this in MonoBehaviours where this is used. 20 | /// 21 | public void Validate(MonoBehaviour dirtyTarget) { 22 | string name = sceneReference != null ? sceneReference.name : ""; 23 | if (sceneName != name) { 24 | Debug.LogWarning("Click here to go to the object and fix outdated ExportableScene reference for " + dirtyTarget.gameObject.name, dirtyTarget); 25 | } 26 | 27 | #if UNITY_EDITOR 28 | if (!EditorBuildSettings.scenes.Any(s => s.path == AssetDatabase.GetAssetPath(sceneReference))) { 29 | Debug.LogWarning("The referenced scene is not included in the build settings!"); 30 | } 31 | #endif 32 | } 33 | 34 | public string GetSceneName() { 35 | if (sceneReference != null) { 36 | if (sceneReference.name != sceneName) { 37 | Debug.LogError("An ExportableScene reference is out of date: " + sceneName + " -> " + sceneReference.name); 38 | } 39 | // NOTE: Cannot verify that the scene name is NULL when the object reference is null. 40 | } 41 | return sceneName; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Scripts/ExportableScene/ExportableScene.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 47df75b20ca59e441ba4e06c79dde6f7 3 | timeCreated: 1454094872 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Scripts/Extensions.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 615bb0f82c1a87a488cd387629f924dd 3 | folderAsset: yes 4 | timeCreated: 1450130937 5 | licenseType: Free 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Scripts/Extensions/QuickUnityExtensions.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System; 5 | using System.Linq; 6 | using System.Text.RegularExpressions; 7 | using UnityEngine.SceneManagement; 8 | using QuickUnityTools.Audio; 9 | 10 | public static class QuickUnityExtensions { 11 | 12 | public static GameObject CloneAt(this GameObject obj, Vector3 position) { 13 | GameObject obj2 = GameObject.Instantiate(obj); 14 | obj2.transform.position = position; 15 | return obj2; 16 | } 17 | 18 | public static GameObject Clone(this GameObject obj) { 19 | GameObject obj2 = GameObject.Instantiate(obj); 20 | return obj2; 21 | } 22 | 23 | public static GameObject CloneInactive(this GameObject obj) { 24 | obj.gameObject.SetActive(false); 25 | GameObject obj2 = GameObject.Instantiate(obj); 26 | obj.gameObject.SetActive(true); 27 | return obj2; 28 | } 29 | 30 | public static string ColorToHexString(this Color32 color) { 31 | string hex = "#" + color.r.ToString("X2") + color.g.ToString("X2") + color.b.ToString("X2"); 32 | return hex; 33 | } 34 | 35 | public static Color HexStringToColor(string hex) { 36 | try { 37 | byte r = byte.Parse(hex.Substring(0, 2), System.Globalization.NumberStyles.HexNumber); 38 | byte g = byte.Parse(hex.Substring(2, 2), System.Globalization.NumberStyles.HexNumber); 39 | byte b = byte.Parse(hex.Substring(4, 2), System.Globalization.NumberStyles.HexNumber); 40 | return new Color32(r, g, b, 255); 41 | } catch (FormatException) { 42 | return Color.black; 43 | } 44 | } 45 | 46 | public static Color SetA(this Color color, float a) { 47 | return new Color(color.r, color.g, color.b, a); 48 | } 49 | 50 | public static Vector3 WorldMousePosition(this Camera camera, float? distanceFromCamera = null) { 51 | if (distanceFromCamera == null) { 52 | distanceFromCamera = 0; 53 | } 54 | return camera.ScreenToWorldPoint(Input.mousePosition.SetZ(distanceFromCamera.Value)); 55 | } 56 | 57 | public static Vector3 RandomWithin(this Bounds bounds) { 58 | Vector3 randomWithin = new Vector3( 59 | bounds.size.x * UnityEngine.Random.value, 60 | bounds.size.y * UnityEngine.Random.value, 61 | bounds.size.z * UnityEngine.Random.value); 62 | return bounds.min + randomWithin; 63 | } 64 | 65 | public static Rect SplitHorizontal(this Rect rect, int number, int index, float spacing = 5) { 66 | float width = (rect.width - (spacing * Math.Max(0, number - 1))) / number; 67 | return new Rect(rect.x + (width + spacing) * index, rect.y, width, rect.height); 68 | } 69 | 70 | public static Rect SplitHorizontal(this Rect rect, float splitPercent, bool first, float spacing = 5) { 71 | return new Rect( 72 | rect.x + (first ? 0 : (rect.width - spacing) * splitPercent + spacing), 73 | rect.y, 74 | (rect.width - spacing) * (first ? splitPercent : 1 - splitPercent), 75 | rect.height); 76 | } 77 | 78 | public static Rect SplitVertical(this Rect rect, int number, int index, float spacing = 5) { 79 | float height = (rect.height - (spacing * Math.Max(0, number - 1))) / number; 80 | return new Rect(rect.x, rect.y + (height + spacing) * index, rect.width, height); 81 | } 82 | 83 | public static string CamelCaseToSpaces(this string text) { 84 | return Regex.Replace(text, "(\\B[A-Z])", " $1"); 85 | } 86 | 87 | public static IEnumerable GetChildren(this Transform transform) { 88 | foreach (Transform child in transform) { 89 | yield return child; 90 | } 91 | } 92 | 93 | public static void DestroyChildren(this Transform transform) { 94 | foreach (Transform child in transform) { 95 | GameObject.Destroy(child.gameObject); 96 | } 97 | } 98 | 99 | public static Vector3 TransformPointTo(this RectTransform from, Vector3 point, RectTransform to) { 100 | return to.InverseTransformPoint(from.TransformPoint(point)); 101 | } 102 | 103 | public static Vector2 WorldToRectTransform(Vector3 worldPosition, RectTransform toTransform) { 104 | Vector2 viewportPoint = Camera.main.WorldToViewportPoint(worldPosition); 105 | RectTransform canvasRect = toTransform.GetComponentInParent().GetComponent(); 106 | Vector2 canvasPoint = Vector2.Scale(canvasRect.sizeDelta, (viewportPoint - Vector2.one * 0.5f)); 107 | return TransformPointTo(canvasRect, canvasPoint, toTransform); 108 | } 109 | 110 | public static GameObject GetGameObject(this ResourceRequest request) { 111 | return request.asset as GameObject; 112 | } 113 | 114 | public static IEnumerable FindComponentsOfTypeInScene(this Scene scene) where T : Component { 115 | foreach (GameObject rootObj in scene.GetRootGameObjects()) { 116 | foreach (T foundObj in rootObj.GetComponentsInChildren()) { 117 | yield return foundObj; 118 | } 119 | } 120 | } 121 | 122 | public static IEnumerable FindComponentsOfTypeInScene(this Scene scene, Type type) { 123 | foreach (GameObject rootObj in scene.GetRootGameObjects()) { 124 | foreach (Component foundObj in rootObj.GetComponentsInChildren(type)) { 125 | yield return foundObj; 126 | } 127 | } 128 | } 129 | 130 | public static bool IsPointWithin(this Collider collider, Vector3 point) { 131 | if (!collider.bounds.Contains(point)) { 132 | return false; 133 | } 134 | 135 | Vector3 towardCenter = collider.bounds.center - point; 136 | RaycastHit hit; 137 | return !collider.Raycast(new Ray(point, towardCenter), out hit, towardCenter.magnitude); 138 | } 139 | 140 | public static IEnumerable Yield(this T item) { 141 | yield return item; 142 | } 143 | 144 | public static AudioSource Play(this AudioClip clip) { 145 | return SoundPlayer.instance.Play(clip); 146 | } 147 | 148 | public static bool IncludesLayer(this LayerMask mask, int layer) { 149 | return mask == (mask | (1 << layer)); 150 | } 151 | 152 | public static IEnumerator WrapAsEnumerator(this YieldInstruction yieldInstruction) { 153 | yield return yieldInstruction; 154 | } 155 | 156 | #region Collections 157 | 158 | public static void IndexedForEach(this IEnumerable collection, Action action) { 159 | int i = 0; 160 | foreach (T element in collection) { 161 | action(element, i++); 162 | } 163 | } 164 | 165 | public static void BufferedForEach(this IEnumerable collection, Func condition, Action performIf) { 166 | LinkedList buffer = new LinkedList(); 167 | foreach (T obj in collection) { 168 | if (condition(obj)) { 169 | buffer.AddFirst(obj); 170 | } 171 | } 172 | foreach (T obj in buffer) { 173 | performIf(obj); 174 | } 175 | } 176 | 177 | public static T MinValue(this IEnumerable collection, Func heuristic) { 178 | T minObj = default(T); 179 | float min = float.PositiveInfinity; 180 | foreach (T t in collection) { 181 | float value = heuristic(t); 182 | if (value < min) { 183 | minObj = t; 184 | min = value; 185 | } 186 | } 187 | return minObj; 188 | } 189 | 190 | public static T MaxValue(this IEnumerable collection, Func heuristic) { 191 | T minObj = default(T); 192 | float max = float.NegativeInfinity; 193 | foreach (T t in collection) { 194 | float value = heuristic(t); 195 | if (value > max) { 196 | minObj = t; 197 | max = value; 198 | } 199 | } 200 | return minObj; 201 | } 202 | 203 | public static int MinValueIndex(this IEnumerable collection, Func heuristic) { 204 | int minIndex = 0; 205 | float min = float.PositiveInfinity; 206 | int index = 0; 207 | foreach (T t in collection) { 208 | float value = heuristic(t); 209 | if (value < min) { 210 | minIndex = index; 211 | min = value; 212 | } 213 | index++; 214 | } 215 | return minIndex; 216 | } 217 | 218 | public static int IndexOf(this IEnumerable source, Func predicate) { 219 | int index = 0; 220 | foreach (var item in source) { 221 | if (predicate(item)) { 222 | return index; 223 | } 224 | index++; 225 | } 226 | return -1; 227 | } 228 | 229 | public static int LastIndexOf(this IEnumerable source, Func predicate) { 230 | int reverseIndex = source.Reverse().IndexOf(predicate); 231 | if (reverseIndex == -1) { 232 | return -1; 233 | } 234 | return source.Count() - 1 - reverseIndex; 235 | } 236 | 237 | public static bool AtLeast(this IEnumerable collection, int count, Func predicate = null) { 238 | if (predicate == null) { 239 | predicate = item => true; 240 | } 241 | 242 | int itemsSeen = 0; 243 | foreach (T item in collection) { 244 | if (predicate(item)) { 245 | itemsSeen++; 246 | if (itemsSeen == count) { 247 | return true; 248 | } 249 | } 250 | } 251 | return false; 252 | } 253 | 254 | public static IList Shuffle(this IList list) { 255 | int n = list.Count; 256 | while (n > 1) { 257 | n--; 258 | int k = UnityEngine.Random.Range(0, n + 1); 259 | T value = list[k]; 260 | list[k] = list[n]; 261 | list[n] = value; 262 | } 263 | return list; 264 | } 265 | 266 | public static void Resize(this List list, int size, T element = default(T)) { 267 | int count = list.Count; 268 | 269 | if (size < count) { 270 | list.RemoveRange(size, count - size); 271 | } else if (size > count) { 272 | if (size > list.Capacity) { 273 | list.Capacity = size; // Optimization. 274 | } 275 | list.AddRange(Enumerable.Repeat(element, size - count)); 276 | } 277 | } 278 | 279 | public static T PickRandom(this IList list) { 280 | return list[UnityEngine.Random.Range(0, list.Count)]; 281 | } 282 | 283 | public static T PickRandom(this IEnumerable list) { 284 | int count = list.Count(); 285 | if (count == 0) { 286 | return default(T); 287 | } 288 | return list.ElementAt(UnityEngine.Random.Range(0, count)); 289 | } 290 | 291 | public static T PickRandomWithWeights(this List list, IList weights) { 292 | float total = weights.Sum(); 293 | float value = UnityEngine.Random.Range(0, total); 294 | 295 | int index = 0; 296 | float sum = 0; 297 | while (index < weights.Count && sum + weights[index] < value) { 298 | sum += weights[index]; 299 | index++; 300 | } 301 | if (index >= weights.Count) { 302 | Debug.LogWarning("Something went wrong..."); 303 | return list[0]; 304 | } 305 | return list[index]; 306 | } 307 | 308 | public static TValue GetValueOrDefault(this IDictionary dictionary, TKey key, TValue defaultValue) { 309 | TValue value; 310 | return dictionary.TryGetValue(key, out value) ? value : defaultValue; 311 | } 312 | 313 | #endregion 314 | } 315 | -------------------------------------------------------------------------------- /Scripts/Extensions/QuickUnityExtensions.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 20b4a83efeb977c41ab11ab8937633d2 3 | timeCreated: 1449937700 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Scripts/Extensions/QuickUnityMathExtensions.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | using System; 4 | 5 | public static class QuickUnityMathExtensions { 6 | 7 | public static Vector3 Round(this Vector3 vector) { 8 | return new Vector3(Mathf.Round(vector.x), Mathf.Round(vector.y), Mathf.Round(vector.z)); 9 | } 10 | 11 | public static Vector3 Clamp(this Vector3 v, float length) { 12 | float m = v.magnitude; 13 | if (m > length) { 14 | return (v / m) * length; 15 | } 16 | return v; 17 | } 18 | 19 | public static Vector3 Snap(this Vector3 v, float cellSize) { 20 | v = (v / cellSize); 21 | return new Vector3((int)v.x, (int)v.y, (int)v.z) * cellSize; 22 | } 23 | 24 | public static Vector3 SetX(this Vector3 vector, float x) { 25 | return new Vector3(x, vector.y, vector.z); 26 | } 27 | 28 | public static Vector3 SetY(this Vector3 vector, float y) { 29 | return new Vector3(vector.x, y, vector.z); 30 | } 31 | 32 | public static Vector3 SetZ(this Vector3 vector, float z) { 33 | return new Vector3(vector.x, vector.y, z); 34 | } 35 | 36 | public static Vector2 SetX(this Vector2 vector, float x) { 37 | return new Vector2(x, vector.y); 38 | } 39 | 40 | public static Vector2 SetY(this Vector2 vector, float y) { 41 | return new Vector2(vector.x, y); 42 | } 43 | 44 | public static Vector3 TransformComponents(this Vector3 vector, Func transformation) { 45 | return new Vector3(transformation(vector.x), transformation(vector.y), transformation(vector.z)); 46 | } 47 | 48 | public static int Mod(this int x, int m) { 49 | return (x % m + m) % m; 50 | } 51 | 52 | public static float Sqr(this float v) { 53 | return v * v; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Scripts/Extensions/QuickUnityMathExtensions.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 981faf9218be6004fad37c404a75b25f 3 | timeCreated: 1454094251 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Scripts/Physics.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 823af75b9840da040b61d5055b2c3020 3 | folderAsset: yes 4 | timeCreated: 1454122070 5 | licenseType: Free 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Scripts/Physics/NavigatorMovement.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | using UnityEngine.AI; 4 | using System; 5 | 6 | /// 7 | /// A physics movement rigidbody that follows the nav mesh. 8 | /// 9 | public class NavigatorMovement : PhysicsMovement { 10 | 11 | private NavMeshNavigator navigator; 12 | 13 | private void Start() { 14 | navigator = gameObject.AddComponent(); 15 | } 16 | 17 | public void SetGoal(Vector3 goal) { 18 | navigator.SetGoal(goal); 19 | } 20 | 21 | public override Vector3 GetDesiredMovementVector() { 22 | return navigator.GetMovementDirection(); 23 | } 24 | } 25 | 26 | /// 27 | /// Uses the nav mesh to calculate the direction the object should move. 28 | /// This direction can be retrieved by calling . 29 | /// 30 | public class NavMeshNavigator : MonoBehaviour { 31 | public float successDistance = 0.7f; 32 | public bool enableMovement = true; 33 | public RandomRange repathTimeout = new RandomRange(0.25f, 1f); 34 | 35 | public bool hasGoal { get { return this.goal.HasValue; } } 36 | public bool hasValidPath { get { return this.currentPath != null && this.currentPath.status != UnityEngine.AI.NavMeshPathStatus.PathInvalid; } } 37 | public Vector3 destinationNode { get { return this.currentPath.corners[pathNode]; } } 38 | public Vector3? goal { get; private set; } 39 | public event Action onGoalReached; 40 | public event Action onBeforeCalculationTimeout; 41 | 42 | protected NavMeshPath currentPath { get; private set; } 43 | protected int pathNode { get; private set; } 44 | private float recalculateTimer = 0.1f; 45 | private NavMeshPath cachedReusablePath; 46 | 47 | private void Awake() { 48 | cachedReusablePath = new NavMeshPath(); 49 | } 50 | 51 | protected virtual void Update() { 52 | if (this.enableMovement) { 53 | this.recalculateTimer -= Time.deltaTime; 54 | if (this.recalculateTimer <= 0) { 55 | this.OnCalculationTimeout(); 56 | this.recalculateTimer = this.GetRecalculationTime(); // Distribute pathfinding more evenly. 57 | } 58 | 59 | if (this.hasGoal) { 60 | if (this.hasValidPath) { 61 | float distToGoal = (this.destinationNode - this.transform.position).SetY(0).magnitude; 62 | if (distToGoal < this.successDistance) { 63 | this.SelectNextNode(); 64 | } 65 | } else if ((this.goal.Value - this.transform.position).SetY(0).magnitude < this.successDistance) { 66 | this.ReachGoal(); 67 | } 68 | } 69 | } 70 | } 71 | 72 | protected virtual void OnCalculationTimeout() { 73 | if (onBeforeCalculationTimeout != null) { 74 | onBeforeCalculationTimeout(); 75 | } 76 | if (this.hasGoal) { 77 | this.RecalculatePath(); 78 | } 79 | } 80 | 81 | /// 82 | /// Returns the direction this object should move to follow the path. 83 | /// 84 | public Vector3 GetMovementDirection() { 85 | if (this.hasValidPath) { 86 | return (this.destinationNode - this.transform.position).SetY(0).normalized; 87 | } 88 | return Vector3.zero; 89 | } 90 | 91 | /// 92 | /// Paths to the given goal on the goal update event. 93 | /// 94 | public void SetGoal(Vector3 goal) { 95 | this.goal = goal; 96 | } 97 | 98 | /// 99 | /// Paths to the given goal and immediately repaths. 100 | /// 101 | public void SetGoalImmediately(Vector3 goal) { 102 | SetGoal(goal); 103 | RecalculatePath(); 104 | } 105 | 106 | public void ClearGoal() { 107 | this.goal = null; 108 | this.currentPath = null; 109 | } 110 | 111 | private void ReachGoal() { 112 | ClearGoal(); 113 | if (onGoalReached != null) { 114 | onGoalReached(); 115 | } 116 | } 117 | 118 | public float GetDistanceToGoal() { 119 | if (!hasValidPath) { 120 | return 0; 121 | } 122 | 123 | float distance = (transform.position - destinationNode).magnitude; 124 | for (int i = pathNode; i < currentPath.corners.Length - 1; i++) { 125 | distance += (currentPath.corners[i + 1] - currentPath.corners[i]).magnitude; 126 | } 127 | return distance; 128 | } 129 | 130 | private void SelectNextNode() { 131 | this.pathNode++; 132 | if (this.pathNode >= this.currentPath.corners.Length) { 133 | this.ReachGoal(); 134 | } 135 | } 136 | 137 | private void RecalculatePath() { 138 | NavMesh.CalculatePath(this.transform.position, goal.Value, int.MaxValue, cachedReusablePath); 139 | this.currentPath = cachedReusablePath; 140 | this.pathNode = Mathf.Min(1, currentPath.corners.Length - 1); 141 | } 142 | 143 | private float GetRecalculationTime() { 144 | return repathTimeout.Random(); 145 | } 146 | 147 | protected virtual void OnDrawGizmos() { 148 | if (this.hasValidPath) { 149 | for (int i = 1; i < this.currentPath.corners.Length; i++) { 150 | Gizmos.DrawLine(this.currentPath.corners[i - 1], this.currentPath.corners[i]); 151 | } 152 | Gizmos.color = Color.red; 153 | Gizmos.DrawWireSphere(this.destinationNode, this.successDistance); 154 | } 155 | 156 | if (this.hasGoal) { 157 | Gizmos.color = Color.green; 158 | Gizmos.DrawWireCube(this.goal.Value, Vector3.one * 1.4f); 159 | } 160 | } 161 | } -------------------------------------------------------------------------------- /Scripts/Physics/NavigatorMovement.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b3fb46f18f2807f47972d07869286721 3 | timeCreated: 1459024086 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Scripts/Physics/PhysicsMovement.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using UnityEngine; 6 | using UnityEngine.Serialization; 7 | 8 | /// 9 | /// This class applies forces to a rigidbody so that its movement mimics that 10 | /// of a character controller. 11 | /// 12 | public abstract class PhysicsMovement : MonoBehaviour { 13 | 14 | const float DESIRED_DELTA_TIME = 1 / 60f; 15 | const float EPSILON = 0.001f; 16 | 17 | [SerializeField] 18 | [FormerlySerializedAs("maxVelocity")] 19 | private float _maxSpeed = 5; 20 | 21 | /// 22 | /// The movement velocity of the character that we try to reach. 23 | /// 24 | public float maxSpeed { get { return _maxSpeed; } protected set { _maxSpeed = value; } } 25 | 26 | /// 27 | /// The strength of the forces applied to the character to reach the maximum velocity. 28 | /// 29 | public float movementForce = 8000; 30 | 31 | /// 32 | /// The strength of the movement force when suspended in midair. 33 | /// 34 | public float midairForce = 8000; 35 | 36 | /// 37 | /// Additional gravity force to be applied to the character while it is grounded. 38 | /// 39 | public float groundStickyForce = 1f; 40 | 41 | /// 42 | /// The length of the ray that is shot beneath the character to determine if it is grounded. 43 | /// 44 | public float midairRaycastDistance = 0.2f; 45 | 46 | /// 47 | /// The offset of the ray from the bottom of the collider that determines if it is grounded. 48 | /// 49 | public float midairRaycastBottomOffset = -0.3f; 50 | 51 | /// 52 | /// The size of the sphere-cast that is shot to determine if it is grounded. 53 | /// 54 | public float midairRayRadius = 0.25f; 55 | 56 | /// 57 | /// The speed at which the object turns to face its direction. 58 | /// 59 | public float turnSpeed = 0; 60 | 61 | /// 62 | /// The mask used for the grounding raycast. 63 | /// 64 | public LayerMask groundRaycastMask = ~(1 << 2); 65 | 66 | /// 67 | /// The position for the force is applied. If left null, it uses the center. 68 | /// 69 | public GameObject forceApplyPosition; 70 | 71 | /// 72 | /// The friction value of the physics material while standing still on the ground. 73 | /// 74 | public float standingFriction = 1f; 75 | 76 | /// 77 | /// The friction value while mid-air or moving itself. 78 | /// 79 | public float movingFriction = 0f; 80 | 81 | /// 82 | /// A cached value of CheckIfGrounded that is updated at the beginning of each update loop. 83 | /// 84 | public bool isGrounded { get; protected set; } 85 | 86 | /// 87 | /// Disables physics material friction entirely. 88 | /// 89 | public bool disableFriction { get; set; } 90 | 91 | /// 92 | /// The direction that points up from the character's head. 93 | /// 94 | public Vector3 upDirection { 95 | get { return -Physics.gravity.normalized; } 96 | } 97 | 98 | /// 99 | /// A reference to the rigidbody for this object. 100 | /// 101 | public Rigidbody body { get; protected set; } 102 | 103 | /// 104 | /// A reference to the collider for this object. 105 | /// 106 | protected Collider myCollider; 107 | 108 | /// 109 | /// The position where the ground raycast starts. 110 | /// 111 | private Vector3 groundRaycastStart { get { return myCollider.bounds.center + Vector3.down * (myCollider.bounds.extents.y + midairRaycastBottomOffset); } } 112 | 113 | 114 | #region UnityEvents 115 | 116 | protected virtual void Awake() { 117 | this.isGrounded = true; 118 | this.body = this.GetComponent(); 119 | this.myCollider = this.GetComponent(); 120 | if (this.forceApplyPosition == null) { 121 | this.forceApplyPosition = this.gameObject; 122 | } 123 | } 124 | 125 | protected virtual void Update() { 126 | // Override me. 127 | } 128 | 129 | protected virtual void FixedUpdate() { 130 | this.UpdateMovement(); 131 | 132 | // Lets the object stay rooted to the ground when standing. 133 | if (this.isGrounded) { 134 | this.body.AddForce(-this.transform.up * this.groundStickyForce); 135 | } 136 | 137 | // Stop moving if we close to stopping. 138 | if (this.body.velocity.SetY(0).sqrMagnitude <= EPSILON * EPSILON) { 139 | this.body.velocity = this.body.velocity.SetX(0).SetZ(0); 140 | } 141 | 142 | // Stop sliding on the ground by applying friction sometimes. 143 | bool noInput = GetDesiredMovementVector() * ResolveMaximumVelocity() == Vector3.zero; 144 | float friction = (isGrounded && noInput && !disableFriction) ? standingFriction : 0f; 145 | myCollider.material.dynamicFriction = friction; 146 | myCollider.material.staticFriction = friction; 147 | } 148 | 149 | protected virtual void OnDrawGizmos() { 150 | if (!myCollider) { 151 | this.myCollider = GetComponent(); 152 | } 153 | Gizmos.color = Color.yellow; 154 | Gizmos.DrawLine(groundRaycastStart, groundRaycastStart + Vector3.down * this.midairRaycastDistance); 155 | Gizmos.color = Color.white; 156 | Gizmos.DrawWireSphere(groundRaycastStart + Vector3.down * this.midairRaycastDistance, this.midairRayRadius); 157 | } 158 | 159 | #endregion 160 | 161 | #region BehaviourLogic 162 | 163 | protected virtual void UpdateMovement() { 164 | this.isGrounded = this.CheckIfGrounded(); 165 | Vector3 direction = this.GetDesiredMovementVector(); 166 | 167 | if (this.ResolveCanMove()) { 168 | Vector3 desiredVelocity = direction * this.ResolveMaximumVelocity(); 169 | Vector3 currentVelocity = this.GetComponent().velocity.SetY(0); // TODO: Generalize to allow movement along different planes. 170 | Vector3 diff = desiredVelocity - currentVelocity; 171 | float force = this.ResolveMovementForce(); 172 | this.body.AddForceAtPosition(diff * force * Time.fixedDeltaTime, this.forceApplyPosition.transform.position); 173 | } 174 | 175 | Vector3 rotateDirection = this.GetDesiredRotateDirection(); 176 | if (this.ResolveCanRotate() && rotateDirection != Vector3.zero) { 177 | float angle = Vector3.Angle(this.GetRotationModel().transform.forward.SetY(0), rotateDirection) 178 | * Mathf.Sign(Vector3.Cross(this.GetRotationModel().transform.forward.SetY(0), rotateDirection).y); 179 | this.GetRotationModel().transform.Rotate(this.upDirection, angle * this.turnSpeed * Time.fixedDeltaTime); 180 | } 181 | } 182 | 183 | public virtual bool CheckIfGrounded() { 184 | RaycastHit hit; 185 | bool result = Physics.SphereCast(new Ray(groundRaycastStart, Vector3.down), this.midairRayRadius, out hit, this.midairRaycastDistance, this.groundRaycastMask.value); 186 | return result; 187 | } 188 | 189 | #endregion 190 | 191 | #region OverrideBehaviour 192 | 193 | public abstract Vector3 GetDesiredMovementVector(); 194 | 195 | protected virtual Vector3 GetDesiredRotateDirection() { 196 | return this.GetDesiredMovementVector(); 197 | } 198 | 199 | protected virtual bool ResolveCanRotate() { 200 | return this.ResolveCanMove() && this.isGrounded; 201 | } 202 | 203 | protected virtual GameObject GetRotationModel() { 204 | return this.gameObject; 205 | } 206 | 207 | protected virtual bool ResolveCanMove() { 208 | return true; 209 | } 210 | 211 | public virtual float ResolveMovementForce() { 212 | float force = this.isGrounded ? this.movementForce : this.midairForce; 213 | return force; 214 | } 215 | 216 | public virtual float ResolveMaximumVelocity() { 217 | return this._maxSpeed; 218 | } 219 | 220 | #endregion 221 | } -------------------------------------------------------------------------------- /Scripts/Physics/PhysicsMovement.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: fe28afd5924ee694b9ddaa4cf6580263 3 | timeCreated: 1454124656 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Scripts/Physics/StabilizeTorque.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | 4 | [RequireComponent(typeof(Rigidbody))] 5 | public class StabilizeTorque : MonoBehaviour { 6 | 7 | public Vector3 upDirection; 8 | public float upTorque; 9 | 10 | public Vector3 forwardDirection; 11 | public float forwardTorque; 12 | 13 | private Rigidbody body; 14 | 15 | void Start() { 16 | this.body = this.GetComponent(); 17 | } 18 | 19 | void FixedUpdate() { 20 | Vector3 torque = Vector3.Cross(this.transform.up, upDirection) * this.upTorque; 21 | this.body.AddTorque(torque); 22 | torque = Vector3.Cross(this.transform.forward, forwardDirection) * this.forwardTorque; 23 | this.body.AddTorque(torque); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Scripts/Physics/StabilizeTorque.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 01ca4bc8524b8ef4abe45a4119fdce04 3 | timeCreated: 1454167722 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Scripts/Physics/StandUpStraight.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | 4 | [RequireComponent(typeof(Rigidbody))] 5 | public class StandUpStraight : MonoBehaviour { 6 | 7 | public enum UpVector { 8 | Up, 9 | Forward, 10 | Right, 11 | } 12 | 13 | public float standUpTorue = 10f; 14 | public UpVector upVector = UpVector.Up; 15 | private Rigidbody body; 16 | 17 | void Start() { 18 | this.body = this.GetComponent(); 19 | } 20 | 21 | void FixedUpdate() { 22 | Vector3 myUp = Vector3.up; 23 | switch (upVector) { 24 | case UpVector.Up: myUp = transform.up; break; 25 | case UpVector.Right: myUp = transform.right; break; 26 | case UpVector.Forward: myUp = transform.forward; break; 27 | } 28 | Vector3 torque = Vector3.Cross(myUp, Vector3.up) * this.standUpTorue; 29 | this.body.AddTorque(torque); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Scripts/Physics/StandUpStraight.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: bfb5008ee5be3d14c99b8036006182f8 3 | timeCreated: 1454167722 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Scripts/PrioritySortingKey.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | /// 7 | /// For use with SortedList. 8 | /// Sorts the items by priority, with using time as a tiebreaker. 9 | /// 10 | public class PrioritySortingKey : IComparable { 11 | private static int uniqueConsecutiveTimeOrderIds = int.MinValue; 12 | 13 | private int priority; 14 | private int timestamp; 15 | 16 | public PrioritySortingKey(int priority) : this(priority, GetUniqueTime()) { } 17 | 18 | public PrioritySortingKey(int priority, int timestamp) { 19 | this.priority = priority; 20 | this.timestamp = timestamp; 21 | } 22 | 23 | public int CompareTo(object obj) { 24 | int compare = -1 * this.priority.CompareTo(((PrioritySortingKey)obj).priority); 25 | if (compare == 0) { 26 | return -1 * this.timestamp.CompareTo(((PrioritySortingKey)obj).timestamp); 27 | } 28 | return compare; 29 | } 30 | 31 | public override string ToString() { 32 | return "(Priority: " + priority + ", Time: " + timestamp + ")"; 33 | } 34 | 35 | public static int GetUniqueTime() { 36 | uniqueConsecutiveTimeOrderIds++; 37 | return uniqueConsecutiveTimeOrderIds; 38 | } 39 | } 40 | 41 | /// 42 | /// For use with SortedList where the list represents an ordered stack that manages some global state 43 | /// that users have to register themselves with to modify. 44 | /// 45 | public class StackResourceSortingKey : PrioritySortingKey { 46 | 47 | private Action releaseResource; 48 | 49 | public StackResourceSortingKey(int priority, Action releaseResource) : base(priority) { 50 | Asserts.NotNull(releaseResource); 51 | this.releaseResource = releaseResource; 52 | } 53 | 54 | public void ReleaseResource() { 55 | releaseResource(this); 56 | } 57 | 58 | public static void Release(StackResourceSortingKey key) { 59 | if (key != null) { 60 | key.ReleaseResource(); 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /Scripts/PrioritySortingKey.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 66b13f1b112399c44b77a5b716d6f615 3 | timeCreated: 1513021834 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Scripts/RandomRange.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | using System; 4 | 5 | [Serializable] 6 | public struct RandomRange { 7 | public float min; 8 | public float max; 9 | 10 | public RandomRange(float min, float max) { 11 | this.max = max; 12 | this.min = min; 13 | } 14 | 15 | public float Random() { 16 | return UnityEngine.Random.Range(this.min, this.max); 17 | } 18 | } -------------------------------------------------------------------------------- /Scripts/RandomRange.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a88c7c2725974e44fabdf1da1196dde2 3 | timeCreated: 1459027254 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Scripts/Services.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 11e463743ec1df64fbe6b5537959d5c5 3 | folderAsset: yes 4 | timeCreated: 1511283283 5 | licenseType: Free 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Scripts/Services/ServiceLocator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using UnityEngine; 5 | using UnityEngine.SceneManagement; 6 | 7 | /// 8 | /// Provides controlled access to services that exist locally within a scene. Concrete classes should 9 | /// provide getters for various services and use the Locate() method to find and cache the service 10 | /// reference. The cache is cleared when the active scene changes to point to the services in the new 11 | /// scene. 12 | /// 13 | /// The concrete type that is used for singleton access. 14 | public abstract class ServiceLocator : Singleton where TMyType : MonoBehaviour { 15 | 16 | private static bool applicationIsQuitting = false; 17 | 18 | protected Dictionary cachedSceneServices = new Dictionary(); 19 | 20 | 21 | protected virtual void Awake() { 22 | SceneManager.activeSceneChanged += OnActiveSceneChange; 23 | } 24 | 25 | protected virtual void OnApplicationQuit() { 26 | applicationIsQuitting = true; 27 | } 28 | 29 | private void OnActiveSceneChange(Scene oldScene, Scene newScene) { 30 | Log("Clearing the cached scene services."); 31 | cachedSceneServices.Clear(); 32 | } 33 | 34 | /// 35 | /// Looks for a service that exists as a MonoBehaviour in the current scene. 36 | /// 37 | protected TService LocateServiceInActiveScene() where TService : ServiceMonoBehaviour { 38 | var service = LocateServiceInActiveSceneWithoutErrors(); 39 | if (service == null) { 40 | LogError(typeof(TService), "Service is not present in this scene!"); 41 | } 42 | return service; 43 | } 44 | 45 | public TService LocateServiceInActiveSceneWithoutErrors() where TService : ServiceMonoBehaviour { 46 | // Try and find a cached version of the service. 47 | Type serviceType = typeof(TService); 48 | if (cachedSceneServices.ContainsKey(serviceType)) { 49 | var cached = (TService)cachedSceneServices[serviceType]; 50 | if (cached != null) { 51 | return cached; 52 | } else { 53 | Log(serviceType, "The cached service was null! Hopefully it was destroyed in a scene transition?"); 54 | } 55 | } 56 | 57 | TService foundService = SceneManager.GetActiveScene().FindComponentsOfTypeInScene().FirstOrDefault(); 58 | if (foundService == null) { 59 | return null; 60 | } 61 | 62 | Log(serviceType, "Located service and caching it."); 63 | cachedSceneServices[serviceType] = foundService; 64 | return foundService; 65 | } 66 | 67 | protected TService LocateOrCreateServiceInActiveScene() where TService : ServiceMonoBehaviour { 68 | TService service = LocateServiceInActiveSceneWithoutErrors(); 69 | if (service != null) { 70 | return service; 71 | } 72 | 73 | if (applicationIsQuitting) { 74 | return null; 75 | } 76 | 77 | Type serviceType = typeof(TService); 78 | Log(serviceType, "Created a new service in the scene."); 79 | GameObject obj = new GameObject("(Created Service) " + serviceType.Name); 80 | service = obj.AddComponent(); 81 | cachedSceneServices[serviceType] = service; 82 | return service; 83 | } 84 | 85 | private void Log(string message) { 86 | Log(null, message); 87 | } 88 | 89 | private void Log(Type service, string message) { 90 | string serviceText = service != null ? service.Name + ": " : ""; 91 | Debug.Log("[" + GetType().Name + "] " + serviceText + message); 92 | } 93 | 94 | private void LogError(Type service, string warning) { 95 | string serviceText = service != null ? service.Name + ": " : ""; 96 | Debug.LogError("[" + GetType().Name + "] " + serviceText + warning); 97 | } 98 | } -------------------------------------------------------------------------------- /Scripts/Services/ServiceLocator.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 30a640e26ef926540b7ac3d50ea7a280 3 | timeCreated: 1511209578 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Scripts/Services/ServiceMonoBehaviour.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using UnityEngine; 5 | 6 | /// 7 | /// Classes that inherit from this can be found globally using the ServiceLocator class. 8 | /// This behaviour also ensures that there is only one service per scene. 9 | /// 10 | public abstract class ServiceMonoBehaviour : MonoBehaviour { 11 | 12 | protected virtual void OnEnable() { 13 | var sceneObjects = gameObject.scene.FindComponentsOfTypeInScene(GetType()); 14 | 15 | if (sceneObjects.AtLeast(2)) { 16 | Debug.LogError("[ServiceMonoBehaviour] " + GetType().Name + ": Multiple instances of this service in this scene."); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /Scripts/Services/ServiceMonoBehaviour.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1c928a493f60aad4f99f2918d51a1c0f 3 | timeCreated: 1511215392 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Scripts/Singleton.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a20c0020464447d47beb82947a4a3ec0 3 | folderAsset: yes 4 | timeCreated: 1450129737 5 | licenseType: Free 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Scripts/Singleton/ResourceSingletonAttribute.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | using System; 4 | 5 | /// 6 | /// Add this attribute to a Singleton class and it will load the Singleton from the Resources folder. 7 | /// 8 | [AttributeUsage(AttributeTargets.Class)] 9 | public class ResourceSingletonAttribute : Attribute { 10 | public readonly string resourceFilePath; 11 | 12 | public ResourceSingletonAttribute(string path) { 13 | this.resourceFilePath = path; 14 | } 15 | } -------------------------------------------------------------------------------- /Scripts/Singleton/ResourceSingletonAttribute.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0a845c01d07e6b64f85246708f22e146 3 | timeCreated: 1449956169 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Scripts/Singleton/SceneSingleton.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | 4 | /// 5 | /// Represents a single-instance object that is placed in scenes. 6 | /// This will not create the singleton, it just provides quick access to the Singleton. 7 | /// 8 | /// I think this class is still kind of buggy in some situations, I'll fix it later. 9 | /// 10 | [Obsolete("SceneSingletons aren't really singletons. Use the ServiceLocator instead.")] 11 | public class SceneSingleton : MonoBehaviour where T : MonoBehaviour { 12 | private static T _instance; 13 | 14 | public static T instance { 15 | get { 16 | // HACK: Search for a new one if the current is disabled; this is needed for my current project. 17 | if (_instance != null && !_instance.isActiveAndEnabled) { 18 | _instance = null; 19 | Debug.Log("[SceneSingleton] The current instance of " + typeof(T) + " is inactive. Searching for an active one..."); 20 | } 21 | 22 | if (_instance == null) { 23 | _instance = (T)GameObject.FindObjectOfType(typeof(T)); 24 | 25 | if (GameObject.FindObjectsOfType(typeof(T)).Length > 1) { 26 | Debug.LogError("[SceneSingleton] Something went really wrong - there should never be more than 1 singleton! Reopening the scene might fix it."); 27 | return _instance; 28 | } 29 | 30 | if (_instance == null) { 31 | Debug.LogWarning("[SceneSingleton] An instance of " + typeof(T) + " is needed in the scene, but none exists!"); 32 | } else { 33 | Debug.Log("[SceneSingleton] Using instance already created: " + _instance.gameObject.name); 34 | } 35 | } 36 | 37 | return _instance; 38 | } 39 | } 40 | 41 | protected virtual void Awake() { 42 | // Placeholder. 43 | } 44 | 45 | protected virtual void OnDestroy() { 46 | _instance = null; 47 | } 48 | } -------------------------------------------------------------------------------- /Scripts/Singleton/SceneSingleton.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4026e5559279e3b448c8a2db1d0a1b18 3 | timeCreated: 1449948596 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Scripts/Singleton/Singleton.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | 4 | /// 5 | /// Global Singletons are created when needed. 6 | /// They are never initially present in a scene, and are lazily spawned. 7 | /// They belong to DontDestroyOnLoad and persist forever. 8 | /// 9 | public class Singleton : MonoBehaviour where T : MonoBehaviour { 10 | private static T _instance; 11 | 12 | private static object _lock = new object(); 13 | 14 | public static T instance { 15 | get { 16 | if (applicationIsQuitting) { 17 | Debug.LogWarning("[Singleton] Instance '" + typeof(T) + 18 | "' already destroyed on application quit." + 19 | " Won't create again - returning null."); 20 | return null; 21 | } 22 | 23 | lock (_lock) { 24 | if (_instance == null) { 25 | _instance = (T)FindObjectOfType(typeof(T)); 26 | 27 | if (FindObjectsOfType(typeof(T)).Length > 1) { 28 | Debug.LogError("[Singleton] Something went really wrong " + 29 | " - there should never be more than 1 singleton!" + 30 | " Reopening the scene might fix it."); 31 | return _instance; 32 | } 33 | 34 | if (_instance == null) { 35 | // Check if the ResourceSingletonAttribute is present... 36 | ResourceSingletonAttribute resource = null; 37 | object[] attributes = typeof(T).GetCustomAttributes(false); 38 | foreach (System.Attribute attribute in attributes) { 39 | resource = attribute as ResourceSingletonAttribute; 40 | if (resource != null) { 41 | break; 42 | } 43 | } 44 | 45 | if (resource != null) { 46 | // Load a prefab that contains this singleton. 47 | GameObject prefab = Resources.Load(resource.resourceFilePath); 48 | if (prefab == null) { 49 | Debug.LogError("The Resource Singleton " + typeof(T) + " was not found in any resources folder!"); 50 | return null; 51 | } 52 | GameObject singleton = GameObject.Instantiate(prefab); 53 | singleton.name = "(Resource Global Singleton) " + singleton.name; 54 | _instance = singleton.GetComponent(); 55 | if (_instance == null) { 56 | Debug.LogError("A prefab was loaded for the singleton, but the component was not on it!"); 57 | } else { 58 | DontDestroyOnLoad(singleton); 59 | Debug.Log("[Singleton] An instance of " + typeof(T) + 60 | " is needed in the scene, so '" + singleton + 61 | "' was loaded as a prefab with DontDestroyOnLoad."); 62 | } 63 | } else { 64 | // Create a singleton component in the world. 65 | GameObject singleton = new GameObject(); 66 | _instance = singleton.AddComponent(); 67 | singleton.name = "(Global Singleton) " + typeof(T).ToString(); 68 | DontDestroyOnLoad(singleton); 69 | Debug.Log("[Singleton] An instance of " + typeof(T) + 70 | " is needed in the scene, so '" + singleton + 71 | "' was created with DontDestroyOnLoad."); 72 | } 73 | } else { 74 | Debug.Log("[Singleton] Using instance already created: " + 75 | _instance.gameObject.name); 76 | } 77 | } 78 | 79 | return _instance; 80 | } 81 | } 82 | } 83 | 84 | private static bool applicationIsQuitting = false; 85 | /// 86 | /// When Unity quits, it destroys objects in a random order. 87 | /// In principle, a Singleton is only destroyed when application quits. 88 | /// If any script calls Instance after it have been destroyed, 89 | /// it will create a buggy ghost object that will stay on the Editor scene 90 | /// even after stopping playing the Application. Really bad! 91 | /// So, this was made to be sure we're not creating that buggy ghost object. 92 | /// 93 | public void OnDestroy() { 94 | applicationIsQuitting = true; 95 | } 96 | } -------------------------------------------------------------------------------- /Scripts/Singleton/Singleton.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 335cc2883a276244da368f7d3fbac766 3 | timeCreated: 1449956159 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Scripts/TimeScaleController.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | 5 | public class TimeScaleController : Singleton { 6 | 7 | private const float TIME_SCALE_CHANGE_SPEED = 1000; 8 | 9 | public float baseTimeScale { get; set; } 10 | 11 | private float slowMoScale; 12 | private float slowMoDuration; 13 | 14 | private float timeScaleChangeSpeed = TIME_SCALE_CHANGE_SPEED; 15 | private float initalFixedDeltaTime; 16 | 17 | private SortedList timeScaleAdjustments = new SortedList(); 18 | private IList timeScaleListCache; 19 | 20 | private void Awake() { 21 | initalFixedDeltaTime = Time.fixedDeltaTime; 22 | timeScaleListCache = timeScaleAdjustments.Values; 23 | baseTimeScale = 1; 24 | } 25 | 26 | private void Update() { 27 | float realDeltaTime = Time.unscaledDeltaTime; 28 | 29 | if (slowMoDuration > 0) { 30 | // Apply the slow mo. 31 | slowMoDuration = Mathf.MoveTowards(slowMoDuration, 0, realDeltaTime); 32 | Time.timeScale = Mathf.MoveTowards(Time.timeScale, slowMoScale, timeScaleChangeSpeed * realDeltaTime); 33 | Time.fixedDeltaTime = Mathf.LerpUnclamped(0, initalFixedDeltaTime, Time.timeScale); 34 | } else { 35 | // Return to normal speed. 36 | float desiredTimeScale = baseTimeScale; 37 | if (timeScaleAdjustments.Count > 0) { 38 | desiredTimeScale *= timeScaleListCache[0]; 39 | } 40 | 41 | if (Time.timeScale != desiredTimeScale) { 42 | Time.timeScale = Mathf.MoveTowards(Time.timeScale, desiredTimeScale, timeScaleChangeSpeed * realDeltaTime); 43 | if (Time.timeScale == desiredTimeScale) { 44 | // Once back to speed, return the the default change speed. 45 | timeScaleChangeSpeed = TIME_SCALE_CHANGE_SPEED; 46 | } 47 | } 48 | 49 | // Update the fixed time to match the timescale. 50 | float fixedDeltaTime = Mathf.Min(initalFixedDeltaTime, Mathf.LerpUnclamped(0, initalFixedDeltaTime, Time.timeScale)); 51 | if (Time.fixedDeltaTime != fixedDeltaTime) { 52 | Time.fixedDeltaTime = fixedDeltaTime; 53 | } 54 | } 55 | } 56 | 57 | /// 58 | /// Adjusts the timescale. The returned key must be used to unregister the adjustment and return it to normal. 59 | /// 60 | public StackResourceSortingKey AdjustTimeScale(float timeScale) { 61 | var resourceKey = new StackResourceSortingKey(0, key => timeScaleAdjustments.Remove(key)); 62 | timeScaleAdjustments.Add(resourceKey, timeScale); 63 | return resourceKey; 64 | } 65 | 66 | /// 67 | /// Slows down time for a some realtime seconds. 68 | /// 69 | public void SlowMo(float slowTimeScale, float duration, float? timeChangeSpeed = null) { 70 | slowMoScale = slowTimeScale; 71 | slowMoDuration = duration; 72 | if (timeChangeSpeed.HasValue) { 73 | timeScaleChangeSpeed = timeChangeSpeed.Value; 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /Scripts/TimeScaleController.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c594e214e99e17e45bd975f92f875e49 3 | timeCreated: 1449956137 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Scripts/Timer.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 04bbc6a22624ad043bccfa8d1454e95a 3 | folderAsset: yes 4 | timeCreated: 1450128529 5 | licenseType: Free 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Scripts/Timer/Timer.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System; 3 | using System.Linq; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | 7 | public class Timer { 8 | 9 | public bool isLooped { get; set; } 10 | public bool isPaused { get; set; } 11 | 12 | public float duration { get; private set; } 13 | public bool isCancelled { get; private set; } 14 | public bool isCompleted { get; private set; } 15 | 16 | private float accumulatedTime; 17 | 18 | private Action onComplete; 19 | private bool usesUnscaledTime; 20 | private bool hasAutoDestroyOwner; 21 | private UnityEngine.Object autoDestroyOwner; 22 | 23 | private Timer(float duration, Action onComplete, bool isLooped, bool useUnscaledTime) { 24 | this.duration = duration; 25 | this.onComplete = onComplete; 26 | 27 | this.isLooped = isLooped; 28 | this.isCancelled = false; 29 | this.usesUnscaledTime = useUnscaledTime; 30 | 31 | this.accumulatedTime = 0; 32 | } 33 | 34 | private float GetDeltaTime() { 35 | if (this.usesUnscaledTime) { 36 | return Time.unscaledDeltaTime; 37 | } else { 38 | return Time.deltaTime; 39 | } 40 | } 41 | 42 | public void SetAutoDestroyOwner(UnityEngine.Object owner) { 43 | this.autoDestroyOwner = owner; 44 | this.hasAutoDestroyOwner = owner != null; 45 | } 46 | 47 | public bool IsDone() { 48 | return this.isCompleted || this.isCancelled || (this.hasAutoDestroyOwner && this.autoDestroyOwner == null); 49 | } 50 | 51 | public void Cancel() { 52 | this.isCancelled = true; 53 | } 54 | 55 | public void Update() { 56 | if (this.IsDone() || this.isPaused) { 57 | return; 58 | } 59 | 60 | this.accumulatedTime += this.GetDeltaTime(); 61 | if (this.accumulatedTime >= this.duration) { 62 | this.onComplete(); 63 | 64 | if (this.isLooped) { 65 | this.accumulatedTime = 0; 66 | } else { 67 | this.isCompleted = true; 68 | } 69 | } 70 | } 71 | 72 | public float GetTimeRemaining() { 73 | return this.IsDone() ? 0 : this.duration - this.accumulatedTime; 74 | } 75 | 76 | public float GetTime() { 77 | return this.accumulatedTime; 78 | } 79 | 80 | public float GetPercentageComplete() { 81 | if (this.isCompleted) { 82 | return 1; 83 | } 84 | return this.accumulatedTime / this.duration; 85 | } 86 | 87 | public static Timer Register(float duration, Action onComplete, bool isLooped = false, bool useUnscaledTime = false, UnityEngine.Object autoCancelObj = null) { 88 | Timer timer = new Timer(duration, onComplete, isLooped, useUnscaledTime); 89 | timer.SetAutoDestroyOwner(autoCancelObj); 90 | if (TimerServiceLocator.instance != null && TimerServiceLocator.instance.timerManager != null) { 91 | TimerServiceLocator.instance.timerManager.AddTimer(timer); 92 | } 93 | return timer; 94 | } 95 | 96 | public static void Cancel(Timer timer) { 97 | if (timer != null) { 98 | timer.Cancel(); 99 | } 100 | } 101 | 102 | public static void FinishImmediately(Timer timer) { 103 | if (timer == null) { 104 | return; 105 | } 106 | if (timer.IsDone()) { 107 | return; 108 | } 109 | timer.onComplete(); 110 | timer.isCompleted = true; 111 | } 112 | } -------------------------------------------------------------------------------- /Scripts/Timer/Timer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b6dcdabb99651a14391958bcead3d55d 3 | timeCreated: 1449941199 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Scripts/Timer/TimerExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | 4 | public static class TimerExtensions { 5 | public static Timer RegisterTimer(this MonoBehaviour owner, float duration, Action onComplete, bool isLooped = false, bool useUnscaledTime = false) { 6 | return Timer.Register(duration, onComplete, isLooped, useUnscaledTime, owner); 7 | } 8 | } -------------------------------------------------------------------------------- /Scripts/Timer/TimerExtensions.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c6076695697fa5d4da6f6a6bd7c97393 3 | timeCreated: 1449941199 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Scripts/Timer/TimerManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | using System.Collections; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | 7 | /// 8 | /// Provide scene-local access to the TimerManager using a service locator. 9 | /// 10 | public class TimerServiceLocator : ServiceLocator { 11 | 12 | public TimerManager timerManager { get { return LocateOrCreateServiceInActiveScene(); } } 13 | 14 | public class TimerManager : ServiceMonoBehaviour { 15 | 16 | private List timers = new List(); 17 | private List timersToAddBuffer = new List(); 18 | 19 | private void Update() { 20 | UpdateAllRegisteredTimers(); 21 | } 22 | 23 | private void OnDestroy() { 24 | CancelAllRegisteredTimers(); 25 | } 26 | 27 | public void AddTimer(Timer timer) { 28 | timersToAddBuffer.Add(timer); 29 | } 30 | 31 | private void UpdateAllRegisteredTimers() { 32 | timers.AddRange(timersToAddBuffer); 33 | if (timersToAddBuffer.Count > 0) { 34 | timersToAddBuffer.Clear(); 35 | } 36 | 37 | bool anyDone = false; 38 | foreach (Timer timer in timers) { 39 | timer.Update(); 40 | anyDone |= timer.IsDone(); 41 | } 42 | 43 | if (anyDone) { 44 | timers.RemoveAll(t => t.IsDone()); 45 | } 46 | } 47 | 48 | private void CancelAllRegisteredTimers() { 49 | foreach (Timer timer in timers) { 50 | timer.Cancel(); 51 | } 52 | 53 | timers.Clear(); 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /Scripts/Timer/TimerManager.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: aed8fc0eb4ac22847b0c4cee99254275 3 | timeCreated: 1449941199 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Scripts/TrackerCameraMovement.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System; 6 | 7 | /// 8 | /// Moves the camera to track multiple targets. 9 | /// 10 | public class TrackerCameraMovement : MonoBehaviour { 11 | 12 | protected static List EMPTY_LIST = new List(); 13 | 14 | // Inspector Fields 15 | public float minDistanceBetweenPlayers = 3f; 16 | public float maxDistanceBetweenPlayers = 20f; 17 | 18 | public float minCameraSize = 5; 19 | public float maxCameraSize = 10; 20 | 21 | public float minDistanceFromWorld = 20; 22 | public float maxDistanceFromWorld = 50; 23 | 24 | public float cameraDriftLerp = 1f; 25 | public float acceleration = 12; 26 | 27 | public Vector3 cameraOffset; 28 | public List additionalCameraTargets; 29 | 30 | // Public Properties 31 | public Func limitCameraMovement; 32 | public float speed { get; set; } 33 | 34 | // Private / Protected Properties 35 | protected Camera myCamera; 36 | protected Transform lockedTransform; 37 | protected bool isLocked; 38 | 39 | 40 | protected virtual void Awake() { 41 | this.myCamera = this.GetComponent(); 42 | } 43 | 44 | protected virtual void FixedUpdate() { 45 | float desiredSize = this.myCamera.orthographicSize; 46 | Vector3 desired = this.transform.position; 47 | 48 | // Find the desired position for the camera... 49 | if (this.GetGameObjectsToTrack().Count() > 0 || this.additionalCameraTargets.Count > 0) { 50 | desired = this.CalculatePlayerFocusCameraSettings(out desiredSize); 51 | } 52 | if (this.limitCameraMovement != null) { 53 | desired = this.limitCameraMovement(desired); 54 | } 55 | if (this.isLocked) { 56 | desired = this.lockedTransform.position; 57 | } 58 | 59 | // Clamp the desired movement by the speed and handle acceleration. 60 | Vector3 desiredMovement = (desired - this.transform.position) * cameraDriftLerp; 61 | if (desiredMovement.sqrMagnitude > this.speed.Sqr()) { 62 | float length = desiredMovement.magnitude; 63 | speed = Mathf.MoveTowards(this.speed, length, this.acceleration * Time.fixedDeltaTime); 64 | desiredMovement = desiredMovement * speed / length; 65 | } else { 66 | speed = desiredMovement.magnitude; 67 | } 68 | 69 | // Change the camera movement. 70 | this.transform.position += desiredMovement * Time.fixedDeltaTime; 71 | this.myCamera.orthographicSize += (desiredSize - this.myCamera.orthographicSize) * this.cameraDriftLerp * Time.fixedDeltaTime; 72 | } 73 | 74 | public virtual IEnumerable GetGameObjectsToTrack() { 75 | return EMPTY_LIST; 76 | } 77 | 78 | public Vector3 CalculateDesiredPosition() { 79 | float _orthoSize; 80 | return CalculatePlayerFocusCameraSettings(out _orthoSize); 81 | } 82 | 83 | public virtual void LockTransform(Transform lockedTransform, bool snap = false) { 84 | this.isLocked = true; 85 | this.lockedTransform = lockedTransform; 86 | if (snap) { 87 | this.transform.position = lockedTransform.transform.position; 88 | this.transform.rotation = lockedTransform.transform.rotation; 89 | } 90 | } 91 | 92 | public virtual void UnlockTransform() { 93 | this.isLocked = false; 94 | } 95 | 96 | private Vector3 CalculatePlayerFocusCameraSettings(out float orthoSize) { 97 | IEnumerable ps = this.GetGameObjectsToTrack().Concat(this.additionalCameraTargets); 98 | if (ps.Count() == 0) { 99 | orthoSize = this.GetComponent().orthographicSize; 100 | return this.transform.position; 101 | } 102 | 103 | Vector3 min = new Vector3(ps.Min(p => p.transform.position.x), 104 | ps.Min(p => p.transform.position.y), 105 | ps.Min(p => p.transform.position.z)); 106 | Vector3 max = new Vector3(ps.Max(p => p.transform.position.x), 107 | ps.Max(p => p.transform.position.y), 108 | ps.Max(p => p.transform.position.z)); 109 | 110 | float dist = (min - max).magnitude; 111 | float percentDistance = Mathf.InverseLerp(minDistanceBetweenPlayers, maxDistanceBetweenPlayers, dist); 112 | 113 | float useDist = Mathf.Lerp(minDistanceFromWorld, maxDistanceFromWorld, percentDistance); 114 | orthoSize = Mathf.Lerp(minCameraSize, maxCameraSize, percentDistance); 115 | 116 | Vector3 center = (min + max) / 2; 117 | return center - this.transform.forward * useDist + this.cameraOffset; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /Scripts/TrackerCameraMovement.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: dec6a06b7981c4d4eb332a8cdc76c459 3 | timeCreated: 1454168073 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Scripts/TriggerCollidersTracker.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | 5 | public abstract class TriggerCollidersTracker : MonoBehaviour { 6 | 7 | public int objectsInsideCount { get { return this.collidersInside.Count; } } 8 | protected List collidersInside; 9 | 10 | protected virtual void Start() { 11 | this.collidersInside = new List(); 12 | } 13 | 14 | protected virtual void Update() { 15 | for (int i = collidersInside.Count - 1; i >= 0; i--) { 16 | var collider = collidersInside[i]; 17 | if (collider == null || !collider.enabled || !collider.gameObject.activeInHierarchy) { 18 | this.MarkColliderRemoved(collider); 19 | } 20 | } 21 | } 22 | 23 | private void OnTriggerEnter(Collider collider) { 24 | if (collider.GetComponent() == null) { 25 | return; 26 | } 27 | if (!this.collidersInside.Contains(collider)) { 28 | this.collidersInside.Add(collider); 29 | if (this.collidersInside.Count == 1) { 30 | this.OnAtLeastOneCollider(); 31 | } 32 | } 33 | this.OnColliderEnter(collider); 34 | } 35 | 36 | private void OnTriggerExit(Collider collider) { 37 | this.MarkColliderRemoved(collider); 38 | } 39 | 40 | private void MarkColliderRemoved(Collider collider) { 41 | if (this.collidersInside.Contains(collider)) { 42 | this.collidersInside.Remove(collider); 43 | this.OnColliderExit(collider); 44 | if (this.collidersInside.Count == 0) { 45 | this.OnNoColliders(); 46 | } 47 | } 48 | } 49 | 50 | protected virtual void OnAtLeastOneCollider() { } 51 | 52 | protected virtual void OnNoColliders() { } 53 | 54 | protected virtual void OnColliderEnter(Collider collider) { } 55 | 56 | protected virtual void OnColliderExit(Collider collider) { } 57 | } 58 | 59 | -------------------------------------------------------------------------------- /Scripts/TriggerCollidersTracker.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 38cf00cafcec11b4781abac75c60f93e 3 | timeCreated: 1460305845 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Scripts/Vector2Int.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System; 3 | using System.Text; 4 | 5 | public struct Vector2Int { 6 | 7 | public static readonly Vector2Int[] MAIN_DIRECTIONS = new Vector2Int[] { new Vector2Int(1, 0), new Vector2Int(-1, 0), new Vector2Int(0, 1), new Vector2Int(0, -1) }; 8 | public static readonly Vector2Int[] ALL_DIRECTIONS = new Vector2Int[] { new Vector2Int(1, 0), new Vector2Int(-1, 0), new Vector2Int(0, 1), new Vector2Int(0, -1), new Vector2Int(1, 1), new Vector2Int(-1, 1), new Vector2Int(1, -1), new Vector2Int(-1, -1) }; 9 | public static readonly Vector2Int zero = new Vector2Int(0, 0); 10 | public static readonly Vector2Int unitX = new Vector2Int(1, 0); 11 | public static readonly Vector2Int unitY = new Vector2Int(0, 1); 12 | 13 | public int x; 14 | public int y; 15 | 16 | public Vector2Int(int x, int y) { 17 | this.x = x; 18 | this.y = y; 19 | } 20 | 21 | public bool IsNeighbors(Vector2Int other) { 22 | return Math.Abs(other.x - this.x) <= 1 && Math.Abs(other.y - this.y) <= 1; 23 | } 24 | 25 | public Vector3 ToV3() { 26 | return new Vector3(x, y, 0); 27 | } 28 | 29 | public Vector2 ToV2() { 30 | return new Vector2(x, y); 31 | } 32 | 33 | public override bool Equals(object obj) { 34 | return (obj is Vector2Int) ? this == ((Vector2Int)obj) : false; 35 | } 36 | 37 | public bool Equals(Vector2Int other) { 38 | return this == other; 39 | } 40 | 41 | public override int GetHashCode() { 42 | return this.x + this.y; 43 | } 44 | 45 | public Vector2Int TurnClockwise() { 46 | return new Vector2Int(-y, x); 47 | } 48 | 49 | public override string ToString() { 50 | StringBuilder sb = new StringBuilder(24); 51 | sb.Append("{X:"); 52 | sb.Append(this.x); 53 | sb.Append(" Y:"); 54 | sb.Append(this.y); 55 | sb.Append("}"); 56 | return sb.ToString(); 57 | } 58 | 59 | #region operators 60 | 61 | public static Vector2Int operator -(Vector2Int value) { 62 | value.x = -value.x; 63 | value.y = -value.y; 64 | return value; 65 | } 66 | 67 | 68 | public static bool operator ==(Vector2Int value1, Vector2Int value2) { 69 | return value1.x == value2.x && value1.y == value2.y; 70 | } 71 | 72 | 73 | public static bool operator !=(Vector2Int value1, Vector2Int value2) { 74 | return value1.x != value2.x || value1.y != value2.y; 75 | } 76 | 77 | 78 | public static Vector2Int operator +(Vector2Int value1, Vector2Int value2) { 79 | value1.x += value2.x; 80 | value1.y += value2.y; 81 | return value1; 82 | } 83 | 84 | 85 | public static Vector2Int operator -(Vector2Int value1, Vector2Int value2) { 86 | value1.x -= value2.x; 87 | value1.y -= value2.y; 88 | return value1; 89 | } 90 | 91 | 92 | public static Vector2Int operator *(Vector2Int value1, Vector2Int value2) { 93 | value1.x *= value2.x; 94 | value1.y *= value2.y; 95 | return value1; 96 | } 97 | 98 | 99 | public static Vector2Int operator *(Vector2Int value, int scaleFactor) { 100 | value.x *= scaleFactor; 101 | value.y *= scaleFactor; 102 | return value; 103 | } 104 | 105 | 106 | public static Vector2Int operator *(int scaleFactor, Vector2Int value) { 107 | value.x *= scaleFactor; 108 | value.y *= scaleFactor; 109 | return value; 110 | } 111 | 112 | 113 | public static Vector2Int operator /(Vector2Int value1, Vector2Int value2) { 114 | value1.x /= value2.x; 115 | value1.y /= value2.y; 116 | return value1; 117 | } 118 | 119 | 120 | public static Vector2Int operator /(Vector2Int value1, int divider) { 121 | float factor = 1 / divider; 122 | value1.x = (int)(value1.x * factor); 123 | value1.y = (int)(value1.y * factor); 124 | return value1; 125 | } 126 | 127 | #endregion Operators 128 | } 129 | -------------------------------------------------------------------------------- /Scripts/Vector2Int.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f8a91afe2295fec4cad0369475ae0c2f 3 | timeCreated: 1454094884 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Scripts/WireSphereGizmo.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | 4 | public class WireSphereGizmo : MonoBehaviour { 5 | 6 | public Color color = Color.red; 7 | public float radius = 1f; 8 | 9 | private void OnDrawGizmos() { 10 | Gizmos.color = this.color; 11 | Gizmos.DrawWireSphere(this.transform.position, this.radius); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Scripts/WireSphereGizmo.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 554b83a4b61965e498cdbf9c69496db4 3 | timeCreated: 1454175674 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | --------------------------------------------------------------------------------