This package provides utilities for implementing game architecture which is oriented around ScriptableObject
assets and game events. Most of these ideas are based on Unite2017.
- Game events - allows transferring of data between scripts using
ScriptableObject
event assets. - Game event listeners - listener components which allow subscribing to various events.
- Mutable objects - eases up sharing of mutable data via
ScriptableObject
assets.
This package can be installed by using the Unity Package Manager. To install this package, add the following to manifest.json
:
{
"dependencies": {
"com.chark.unity-scriptable-objects": "https://github.com/chark/unity-scriptable-objects.git#upm"
}
}
Example usage of game events and mutable objects can be found in Assets/Samples directory. These samples can also be imported via Unity Package Manager.
Game events are scriptable objects (Right Click -> Create -> Game Events -> ...) which can be subscribed to via listener components (Add Component -> Game Events -> ...). Events allow to decouple scripts and instead rely on intermediate ScriptableObject
assets for communication.
Available game events:
GameEvent
- simple event which doesn't accept any arguments.BoolGameEvent
- event with abool
argument.IntGameEvent
- event with anint
argument.FloatGameEvent
- event with afloat
argument.StringGameEvent
- event with astring
argument.Vector2GameEvent
- event with aVector2
argument.Vector3GameEvent
- event with aVector3
argument.TransformGameEvent
- event with aTransform
argument.GameObjectGameEvent
- event with aGameObject
argument.
Mutable objects are used for storing and editing data on ScriptableObject
assets at runtime. This data can be referenced, observed and used as a bridge by various scripts. Mutable objects are useful in situations where ScriptableObject
data needs to be reset when the active scene changes.
Available mutable objects:
MutableBool
- encapsulates abool
value.MutableInt
- encapsulates anint
value.MutableFloat
- encapsulates afloat
value.MutableString
- encapsulates astring
value.MutableVector2
- encapsulates aVector2
value.MutableVector3
- encapsulates aVector3
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 (default).ActiveSceneChange
- when the active (focused) scene changes.SceneUnloaded
- when the current scene gets unloaded.SceneLoaded
- when the scene is loaded.
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
:
[Serializable]
public class CustomEvent : UnityEvent<Custom>
{
}
After that is ready, create a game event by extending GameEvents.Generic.ArgumentGameEvent
:
[CreateAssetMenu(fileName = "CustomEvent", menuName = "Game Events/Custom Event")]
public class CustomGameEvent : ArgumentGameEvent<Custom>
{
}
Finally, create a game event listener by extending GameEvents.Generic.ArgumentGameEventListener
:
[AddComponentMenu("Game Events/Custom Game Event Listener")]
public class CustomGameEventListener : ArgumentGameEventListener<CustomGameEvent, CustomEvent, Custom>
{
}
, add a custom editor so that the event could be raised, and the listeners which reference the event get displayed in the inspector.
[CustomEditor(typeof(CustomGameEvent))]
public class GameObjectGameEventEditor : ArgumentGameEventEditor<CustomGameEvent, Custom>
{
protected override Custom DrawArgumentField(Custom value)
{
var fieldValue = EditorGUILayout
.ObjectField(value, typeof(Custom), true);
return fieldValue as Custom;
}
}
In some cases, littering the script code with loads of MutableObject
references can be inconvenient. To avoid this, a single object can be used which encompasses multiple fields.
To create a custom mutable object, extend MutableObjects.Generic.MutableObject
and override ResetValues()
method, e.g:
[CreateAssetMenu(fileName = "MutableCustom", menuName = "Mutable Objects/Mutable Custom")]
public class MutableCustom : MutableObject
{
[SerializeField]
private int health = default;
[SerializeField]
private int armor = default;
[SerializeField]
private int xp = default;
public int Health { get; set; }
public int Armor { get; set; }
public int Xp { get; set; }
// This will set property values when mutable object is enabled or if the values change in the
// inspector.
public override void ResetValues()
{
Health = health;
Armor = armor;
Xp = xp;
}
}