Skip to content
Update 1. Introduction authored by Jake Carfagno's avatar Jake Carfagno
# Table of Contents
[[_TOC_]]
EventTriggers, EventConditions, and EventActions comprise a system for setting up a flow of cause-and-effect within the Unity Inspector. This system was designed to be modular so that it can be used for just about anything interactive in a game. This includes tutorials, narrative, and even menu functionality.
# The Components
Everything within the system is an **EventTrigger**, an **EventCondition**, or an **EventAction**. Think of them as conditions and effects: an EventTrigger specifies when to do something, the EventActions specify what to do when that something happens, and an EventCondition is an extra condition that has to be true when the something happens before the actions can be carried out. All are MonoBehaviours, meaning they can be attached to any GameObject within the Unity Scene.
## EventTriggers
An **EventTrigger** is a condition. While the Trigger is enabled, it will monitor for a specific event to occur. Some examples of EventTriggers with their respective conditions are:
* **TriggerOnEnable:** fires in its `OnEnable()` method. Note that this Trigger differs from TriggerGameObjectEnabled because the latter fires when a specific GameObject is enabled, which does not have to be the GameObject to which the Trigger is attached.
* **TriggerTimePassed:** fires when a specific amount of time in seconds has passed since the Trigger was enabled.
* **TriggerInputKey:** fires when a key with a specified KeyCode is pressed.
When the EventTrigger's condition is met, it also checks every attached and _enabled_ **EventCondition** to make sure all those conditions are true as well. If any returns false, the EventTrigger will not fire and continues to wait for its own condition to be met again. When the EventTrigger does fire, it will carry out the actions of all _enabled_ EventActions attached to the same GameObject.
### Settings
There are several settings that can be configured for all EventTriggers, and some EventTriggers will contain additional settings particular to their specific functionality.
* **hasFired:** This read-only boolean indicates whether the EventTrigger's condition has been met and its Actions executed. It is false when the EventTrigger before the Trigger has fired or after it has reset.
* **repeatable:** This boolean indicates whether the Trigger can be invoked multiple times. The default is that, once the Trigger has fired once, it automatically disables the GameObject it is attached to, stopping monitoring for the condition to be met again. If **repeatable** is true, then when the Trigger fires, it will immediately reset itself so it can fire again the next time the condition is met. This could be used for an animation on the player's health bar when they are hit because the player may be hit multiple times throughout the game.
* **nextTrigger:** This reference to an EventTrigger allows you to specify a next step in the system's flow. When this Trigger's condition is satisfied, it will then enable the **nextTrigger**. This can be used for a couple of systems:
* A linear chain, like a tutorial or narrative in which the player must complete actions in a set order.
* A cycle, like toggling whether a menu is enabled. The player can only enable a pause menu if it is disabled and vice versa, so two TriggerInputKeys can have each other as **nextStep**s to alternate whether the pause menu is shown.
* **enableNextTriggerAfterActionsComplete:** This boolean allows you to choose whether to enable the nextStep after each EventAction is completed instead of before. By default, the EventTrigger will enable its nextStep and then invoke each of its Actions, which is the desired behavior in a couple of scenarios:
* The nextStep must monitor for changes made by the Actions. For example, a Trigger monitors for the player to reach a certain position in the Scene. On arriving at that position, the Trigger runs an Action that starts a cutscene. The nextStep Trigger monitors for when the cutscene finishes.
* The nextStep can occur at any time, even before the previous Trigger's Actions have finished executing. This can happen if you are using a Trigger to start an animation and then listen for input during the animation to skip it.
Use **enableNextTriggerAfterActionsComplete** when you want to complete all Actions before enabling the nextStep. This can be used when animating the player's health bar when hit - the first EventTrigger's attached EventActions grow the health bar. Its nextStep is a TriggerOnEnable that handles the shrinking back to normal size. Because TriggerOnEnable fires immediately when it is enabled, the first EventTrigger must wait until its own animation is finished before enabling the next step TriggerOnEnable.
* **actions:** This is an array of all the EventActions to run when the Trigger fires. This array is read-only and automatically populated in Awake() with all EventActions attached to the same GameObject as the Trigger.
***
## EventActions
With EventTriggers as the conditions, **EventActions** are the effects that are invoked when the EventTrigger is satisfied. Some examples of EventActions and their functions are:
* **ActionGameObjectEnable:** enables a target GameObject when invoked.
* **ActionLoadScene:** loads a Unity Scene with a given name when invoked.
* **ActionButtonInteractableSet:** set whether a Unity Canvas button is interactable.
### Types of EventActions
EventTriggers wait for each of their Actions to complete before either disabling themselves or resetting to be repeated. The simplest EventActions mark themselves as complete right after their ExecuteAction() method is called. However, this behavior is not desirable for more complex EventActions for which the Trigger disabling the GameObject may interrupt the Action's functionality - like an animation relying on interpolation in coroutines - or for which the Trigger should be repeatable but only after it is completely done firing once - like playing a footstep sound effect as the player moves. Because of this, there are a few types of EventActions that allow for more complexity. Note that an EventTrigger **will not** wait for each Action to finish before invoking the next. It invokes them all at once and then waits until all of them are done before moving to the next Trigger, disabling itself, or resetting to repeat.
#### EventActionExplicitDone
The simplest extension of EventActions is the **EventActionExplicitDone**. To create an EventAction that marks itself as done after a specific condition is met instead of just immediately upon invoking, create a child class of the EventActionExplicitDone. An example of this would be an EventAction to play a sound- ActionAudioPlay
```cs
public class ActionAudioPlay : EventActionExplicitDone
{
#region Variables
[SerializeField, Tooltip("Whether to mark the Action as complete after the audio is finished playing")]
private bool completeAfterAudioFinish = true;
[SerializeField, Tooltip("The AudioSource to play the audio through")]
private AudioSource audioSource = null;
[SerializeField, Tooltip("The AudioClip to play")]
private AudioClip audioClip = null;
#endregion Variables
#region MonoBehaviour
private void Update()
{
if (completeAfterAudioFinish && !audioSource.IsPlaying)
{
CompleteAction();
}
}
#endregion MonoBehaviour
#region Protected Methods
protected override void ExecuteAction()
{
audioSource.PlayOneShot(audioClip, volume);
if (!completeAfterAudioFinish)
{
CompleteAction();
}
}
#endregion Protected Methods
}
```
You can see in the above code snippet that there is an option to complete the Action after the audio is finished instead of when it is started. If this bool is true, then the Action will wait in Update() until the AudioSource is not playing anything anymore to say it is done. The CompleteAction() method is provided by the EventActionExplicitDone class, so you only have to make sure to call that at the appropriate time in each child class.
#### EventActionInterruptable
Sometimes, you will need to create an EventAction whose effect is not instant (which EventActionExplicitDone allows), but then you'll want to have the ability to interrupt it mid-effect. This is important for animations, like the above-described health bar effect. If the player is hit again while the animation is already in-motion, we need to be able to cancel the ongoing animation and reset immediately to play the animation again, and **EventActionInterruptable** gives us this ability. It extends from EventActionExplicitDone, so any child classes of EventActionInterruptable have access to (and must use) the CompleteAction() method when finished. EventActionInterruptable adds Coroutines to allow for interruptable effects.
### Settings
The settings that can be configured for every EventAction are:
* **priority:** An integer allowing you to specify the order in which EventActions on a single GameObject should be executed. The default is 0, and Actions are invoked in ascending order (so lower numbers go _before_ higher numbers). If two Actions have equal priority, then the one showing first in the Inspector will execute first. It works exactly like Unity's built-in Script Execution Order for Actions that depend on each other to function properly.
* **actionDone:** This read-only bool indicates whether the action has been invoked. It will be false before the EventAction has run once and true after it has run. When the EventTrigger managing it resets to run again, this bool will be set to false again as well.
* **waitTime:** A float time in seconds to wait after an EventTrigger fires before the Action executes. Because priority only affects the order in which Actions are started, make sure that any Actions that depend on one Action have a waitTime at least as much as the Action they must wait for to function, i.e. the set-up should always wait less time than the things that need to be set up. The wait time can be useful for a pause between actions, like if you want to delay a moment after the player reaches a checkpoint before throwing dialogue at them. It feels more natural this way than if the player is frozen the moment they touch the checkpoint.
## EventConditions
EventTriggers assume that we want an event to happen everytime a certain something happens. EventConditions allow us to check the state of things not directly relevant to the Trigger before executing all actions. A simple example is, we may use an EventCondition that checks that the player's health is non-zero before animating the player's health bar when hit. If the player's health is zero, then we can show a different animation.
When an EventTrigger's condition is met (such as being enabled for TriggerOnEnable), it will call the CheckCondition() method of every attached and enabled EventCondition. If any returns false, the EventTrigger will not fire.
### Settings
Each EventCondition comes with one setting, and some EventCondition will contain additional settings particular to their specific functionality.
* **invertCondition:** A boolean allowing you to invert the output of the condition-check by applying a NOT operator to it. This allows you to use one class to check whether something is true or false instead of having to create separate EventConditions for each version.
# System Flow
![ModularEventsFlow](uploads/7cd04eec96ce33577168440555bcd3e0/ModularEventsFlow.png)
[PDF version of the above diagram](uploads/4d4267b24cf8832edab60325f84a9f22/ModularEventsFlow.pdf)
----
**[Next Page](Modular Events System/How to Use)**
\ No newline at end of file