Skip to content
This repository was archived by the owner on Aug 10, 2021. It is now read-only.

Update game event editors #21

Merged
merged 4 commits into from
Oct 13, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Assets/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 5 additions & 5 deletions Assets/Documentation~/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
Expand All @@ -74,7 +74,7 @@ public class CustomGameEventListener : ArgumentGameEventListener<CustomGameEvent
}
```

Optionally add a custom editor so that the event could be raised, and the listeners which reference the event get displayed in the inspector.
, add a custom editor so that the event could be raised, and the listeners which reference the event get displayed in the inspector.
```cs
[CustomEditor(typeof(CustomGameEvent))]
public class GameObjectGameEventEditor : ArgumentGameEventEditor<CustomGameEvent, Custom>
Expand Down
22 changes: 4 additions & 18 deletions Assets/Editor/GameEvents/Game/GameEventEditor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -21,7 +19,7 @@ public override void OnInspectorGUI()
}

GUI.enabled = Application.isPlaying;
GUILayout.Space(GroupSpacingPixels);
EditorGUILayout.Space();

DrawRaise(gameEvent);

Expand All @@ -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);
}
}
}
}
}
69 changes: 69 additions & 0 deletions Assets/Editor/GameEvents/GameEventEditors.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
using System.Collections.Generic;
using GameEvents.Generic;
using UnityEditor;
using UnityEngine;

namespace GameEvents
{
public static class GameEventEditors
{
/// <summary>
/// Draw a list of argument game event listener references.
/// </summary>
public static void DrawReferences<TArgument>(
IArgumentGameEvent<TArgument> gameEvent
)
{
DrawListeners(gameEvent.Listeners);
}

/// <summary>
/// Draw a list of game event listener references.
/// </summary>
public static void DrawReferences(IGameEvent gameEvent)
{
DrawListeners(gameEvent.Listeners);
}

/// <summary>
/// Draw a label which changes its suffix based on play mode state.
/// </summary>
public static void DrawPlaymodeLabel(string text)
{
var labelSuffix = Application.isPlaying ? "" : "(play mode only)";
GUILayout.Label($"{text} {labelSuffix}");
}

private static void DrawListeners<T>(ICollection<T> 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
);
}
}
}
}
3 changes: 3 additions & 0 deletions Assets/Editor/GameEvents/GameEventEditors.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

27 changes: 4 additions & 23 deletions Assets/Editor/GameEvents/Generic/ArgumentGameEventEditor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ namespace GameEvents.Generic
public abstract class ArgumentGameEventEditor<TGameEvent, TArgument> : Editor
where TGameEvent : class, IArgumentGameEvent<TArgument>
{
private const int GroupSpacingPixels = 8;

private TArgument argumentValue = default;

public override void OnInspectorGUI()
Expand All @@ -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);
Expand All @@ -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);
}
}
}

/// <returns>
/// Value that is entered in the argument field.
/// </returns>
Expand Down
10 changes: 9 additions & 1 deletion Assets/Runtime/GameEvents/Game/GameEvent.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using GameEvents.Generic;
using UnityEngine;

Expand All @@ -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<IGameEventListener> readListeners;

private readonly List<IGameEventListener> listeners =
new List<IGameEventListener>();

public IEnumerable<IGameEventListener> Listeners => listeners;
public ICollection<IGameEventListener> Listeners => readListeners;

public GameEvent()
{
readListeners = listeners.AsReadOnly();
}

public void RaiseGameEvent()
{
Expand Down
10 changes: 9 additions & 1 deletion Assets/Runtime/GameEvents/Generic/ArgumentGameEvent.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using UnityEngine;

namespace GameEvents.Generic
Expand All @@ -10,10 +11,17 @@ public abstract class ArgumentGameEvent<TArgument>
[Tooltip("Should debug messages be logged for this event")]
private bool debug = false;

private readonly ReadOnlyCollection<IArgumentGameEventListener<TArgument>> readListeners;

private readonly List<IArgumentGameEventListener<TArgument>> listeners =
new List<IArgumentGameEventListener<TArgument>>();

public IEnumerable<IArgumentGameEventListener<TArgument>> Listeners => listeners;
public ICollection<IArgumentGameEventListener<TArgument>> Listeners => readListeners;

public ArgumentGameEvent()
{
readListeners = listeners.AsReadOnly();
}

public void RaiseGameEvent(TArgument argument)
{
Expand Down
2 changes: 1 addition & 1 deletion Assets/Runtime/GameEvents/Generic/IArgumentGameEvent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ public interface IArgumentGameEvent<TArgument>
/// <summary>
/// Currently registered listeners.
/// </summary>
IEnumerable<IArgumentGameEventListener<TArgument>> Listeners { get; }
ICollection<IArgumentGameEventListener<TArgument>> Listeners { get; }

/// <summary>
/// Raise this event with an argument.
Expand Down
2 changes: 1 addition & 1 deletion Assets/Runtime/GameEvents/Generic/IGameEvent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ public interface IGameEvent
/// <summary>
/// Currently registered listeners.
/// </summary>
IEnumerable<IGameEventListener> Listeners { get; }
ICollection<IGameEventListener> Listeners { get; }

/// <summary>
/// Raise this event.
Expand Down
2 changes: 1 addition & 1 deletion Assets/Runtime/GameEvents/Int/IntGameEvent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<int>
{
}
Expand Down
2 changes: 1 addition & 1 deletion Assets/Runtime/GameEvents/String/StringGameEvent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<string>
{
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Vector3GameEvent, Vector3Event, UnityEngine.Vector3>
{
Expand Down
2 changes: 1 addition & 1 deletion Assets/Runtime/MutableObjects/Generic/MutableObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down