|
| 1 | +# Documentation |
| 2 | +Most of the documented features can be imported as samples via Unity Package Manager or taken directly from [Samples](../Samples%7E) directory. |
| 3 | + |
| 4 | +## Table of contents |
| 5 | +- [General](#general) |
| 6 | +- [Simple events](#simple-events) |
| 7 | +- [Events with arguments](#events-with-arguments) |
| 8 | +- [Custom events](#custom-events) |
| 9 | + |
| 10 | +## General |
| 11 | + |
| 12 | +### Tips |
| 13 | +- When creating prefabs, event assets can be referenced in them. Then when you add in your prefabs into a scene, no additional setup is necessary! |
| 14 | +- Event assets can be used to communicate between scenes, this is particularly useful when using [Multi Scene Editing](https://docs.unity3d.com/Manual/MultiSceneEditing.html). |
| 15 | + |
| 16 | +### Event asset GUI |
| 17 | +<p align="center"> |
| 18 | + <img src="simple-event.png"/> |
| 19 | +</p> |
| 20 | + |
| 21 | +- Description - custom description for the event, what it does, etc, [Rich Text](https://docs.unity3d.com/Packages/com.unity.ugui@1.0/manual/StyledText.html) is also supported. |
| 22 | +- Lock button - disables the _Description_ field, useful to avoid accidental edits. |
| 23 | +- Suppress Exceptions - if an event has more than one listener, the first listener to throw an exception will break the chain. When enabled, the exception will get logged and other listeners will continue firing. |
| 24 | +- Trace - enable additional logging. |
| 25 | +- Raise Event - _Raise_ the event, available only in Play Mode. |
| 26 | +- Added Listeners - shows listeners which are subscribed to the event, available only in Play Mode. |
| 27 | + |
| 28 | +## Simple Events |
| 29 | +Simple events are useful when you need to _ping_ a `MonoBehaviour` in order to trigger an action. For example, the player loses, and the game needs to show a game over screen (complete example can be found [here](../Samples~/SimpleEvents)). |
| 30 | + |
| 31 | +### Event asset |
| 32 | +To create a simple event, _right click_ in the _Project_ window, and select _Create -> Scriptable Events -> Simple Scriptable Event_. This will create a `SimpleScriptableEvent` asset file, you can name the file however you like and place it anywhere in your project. |
| 33 | + |
| 34 | +### Raise event |
| 35 | +After you have prepared the event, you will want to _Raise_ it. This can be done by dragging the event asset into any `UnityEvent` available on one of your `MonoBehaviour` and selecting the `Raise` method: |
| 36 | +<p align="center"> |
| 37 | + <img src="simple-event-raise.png"/> |
| 38 | +</p> |
| 39 | + |
| 40 | +Alternatively you can _Raise_ the event from within a script: |
| 41 | +```cs |
| 42 | +[SerializedField] |
| 43 | +private SimpleScriptableEvent simpleScriptableEvent; |
| 44 | + |
| 45 | +... |
| 46 | + |
| 47 | +simpleScriptableEvent.Raise(); |
| 48 | +``` |
| 49 | + |
| 50 | +### Event listener |
| 51 | +Next thing you will need is a listener, select any `GameObject` in the scene, click _Add Component_ and select _Scriptable Events -> Simple Scriptable Event Listener_. Then, slot in the event asset in _Scriptable Event_ field and add a callback to the _On Raised_ `UnityEvent` under the listener which will invoke a method on a `MonoBehaviour`: |
| 52 | +<p align="center"> |
| 53 | + <img src="simple-event-listener.png"/> |
| 54 | +</p> |
| 55 | + |
| 56 | +Alternatively, you can subscribe to an event via a script: |
| 57 | +```cs |
| 58 | +public class CustomListener : MonoBehaviour, IScriptableEventListener<SimpleArg> |
| 59 | +{ |
| 60 | + [SerializeField] |
| 61 | + private SimpleScriptableEvent simpleScriptableEvent; |
| 62 | + |
| 63 | + public void OnRaised(SimpleArg arg) |
| 64 | + { |
| 65 | + // Handle event. |
| 66 | + } |
| 67 | + |
| 68 | + private void OnEnable() |
| 69 | + { |
| 70 | + simpleScriptableEvent.Add(this); |
| 71 | + } |
| 72 | + |
| 73 | + private void OnDisable() |
| 74 | + { |
| 75 | + simpleScriptableEvent.Remove(this); |
| 76 | + } |
| 77 | +} |
| 78 | +``` |
| 79 | + |
| 80 | +## Events with arguments |
| 81 | +Events with arguments are useful when you need to pass around data instead of just _pinging_. For example, the player picks up a coin, and you need to increment the score counter by a certain amount based on the coins value (complete example can be found [here](../Samples~/EventsWithArguments)). |
| 82 | + |
| 83 | +### Event asset |
| 84 | +To create an event with an argument, the approach is similar to [Simple Events](#simple-events). Simply _right click_ in the _Project_ window, and select _Create -> Scriptable Events -> {Type} Scriptable Event_, where _{Type}_ is the type of the data you want to pass. |
| 85 | + |
| 86 | +### Raise event |
| 87 | +To _Raise_ event with an argument, you will first need a `UnityEvent` that accepts a value of desired type. For each `ScriptableEvent` exists a `UnityEvent` with given type (see `ScriptableEvents.{Type}.{Type}UnityEvent` for existing implementations). |
| 88 | + |
| 89 | +After that is sorted, you will need to expose the `UnityEvent` and `Invoke` it in your script, for example a `UnityEvent` that accepts `float` would looks like: |
| 90 | +```cs |
| 91 | +[SerializedField] |
| 92 | +private ScriptableEvents.Float.FloatUnityEvent floatUnityEvent; |
| 93 | + |
| 94 | +... |
| 95 | + |
| 96 | +floatUnityEvent.Invoke(1.0f); |
| 97 | +``` |
| 98 | + |
| 99 | +Then, to raise the `ScriptableEvent`, drag it into the exposed `UnityEvent` of specific type and select the **Dynamic** `Raise` method. This will ensure that the value passed to the `UnityEvent` gets forwarded to the `Raise` method: |
| 100 | +<p align="center"> |
| 101 | + <img src="argument-event-raise.png"/> |
| 102 | +</p> |
| 103 | + |
| 104 | +Alternatively, you can _Raise_ the event from a script: |
| 105 | +```cs |
| 106 | +[SerializedField] |
| 107 | +private FloatScriptableEvent floatScriptableEvent; |
| 108 | + |
| 109 | +... |
| 110 | + |
| 111 | +floatScriptableEvent.Raise(1.0f); |
| 112 | +``` |
| 113 | + |
| 114 | +### Event listener |
| 115 | +Next thing you will need is a listener. The approach of adding a listener is similar to [Simple Events](#simple-events) as well. Select any `GameObject` in the scene, click _Add Component_ and select _Scriptable Events -> {Type} Scriptable Event Listener_. The only difference is that when selecting your method, you must select a **Dynamic** method. |
| 116 | + |
| 117 | +When subscribing via a script, you will also need to specify a different type argument rather than using `SimpleArg`, in this example `float` is used: |
| 118 | +```cs |
| 119 | +public class CustomListener : MonoBehaviour, IScriptableEventListener<float> |
| 120 | +{ |
| 121 | + [SerializeField] |
| 122 | + private FloatScriptableEvent floatScriptableEvent; |
| 123 | + |
| 124 | + public void OnRaised(float arg) |
| 125 | + { |
| 126 | + // Handle event. |
| 127 | + } |
| 128 | + |
| 129 | + private void OnEnable() |
| 130 | + { |
| 131 | + floatScriptableEvent.Add(this); |
| 132 | + } |
| 133 | + |
| 134 | + private void OnDisable() |
| 135 | + { |
| 136 | + floatScriptableEvent.Remove(this); |
| 137 | + } |
| 138 | +} |
| 139 | +``` |
| 140 | + |
| 141 | +## Custom Events |
| 142 | +You can also create custom events where you can pass any kind of data. For example, the player loses, and you want to send the reference to the player, and the enemy which collided with the player (complete example can be found [here](../Samples~/CustomEvents)). |
| 143 | + |
| 144 | +First define the data that you want to send: |
| 145 | +```cs |
| 146 | +public class CustomData |
| 147 | +{ |
| 148 | + public float ValueA { get; } |
| 149 | + |
| 150 | + public int ValueB { get; } |
| 151 | + |
| 152 | + public MaterialData(float valueA, int valueB) |
| 153 | + { |
| 154 | + ValueA = valueA; |
| 155 | + ValueB = valueB; |
| 156 | + } |
| 157 | +} |
| 158 | +``` |
| 159 | + |
| 160 | +Similarly to [Events with arguments](#events-with-arguments) you will need a `UnityEvent` which accepts your custom data type: |
| 161 | +```cs |
| 162 | +[Serializable] |
| 163 | +public CustomDataUnityEvent : UnityEvent<CustomData> |
| 164 | +{ |
| 165 | +} |
| 166 | +``` |
| 167 | + |
| 168 | +Next up, define the scriptable event: |
| 169 | +```cs |
| 170 | +[CreateAssetMenu( |
| 171 | + fileName = "CustomDataScriptableEvent", |
| 172 | + menuName = "Custom Scriptable Events/Custom Data Scriptable Event" |
| 173 | +)] |
| 174 | +public class CustomDataScriptableEvent : BaseScriptableEvent<CustomData> |
| 175 | +{ |
| 176 | +} |
| 177 | +``` |
| 178 | + |
| 179 | +Finally, create a listener which will glue everything together: |
| 180 | +```cs |
| 181 | +[AddComponentMenu("Custom Scriptable Events/Custom Data Event Listener")] |
| 182 | +public class CustomDataScriptableEventListener |
| 183 | + : BaseScriptableEventListener< |
| 184 | + CustomDataScriptableEvent, |
| 185 | + CustomDataUnityEvent, |
| 186 | + CustomData |
| 187 | + > |
| 188 | +{ |
| 189 | +} |
| 190 | +``` |
| 191 | + |
| 192 | +After setting everything, you can use your custom event the same way as described in [Events with arguments](#events-with-arguments). However, you will not be able to use additional GUI features as with built-in events. To solve this, define a custom editor script (make sure to place it under `Editor` directory): |
| 193 | +```cs |
| 194 | +[CustomEditor(typeof(CustomDataScriptableEvent))] |
| 195 | +public class CustomDataScriptableEventEditor : BaseScriptableEventEditor<CustomData> |
| 196 | +{ |
| 197 | + protected override CustomData DrawArgField(CustomData value) |
| 198 | + { |
| 199 | + // Initially the value will be null as BaseScriptableEventEditor does not know how to |
| 200 | + // construct an instance of CustomData. |
| 201 | + if (value == null) |
| 202 | + { |
| 203 | + return new CustomData(0f, 0); |
| 204 | + } |
| 205 | + |
| 206 | + // Add input fields under the inspector. Note that these can also be objects - you can drag |
| 207 | + // them in from the scene window! |
| 208 | +
|
| 209 | + // Also note that you can omit this step if your data is complex. For example, |
| 210 | + // SimpleScriptableEvent does not render any fields. |
| 211 | + EditorGUILayout.BeginVertical(); |
| 212 | + var valueA = EditorGUILayout.FloatField("Value A", value.ValueA); |
| 213 | + var valueB = EditorGUILayout.IntField("Value B", value.ValueB); |
| 214 | + EditorGUILayout.EndVertical(); |
| 215 | + |
| 216 | + return new CustomData(valueA, valueB); |
| 217 | + } |
| 218 | +} |
| 219 | +``` |
0 commit comments