-
-
Notifications
You must be signed in to change notification settings - Fork 0
Custom Logic
You can make any existing MonoBehaviour
in your project reactive by implementing the IReactive
interface:
public class Button : MonoBehaviour, IReactive
{
...
}
The IReactive
interface and system require a common implementation, you should copy this into your class:
#region Required IReactive Implementation
[SerializeField]
private ReactiveData _reactiveData;
public ReactiveData reactiveData => _reactiveData;
public ReactiveMetadata reactiveMetadata => _reactiveMeta;
private void OnEnable() => this.OnReactiveEnable();
private void OnDisable() => this.OnReactiveDisable();
#endregion Required IReactive Implementation
private static ReactiveMetadata _reactiveMeta = new ReactiveMetadata();
Only the last line should be modified to provide metadata.
This contains a list of inputs, outputs, parameters and descriptions among other things.
This is primarily used to support the game developer within Unity Editor and is not a requirement for function.
The last line in the code above could for example be:
private static ReactiveMetadata _reactiveMeta = new ReactiveMetadata(
new MetaInterface(MetaInterfaceType.Input, "Use", "When a player presses the button."),
new MetaInterface(MetaInterfaceType.Output, "Pressed", "Invoked when the button has been pressed."),
new MetaInterface(MetaInterfaceType.Output, "Reset", "Invoked when the button is reset after being pressed.")
);
This declares an input "Use" with a description. The player could for example press the E
-Key and invoke the "Use" input on the button. Then the output "Pressed" will be invoked when the button is down and "Reset" will be invoked when the button is up. These inputs and outputs will appear in the Unity Editor to help make clever suggestions and prevent typos.
You will find more MetaInterface
-constructor overloads to provide details for a parameter name, type and description.
For example:
new MetaInterface(MetaInterfaceType.Input, "Message", "Logs a message to the Unity Console.",
"message", MetaParameterType.String, "The message to be logged in the console.")
The IReactive
interface requires a function to be implemented that handles inputs:
public void OnReactiveInput(ReactiveInput input)
{
switch (input.name)
{
case "Use":
...
break;
}
}
Here you can write custom logic for the input name that was invoked. The input
class contains useful details such as the activator, caller and parameter passed to the input.
The IReactive
interface provides extension methods on your current MonoBehaviour
. You can find them by prefixing the call with this
.
this.OnReactiveOutput(input, "Pressed");
This invokes all user-configured output handlers on this component matching the specified output name, "Pressed" in this case.
If you wish to pass along a parameter you can do so with this variant of the function:
this.OnReactiveOutput(input, "Pressed", parameter);
Where parameter
is an object
that can be of various parameter types.
If the level designer writes a custom parameter in an output, that custom parameter takes priority over the parameter provided here. It can be considered a default value used when the level designer leaves the parameter field blank.
By invoking an output upon receiving an input, you create a chain of events:
public void OnReactiveInput(ReactiveInput input)
{
switch (input.name)
{
case "Use":
this.OnReactiveOutput(input, "Pressed");
break;
}
}
In this case, invoking "Use" on the button causes an output "Pressed". An output handler for "Pressed" may then turn on a light by invoking an input "Enable" on the light. The light starts burning and breaks after 10 seconds causing a "Broken" output. An output handler for "Broken" may then play a sound by invoking an input on a sound component and so on.
It is important to note that the invocation of inputs and outputs are not immediate. They will require at least one FixedUpdate
-step in the game engine and may be delayed further (possibly several seconds if not minutes) by the user.
Our example button class could look something like this in practice:
public class Button : MonoBehaviour, IReactive
{
[Required IReactive Implementation] // the closed region.
private static ReactiveMetadata _reactiveMeta = new ReactiveMetadata(
new MetaInterface(MetaInterfaceType.Input, "Use", "When a player presses the button."),
new MetaInterface(MetaInterfaceType.Output, "Pressed", "Invoked when the button has been pressed."),
new MetaInterface(MetaInterfaceType.Output, "Reset", "Invoked when the button is reset after being pressed.")
);
private bool busy = false;
public void OnReactiveInput(ReactiveInput input)
{
if (busy) return;
if (input.name == "Use")
{
StartCoroutine(CoPressed(input));
}
}
private IEnumerator CoPressed(ReactiveInput input)
{
busy = true;
// animation button getting pressed.
yield return new WaitForSeconds(0.1f);
this.OnReactiveOutput(input, "Pressed");
// animation button getting released.
yield return new WaitForSeconds(0.1f);
this.OnReactiveOutput(input, "Reset");
busy = false;
}
}
It's then easy to add outputs for "Pressed" and invoke things in your scene:
For an example on how to use a keyboard key to invoke the "Use" input see Use Key.
Logic Components | Terminology | Programming | External Components | |
---|---|---|---|---|
Logic Animator | Activator | Custom Logic | Reactive Dynamic Light | |
Logic Auto | Caller | Custom Inspector | ||
Logic Branch | Delay | Use Key | ||
Logic Case | Target | |||
Logic Collision Pair | User Inputs | |||
Logic Compare | ||||
Logic Counter | ||||
Logic Destroy | ||||
Logic Filter | ||||
Logic Group | ||||
Logic Instantiate | ||||
Logic Log | ||||
Logic Move Linear | ||||
Logic Relay | ||||
Logic Timer | ||||
Logic Trigger | ||||
Logic Unity |