diff --git a/Documentation/HowToGuides/Actions/CreatingACustomBooleanAction/README.md b/Documentation/HowToGuides/Actions/CreatingACustomBooleanAction/README.md new file mode 100644 index 0000000..5d91c17 --- /dev/null +++ b/Documentation/HowToGuides/Actions/CreatingACustomBooleanAction/README.md @@ -0,0 +1,314 @@ +> [Home](../../../../README.md) > [How-to Guides](../../README.md) > [Actions](../README.md) + +# Creating a Custom Boolean Action + +> * Level: Beginner +> +> * Reading Time: 5 minutes +> +> * Checked with: Unity 2018.3.11f1 + +## Introduction + +Custom Boolean Actions emit events when a defined boolean value changes. + +A Custom Boolean Action is derived from a [Zinnia.Unity] Action and therefore can be injected into any VRTK prefab that requires an initiating action (e.g. grab action in a `Interactor`). + +## Useful definitions + +* `Boolean Action` - A Zinnia.Action that emits events based on a bool value. +* `Zinnia` - A library containing a collection of reusable software design patterns for use with the Unity software. +* `Namespace` - A C# namespace is used to organize classes into distinct groups so duplicate definitions do not cause conflicts. + +## Prerequisites + +* Follow the [VRTK Getting Started] steps to add VRTK to your Unity project. + +## Let's Start + +### Step 1 + +Create a new `C# Script` and name it `CustomBooleanAction`, this script will be our new `Boolean Action` component. + +Create two new `Cube` Unity 3D Objects by selecting `Main Menu -> GameObject -> 3D Object -> Cube` and change their `Transform` properties to: + +* Position: `X = 0.5`, `Y = 1`, `Z = 1` +* Scale: `X = 0.5`, `Y = 0.5`, `Z = 0.5` + +and + +* Position: `X = -0.5`, `Y = 1`, `Z = 1` +* Scale: `X = 0.5`, `Y = 0.5`, `Z = 0.5` + +We will name them `BlueCube` and `RedCube` for clarity, let's also change their materials accordingly. + +We will be using these as a way of testing our `Boolean Action` by changing a `GameObject` activation state on and off based on another `GameObject` activation state. + +Create an empty `GameObject` too, this is where we will set our `CustomBooleanAction` component. + +![Create GameObjects](assets/images/CreateGameObjects.png) + +### Step 2 + +Open the newly created `CustomBooleanAction` script. The Unity Software creates the default body for the script so right now it should look like this: + +```csharp +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class CustomBooleanAction : MonoBehaviour +{ + // Start is called before the first frame update + void Start() + { + + } + + // Update is called once per frame + void Update() + { + + } +} +``` + +We don't actually need most of that default code so delete the relevant lines so the code is at the following starting point: + +```csharp +using UnityEngine; + +public class CustomBooleanAction : MonoBehaviour +{ + + void Update() + { + + } + +} +``` + +### Step 3 + +First thing we should do is wrap our class in a namespace to prevent any definitions we make such as the class name causing conflicts with duplicate class names elsewhere in the code base. + +It's best practice to use the directory structure as the basis of the namespace so in the `Assets` directory create a new nested set of directories as such: +`Actions/BooleanActions/` and move the `CustomBooleanAction.cs` file inside that directory. Our namespace can then become the name of our project followed by the directory structure our script is found in. i.e.: + +`Actions.BooleanActions` + +So to add that namespace to our code, we simply wrap the class with a namespace block, like so: + +```csharp +namespace Actions.BooleanActions +{ + using UnityEngine; + + public class CustomBooleanAction : MonoBehaviour + { + + void Update() + { + + } + + } +} +``` + +### Step 4 + +Currently the class extends the [MonoBehaviour](https://docs.unity3d.com/ScriptReference/MonoBehaviour.html) component. However, we want our component to be a Zinnia Boolean Action so we can emit a boolean value. To extend the Zinnia Boolean Action, we simply need to include the relevant Zinnia Action library in our class and make sure we extend it. + +```csharp +namespace Actions.BooleanActions +{ + using UnityEngine; + using Zinnia.Action; + + public class CustomBooleanAction : BooleanAction + { + + void Update() + { + + } + + } +} +``` + +### Step 5 + +We have two more things to do, first, let's add a GameObject variable to our CustomBooleanAction script, we will change the Boolean Action based on this GameObject state. + +```csharp +namespace Actions.BooleanActions +{ + using UnityEngine; + using Zinnia.Action; + + public class CustomBooleanAction : BooleanAction + { + + [SerializeField] + private GameObject MonitoredObject; + + void Update() + { + + } + + } +} +``` + +Now, let's use the `Receive` method from Zinnia.Action to keep track of our `MonitoredObject` activation state. + +```csharp +namespace Actions.BooleanActions +{ + using UnityEngine; + using Zinnia.Action; + + public class CustomBooleanAction : BooleanAction + { + + [SerializeField] + private GameObject MonitoredObject; + + void Update() + { + Receive(MonitoredObject.activeInHierarchy); + } + + } +} +``` + +> Note: `Receive` takes a boolean value and changes the Boolean Action state accordingly, keep in mind that its argument doesn't have to come from the same class, you can use any boolean value from anywhere in your project. + +### Step 6 + +Now, add the script to the empty GameObject in order to see it as a component. + +![Add Custom Boolean Action Component](assets/images/AddCustomBooleanActionComponent.png) + +Then, drag and drop the `RedCube` into the `MonitoredObject` variable. + +![Red Cube To MonitoredObject](assets/images/RedCubeToMonitoredObject.png) + +### Step 7 + +The `Custom Boolean Value` component has 4 events that can be hooked into: + +* Activation State Changed: Emitted when the activation state of the action changes from the previous state. +* Activated: Emitted when the boolean value in the `Receive` method is true (Once). +* Value Changed: Emitted when the state of the boolean value in the `Receive` changes, e.g. goes from being true to being false. +* Deactivated: Emitted when the boolean value in the `Receive` method is false (Once). + +For an example of how to hook these events into doing something, let's make the `BlueCube` GameObject disappear when our `Custom Boolean Value` activation state is false and then have it reappear when the `Custom Boolean Value` activation state is true. + +Click the `+` symbol in the bottom right corner of the `Activated` event parameter in the `Custom Boolean Action` component and then drag and drop the `BlueCube` GameObject into the box that appears and displays `None (Object)`. + +Select a `Function` to perform when the `Activated` event is emitted. For this example, select `GameObject -> SetActive(bool)` (be sure not to select `Dynamic bool - SetActive` for this example). + +![Add Blue Cube To Activated Event Listener](assets/images/AddBlueCubeToActivatedListener.png) + +Do the same with the `Deactivated` event, selecting `GameObject -> SetActive(bool)` again, with a different argument. + +![Add Blue Cube To Deactivated Event Listener](assets/images/AddBlueCubeToDeactivatedListener.png) + +### Step 8 + +Play the Unity scene and change the `RedCube` activation state in the inspector, the `BlueCube` GameObject will disappear when the `RedCube` object is deactivated and reappear when the `RedCube` object is activated. + +![Change Red Cube Activation State](assets/images/ChangeRedCubeActivationState.png) + +### Step 9 + +Now, let's add some extra functionality to our Boolean Action. We will add a bool variable that allows us to flip our script behaviour. + +To do this, we have to create a bool variable in the script. + +```csharp +namespace Actions.BooleanActions +{ + using UnityEngine; + using Zinnia.Action; + + public class CustomBooleanAction : BooleanAction + { + + [SerializeField] + private bool flip = false; + + [SerializeField] + private GameObject MonitoredObject; + + void Update() + { + Receive(MonitoredObject.activeInHierarchy); + } + + } +} +``` + +Next, let's add an if statement that will send the `MonitoredObject` activation state negated to the `Receive` method if the `flip` variable is True. + +```csharp +namespace Actions.BooleanActions +{ + using UnityEngine; + using Zinnia.Action; + + public class CustomBooleanAction : BooleanAction + { + + [SerializeField] + private bool flip = false; + + [SerializeField] + private GameObject MonitoredObject; + + void Update() + { + if(flip){ + Receive(!MonitoredObject.activeInHierarchy); + } + else{ + Receive(MonitoredObject.activeInHierarchy); + } + } + + } +} +``` + +You can add as many `Receive` methods as you want in a script in order to create more complex behaviours. + +### Step 10 + +Play the Unity scene and change the `RedCube` activation state in the inspector again. +If you set the `flip` value to `True` the `BlueCube` GameObject will disappear when the `RedCube` object is activated and reappear when the `RedCube` object is deactivated. + +![Change Red Cube Activation State With Flip](assets/images/ChangeRedCubeActivationStateWithFlip.png) + +### Done + +Now you have a custom Boolean Action performing the action of hiding and showing another GameObject based on another GameObject activation state. + +If you require many different inputs to perform the same output, then you don't want to have to wire up the same action outcomes in all of the Actions you add to your scene. This can be streamlined by creating Proxy Actions and chaining actions together. + +## Related Reading + +* [Adding A Unity Axis Action](../AddingAUnityAxisAction/README.md) +* [Creating Proxy Actions And Chaining Actions](../CreatingProxyActionsAndChainingActions/README.md) +* [Adding A Straight Pointer](../../Pointers/AddingAStraightPointer/README.md) +* [Adding A Curved Pointer](../../Pointers/AddingACurvedPointer/README.md) +* [Adding An Interactor](../../Interactions/AddingAnInteractor/README.md) + +[Zinnia.Unity]: https://github.com/ExtendRealityLtd/Zinnia.Unity +[VRTK Getting Started]: https://github.com/ExtendRealityLtd/VRTK#getting-started diff --git a/Documentation/HowToGuides/Actions/CreatingACustomBooleanAction/assets/images/AddBlueCubeToActivatedListener.png b/Documentation/HowToGuides/Actions/CreatingACustomBooleanAction/assets/images/AddBlueCubeToActivatedListener.png new file mode 100644 index 0000000..333582b Binary files /dev/null and b/Documentation/HowToGuides/Actions/CreatingACustomBooleanAction/assets/images/AddBlueCubeToActivatedListener.png differ diff --git a/Documentation/HowToGuides/Actions/CreatingACustomBooleanAction/assets/images/AddBlueCubeToDeactivatedListener.png b/Documentation/HowToGuides/Actions/CreatingACustomBooleanAction/assets/images/AddBlueCubeToDeactivatedListener.png new file mode 100644 index 0000000..902d70e Binary files /dev/null and b/Documentation/HowToGuides/Actions/CreatingACustomBooleanAction/assets/images/AddBlueCubeToDeactivatedListener.png differ diff --git a/Documentation/HowToGuides/Actions/CreatingACustomBooleanAction/assets/images/AddCustomBooleanActionComponent.png b/Documentation/HowToGuides/Actions/CreatingACustomBooleanAction/assets/images/AddCustomBooleanActionComponent.png new file mode 100644 index 0000000..769682f Binary files /dev/null and b/Documentation/HowToGuides/Actions/CreatingACustomBooleanAction/assets/images/AddCustomBooleanActionComponent.png differ diff --git a/Documentation/HowToGuides/Actions/CreatingACustomBooleanAction/assets/images/ChangeRedCubeActivationState.png b/Documentation/HowToGuides/Actions/CreatingACustomBooleanAction/assets/images/ChangeRedCubeActivationState.png new file mode 100644 index 0000000..40be2b4 Binary files /dev/null and b/Documentation/HowToGuides/Actions/CreatingACustomBooleanAction/assets/images/ChangeRedCubeActivationState.png differ diff --git a/Documentation/HowToGuides/Actions/CreatingACustomBooleanAction/assets/images/ChangeRedCubeActivationStateWithFlip.png b/Documentation/HowToGuides/Actions/CreatingACustomBooleanAction/assets/images/ChangeRedCubeActivationStateWithFlip.png new file mode 100644 index 0000000..a5cf408 Binary files /dev/null and b/Documentation/HowToGuides/Actions/CreatingACustomBooleanAction/assets/images/ChangeRedCubeActivationStateWithFlip.png differ diff --git a/Documentation/HowToGuides/Actions/CreatingACustomBooleanAction/assets/images/CreateGameObjects.png b/Documentation/HowToGuides/Actions/CreatingACustomBooleanAction/assets/images/CreateGameObjects.png new file mode 100644 index 0000000..51493f9 Binary files /dev/null and b/Documentation/HowToGuides/Actions/CreatingACustomBooleanAction/assets/images/CreateGameObjects.png differ diff --git a/Documentation/HowToGuides/Actions/CreatingACustomBooleanAction/assets/images/RedCubeToMonitoredObject.png b/Documentation/HowToGuides/Actions/CreatingACustomBooleanAction/assets/images/RedCubeToMonitoredObject.png new file mode 100644 index 0000000..fac666f Binary files /dev/null and b/Documentation/HowToGuides/Actions/CreatingACustomBooleanAction/assets/images/RedCubeToMonitoredObject.png differ diff --git a/Documentation/HowToGuides/Actions/README.md b/Documentation/HowToGuides/Actions/README.md index 9b9ec29..4ad757f 100644 --- a/Documentation/HowToGuides/Actions/README.md +++ b/Documentation/HowToGuides/Actions/README.md @@ -6,5 +6,6 @@ Providing generic ways of listening for input and determining an appropriate out ### [Adding A Unity Button Action](AddingAUnityButtonAction/README.md) ### [Adding A Unity Axis Action](AddingAUnityAxisAction/README.md) +### [Creating A Custom Boolean Action](CreatingACustomBooleanAction/README.md) ### [Creating Proxy Actions And Chaining Actions](CreatingProxyActionsAndChainingActions/README.md) ### [Converting A Float Action To A Boolean Action](ConvertingAFloatActionToABooleanAction/README.md) \ No newline at end of file