diff --git a/Assets/CHANGELOG.md b/Assets/CHANGELOG.md index 7820908..167a9f3 100644 --- a/Assets/CHANGELOG.md +++ b/Assets/CHANGELOG.md @@ -4,6 +4,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.0.0] - 2020-10-13 + +### Changed +- Made game event editors more user-friendly. +- Naming in asset menus for game events. +- Listeners are now exposed as an unmodifiable `ICollection`. + ## [1.0.0-rc.1] - 2020-10-11 ## [0.7.0] - 2020-10-11 diff --git a/Assets/Documentation~/README.md b/Assets/Documentation~/README.md index b820f62..9eba67f 100644 --- a/Assets/Documentation~/README.md +++ b/Assets/Documentation~/README.md @@ -17,7 +17,7 @@ Game events are scriptable objects (_Right Click -> Create -> Game Events -> ... Available game events: - `GameEvent` - simple event which doesn't accept any arguments. - `BoolGameEvent` - event with a `bool` argument. -- `IntGameEvent` - event with a `int` argument. +- `IntGameEvent` - event with an `int` argument. - `FloatGameEvent` - event with a `float` argument. - `StringGameEvent` - event with a `string` argument. - `Vector2GameEvent` - event with a `Vector2` argument. @@ -35,20 +35,20 @@ Mutable objects are used for storing and editing data on `ScriptableObject` asse Available mutable objects: - `MutableBool` - encapsulates a `bool` value. -- `MutableInt` - encapsulates a `int` value. +- `MutableInt` - encapsulates an `int` value. - `MutableFloat` - encapsulates a `float` value. - `MutableString` - encapsulates a `string` value. - `MutableVector2` - encapsulates a `Vector2` value. - `MutableVector3` - encapsulates a `Vector3` value. Each mutable object has a `ResetType` property. This allows specifying when data in the mutable object should be reset. The following modes are available: -- `None` - do not reset. +- `None` - do not reset (default). - `ActiveSceneChange` - when the active (focused) scene changes. - `SceneUnloaded` - when the current scene gets unloaded. - `SceneLoaded` - when the scene is loaded. ### Custom game events -In some situations, built-in game events might not suffice. For example if a custom type needs to be passed as an argument to the event. In this case, custom game event can be created which would carry all the necessary data. +In some situations, built-in game events might not suffice. For example if a custom type needs to be passed as an argument to the event. In this case, a custom game event can be created which would carry all the necessary data. To create a custom game event, first create a regular `UnityEvent`: ```cs @@ -74,7 +74,7 @@ public class CustomGameEventListener : ArgumentGameEventListener diff --git a/Assets/Editor/GameEvents/Game/GameEventEditor.cs b/Assets/Editor/GameEvents/Game/GameEventEditor.cs index eff2222..8507006 100644 --- a/Assets/Editor/GameEvents/Game/GameEventEditor.cs +++ b/Assets/Editor/GameEvents/Game/GameEventEditor.cs @@ -8,8 +8,6 @@ namespace GameEvents.Game [CustomEditor(typeof(GameEvent))] public class GameEventEditor : Editor { - private const int GroupSpacingPixels = 8; - public override void OnInspectorGUI() { base.OnInspectorGUI(); @@ -21,7 +19,7 @@ public override void OnInspectorGUI() } GUI.enabled = Application.isPlaying; - GUILayout.Space(GroupSpacingPixels); + EditorGUILayout.Space(); DrawRaise(gameEvent); @@ -30,29 +28,17 @@ public override void OnInspectorGUI() return; } - GUILayout.Space(GroupSpacingPixels); - DrawListeners(gameEvent); + EditorGUILayout.Space(); + GameEventEditors.DrawReferences(gameEvent); } private static void DrawRaise(IGameEvent gameEvent) { - GUILayout.Label("Raise event (play mode only)"); + GameEventEditors.DrawPlaymodeLabel("Raise event"); if (GUILayout.Button("Raise")) { gameEvent.RaiseGameEvent(); } } - - private static void DrawListeners(IGameEvent gameEvent) - { - GUILayout.Label("Listeners"); - foreach (var listener in gameEvent.Listeners) - { - if (listener is MonoBehaviour behaviour) - { - EditorGUILayout.ObjectField(behaviour, typeof(Object), true); - } - } - } } } diff --git a/Assets/Editor/GameEvents/GameEventEditors.cs b/Assets/Editor/GameEvents/GameEventEditors.cs new file mode 100644 index 0000000..f867f83 --- /dev/null +++ b/Assets/Editor/GameEvents/GameEventEditors.cs @@ -0,0 +1,69 @@ +using System.Collections.Generic; +using GameEvents.Generic; +using UnityEditor; +using UnityEngine; + +namespace GameEvents +{ + public static class GameEventEditors + { + /// + /// Draw a list of argument game event listener references. + /// + public static void DrawReferences( + IArgumentGameEvent gameEvent + ) + { + DrawListeners(gameEvent.Listeners); + } + + /// + /// Draw a list of game event listener references. + /// + public static void DrawReferences(IGameEvent gameEvent) + { + DrawListeners(gameEvent.Listeners); + } + + /// + /// Draw a label which changes its suffix based on play mode state. + /// + public static void DrawPlaymodeLabel(string text) + { + var labelSuffix = Application.isPlaying ? "" : "(play mode only)"; + GUILayout.Label($"{text} {labelSuffix}"); + } + + private static void DrawListeners(ICollection listeners) + { + DrawPlaymodeLabel("Listeners"); + if (Application.isPlaying) + { + if (listeners.Count == 0) + { + EditorGUILayout.HelpBox( + "There are no listeners on this event", + MessageType.Warning + ); + + return; + } + + foreach (var listener in listeners) + { + if (listener is MonoBehaviour behaviour) + { + EditorGUILayout.ObjectField(behaviour, typeof(Object), true); + } + } + } + else + { + EditorGUILayout.HelpBox( + "Registered listeners will be displayed here", + MessageType.Info + ); + } + } + } +} diff --git a/Assets/Editor/GameEvents/GameEventEditors.cs.meta b/Assets/Editor/GameEvents/GameEventEditors.cs.meta new file mode 100644 index 0000000..b52dc18 --- /dev/null +++ b/Assets/Editor/GameEvents/GameEventEditors.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 0b8ab43a41cc4902a452e98af8e27a04 +timeCreated: 1602580628 \ No newline at end of file diff --git a/Assets/Editor/GameEvents/Generic/ArgumentGameEventEditor.cs b/Assets/Editor/GameEvents/Generic/ArgumentGameEventEditor.cs index ca82658..27ac0a8 100644 --- a/Assets/Editor/GameEvents/Generic/ArgumentGameEventEditor.cs +++ b/Assets/Editor/GameEvents/Generic/ArgumentGameEventEditor.cs @@ -7,8 +7,6 @@ namespace GameEvents.Generic public abstract class ArgumentGameEventEditor : Editor where TGameEvent : class, IArgumentGameEvent { - private const int GroupSpacingPixels = 8; - private TArgument argumentValue = default; public override void OnInspectorGUI() @@ -21,22 +19,17 @@ public override void OnInspectorGUI() } GUI.enabled = Application.isPlaying; - GUILayout.Space(GroupSpacingPixels); + EditorGUILayout.Space(); DrawRaise(gameEvent); - if (!Application.isPlaying) - { - return; - } - - GUILayout.Space(GroupSpacingPixels); - DrawListeners(gameEvent); + EditorGUILayout.Space(); + GameEventEditors.DrawReferences(gameEvent); } private void DrawRaise(TGameEvent gameEvent) { - GUILayout.Label("Raise event (play mode only)"); + GameEventEditors.DrawPlaymodeLabel("Raise event"); GUILayout.BeginHorizontal(); argumentValue = DrawArgumentField(argumentValue); @@ -48,18 +41,6 @@ private void DrawRaise(TGameEvent gameEvent) GUILayout.EndHorizontal(); } - private static void DrawListeners(TGameEvent gameEvent) - { - GUILayout.Label("Listeners"); - foreach (var listener in gameEvent.Listeners) - { - if (listener is MonoBehaviour behaviour) - { - EditorGUILayout.ObjectField(behaviour, typeof(Object), true); - } - } - } - /// /// Value that is entered in the argument field. /// diff --git a/Assets/Runtime/GameEvents/Game/GameEvent.cs b/Assets/Runtime/GameEvents/Game/GameEvent.cs index 784f5de..60fff24 100644 --- a/Assets/Runtime/GameEvents/Game/GameEvent.cs +++ b/Assets/Runtime/GameEvents/Game/GameEvent.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Collections.ObjectModel; using GameEvents.Generic; using UnityEngine; @@ -11,10 +12,17 @@ public class GameEvent : ScriptableObject, IGameEvent [Tooltip("Should debug messages be logged for this event")] private bool debug = false; + private readonly ReadOnlyCollection readListeners; + private readonly List listeners = new List(); - public IEnumerable Listeners => listeners; + public ICollection Listeners => readListeners; + + public GameEvent() + { + readListeners = listeners.AsReadOnly(); + } public void RaiseGameEvent() { diff --git a/Assets/Runtime/GameEvents/Generic/ArgumentGameEvent.cs b/Assets/Runtime/GameEvents/Generic/ArgumentGameEvent.cs index 090be13..763da03 100644 --- a/Assets/Runtime/GameEvents/Generic/ArgumentGameEvent.cs +++ b/Assets/Runtime/GameEvents/Generic/ArgumentGameEvent.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Collections.ObjectModel; using UnityEngine; namespace GameEvents.Generic @@ -10,10 +11,17 @@ public abstract class ArgumentGameEvent [Tooltip("Should debug messages be logged for this event")] private bool debug = false; + private readonly ReadOnlyCollection> readListeners; + private readonly List> listeners = new List>(); - public IEnumerable> Listeners => listeners; + public ICollection> Listeners => readListeners; + + public ArgumentGameEvent() + { + readListeners = listeners.AsReadOnly(); + } public void RaiseGameEvent(TArgument argument) { diff --git a/Assets/Runtime/GameEvents/Generic/IArgumentGameEvent.cs b/Assets/Runtime/GameEvents/Generic/IArgumentGameEvent.cs index 8a873b1..d473a55 100644 --- a/Assets/Runtime/GameEvents/Generic/IArgumentGameEvent.cs +++ b/Assets/Runtime/GameEvents/Generic/IArgumentGameEvent.cs @@ -7,7 +7,7 @@ public interface IArgumentGameEvent /// /// Currently registered listeners. /// - IEnumerable> Listeners { get; } + ICollection> Listeners { get; } /// /// Raise this event with an argument. diff --git a/Assets/Runtime/GameEvents/Generic/IGameEvent.cs b/Assets/Runtime/GameEvents/Generic/IGameEvent.cs index 0e52eb9..af1467c 100644 --- a/Assets/Runtime/GameEvents/Generic/IGameEvent.cs +++ b/Assets/Runtime/GameEvents/Generic/IGameEvent.cs @@ -7,7 +7,7 @@ public interface IGameEvent /// /// Currently registered listeners. /// - IEnumerable Listeners { get; } + ICollection Listeners { get; } /// /// Raise this event. diff --git a/Assets/Runtime/GameEvents/Int/IntGameEvent.cs b/Assets/Runtime/GameEvents/Int/IntGameEvent.cs index 535edaa..5fd24e0 100644 --- a/Assets/Runtime/GameEvents/Int/IntGameEvent.cs +++ b/Assets/Runtime/GameEvents/Int/IntGameEvent.cs @@ -3,7 +3,7 @@ namespace GameEvents.Int { - [CreateAssetMenu(fileName = "IntEvent", menuName = "Game Events/Int Game Event")] + [CreateAssetMenu(fileName = "IntGameEvent", menuName = "Game Events/Int Game Event")] public class IntGameEvent : ArgumentGameEvent { } diff --git a/Assets/Runtime/GameEvents/String/StringGameEvent.cs b/Assets/Runtime/GameEvents/String/StringGameEvent.cs index 17ec048..cdcabd0 100644 --- a/Assets/Runtime/GameEvents/String/StringGameEvent.cs +++ b/Assets/Runtime/GameEvents/String/StringGameEvent.cs @@ -3,7 +3,7 @@ namespace GameEvents.String { - [CreateAssetMenu(fileName = "StringEvent", menuName = "Game Events/String Game Event")] + [CreateAssetMenu(fileName = "StringGameEvent", menuName = "Game Events/String Game Event")] public class StringGameEvent : ArgumentGameEvent { } diff --git a/Assets/Runtime/GameEvents/Vector3/Vector3GameEventListener.cs b/Assets/Runtime/GameEvents/Vector3/Vector3GameEventListener.cs index 2ed041f..2fdc4f4 100644 --- a/Assets/Runtime/GameEvents/Vector3/Vector3GameEventListener.cs +++ b/Assets/Runtime/GameEvents/Vector3/Vector3GameEventListener.cs @@ -3,7 +3,7 @@ namespace GameEvents.Vector3 { - [AddComponentMenu("Game Events/Vector 3 Game Event Listener")] + [AddComponentMenu("Game Events/Vector3 Game Event Listener")] public class Vector3GameEventListener : ArgumentGameEventListener { diff --git a/Assets/Runtime/MutableObjects/Generic/MutableObject.cs b/Assets/Runtime/MutableObjects/Generic/MutableObject.cs index 3cf813e..15d4cfc 100644 --- a/Assets/Runtime/MutableObjects/Generic/MutableObject.cs +++ b/Assets/Runtime/MutableObjects/Generic/MutableObject.cs @@ -6,7 +6,7 @@ public abstract class MutableObject : ScriptableObject, IMutableObject { [SerializeField] [Tooltip("When reset should be called for this object")] - private ResetType resetType = ResetType.ActiveSceneChange; + private ResetType resetType = ResetType.None; public ResetType ResetType => resetType; diff --git a/README.md b/README.md index e8dc247..341b872 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ Available mutable objects: - `MutableVector3` - encapsulates a `Vector3` value. Each mutable object has a `ResetType` property. This allows specifying when data in the mutable object should be reset. The following modes are available: -- `None` - do not reset. +- `None` - do not reset (default). - `ActiveSceneChange` - when the active (focused) scene changes. - `SceneUnloaded` - when the current scene gets unloaded. - `SceneLoaded` - when the scene is loaded.