Implementing key objects in Unity by a newbie. Part 1.
Tags: unity3d, gamedev, csharp
Published: 2021-05-12 15:45:43

Hello everyone once again! Today I’m writing a post about Unity! I started developing using Unity just a few months ago, but despite having some experience in development, this is my first time with videogames.

I believe that explaining each step while having zero experience myself might be interesting and could provide other developers with some insight of the challenges that come with developing games.

I’m a newbie when it comes to developing videogames in general, and more particularly using Unity, so feel free to make any suggestions you might have, and also let me know if you believe there are more standardized ways to solve the problems I’m about to propose.

As a final note before we dive in, I would like to remind you that I’m no artist at all, therefore I won’t be creating any assets for the game from scratch (like 3D models, textures, animations and so on). I’ll be using free asset packs from the unity store :)

## Step 0 - Preparing the basic environment.

Everything being developed in this post is publicly available in [this repository](https://github.com/alesanmed-blog-posts/unity-animate-doors).

Every step is going to be also accessible via Tags. For example, you can check the final code of step 0 [here](https://github.com/alesanmed-blog-posts/unity-animate-doors/tree/step-0).

In this first part we are only setting up the minimum things we need to start working. This includes a first person player controller that was also implemented by me (not covered in this post). A ground plane with a wood tiling downloaded from the Assets Store (this [package](https://assetstore.unity.com/packages/2d/textures-materials/wood/wooden-floor-materials-150564)) and a door also downloaded from the assets store (this [package](https://assetstore.unity.com/packages/3d/props/apartment-door-146225)).

That door is the one we are going to animate later for opening and closing it.

For the character movement I've set up the new input system so we can move with the standard WASD keys.

Feel free to check out the project and explore everything in there until you feel comfortable to move to the next step.

> Note: For showing the cursor while playing the game, press **ESC**

## Step 1 - Animating the door

In this step we are going to animate the door so it can be opened and closed. For that we are going to make the door rotate around the hinges, by rotating the *SimpleDoor:Global_ControlSimpleDoor_MainDoor_LOD1* component around the Y axis.

First things first: If you haven’t done it already, make sure to display the animation window by clicking Window > Animation > Animation. Then switch to that tab, click the door and, in the Animation tab, click to create a new animation. Call it "Door".

{% youtube KH5707VMy6U %}

Once we have the animation file, we are going to create the animation itself. Switch to the Animation tab and click the *SimpleDoor:Global_ControlSimpleDoor_MainDoor_LOD1* component inside *Aparment_Door* prefab.

Now click the record button in the Animation tab and right click the *Rotation* attribute inside Transform section of *SimpleDoor:Global_ControlSimpleDoor_MainDoor_LOD1* and click *Add Key*.

That will create a [key frame](https://docs.unity3d.com/Manual/animeditor-AnimatingAGameObject.html) in the frame 0 with the rotation set to (0,0,0). Then move to the frame 120 and change the rotation around the Y axis to 97º. It's important to change it in the rotation section instead of rotating directly in the scene view.

Now you should have 2 key frames, so you can stop recording and save the animation.

{% youtube knojeRw3v4M %}

Now that we have the animation ready we have to set the animation controller up so we can control when to play the animation and how.

Show the animator tab by clicking Window > Animation > Animator and click the *Aparment_Door* prefab so you can manipulate the animation state machine of the door.

We are going to create a new state and call it *Idle*. Then make the layer default one and uncheck the "Write Defaults" checkbox.

Next step is create a transition from the *Idle* state to the already existing *Door* state and uncheck the "Has Exit Time" checkbox from the transition. Also uncheck the "Write Defaults" checkbox from the *Door* state.

Finally, the last steps we need to follow are to change the door state name to *DoorOpen*, and create a new Float parameter which we will call *direction*. That param will be used to control whether the animation has to be played regularly or backwards. To make that happen we must go to the *DoorOpen* state and under *Speed* parameter there will be a greyed *Multiplier* parameter. That is what is going to allow us to reverse the animation, so check the "parameter" checkbox and the drop down should show the "direction" parameter we created earlier.

To check that everything is working fine we have to play the game and check that the *Idle* state is played on loop.

{% youtube 0f3n2QtMy4M %}

> Access all the step changes [here](https://github.com/alesanmed-blog-posts/unity-animate-doors/tree/step-1).

## Step 2 - Making generic actionable objects

Now it's time to code!

We are going to start creating a generic type for defining actionable objects. Since we are going to implement not only standard actionable objects but also actionable by key or by trigger (in upcoming posts), we'll create the foundations first.

We are going to use inheritance for that. If you don't know what inheritance is, is a technique that allows us to reuse and extend functionality by using parent-child classes. For a more extended a technical definition, head to [someone who knows more than me](https://en.wikipedia.org/wiki/Inheritance_(object-oriented_programming)).

![Diagram showing inheritance](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9s610c3xcl11t61enia4.jpg)

We will see each part in detail when we reach them. We are going to define an interface to make our life easier when it comes to detect an actionable object and also a specific class for each type of actionable object, extending the generic classes with specific functionality. And for the key objects specifically, we will take advantage of the [C# events](https://docs.microsoft.com/en-us/dotnet/standard/events/).

Start creating the following folder structure:
```
├───Scripts
│   ├───ActionableObjects
│   │   └───Generics
```

All of the files created in this section are going to be placed inside the `Generics` folder.

### Defining the interface

Our interface is an easy one:

```c#
public interface IActionableObject
{
    void Interact();
    string GetInteractionText();
    bool IsInteracterActive();
}
```

We have to define an `Interact` method which will be called when the actionable object is interacted and will trigger whatever behavior we want.

`GetInteractionText` returns the text to show when looking at the object (if any). For example: "Press A to open".

`IsInteracterActive` is for telling us if the object is interactive or not (for a key object, it will return false until the key is picked up).

### ActionableObject and InteractionText

Now that our interface is ready to be used to detect actionable objects, let's move to our other two generic components. First the InteractionText:

```c#
[RequireComponent(typeof(Text))]
public class InteractionText : MonoBehaviour
{
    private Text m_text;
    
    private string m_initialText;
    private string m_interactionText;

    private void Start() {
        m_text = GetComponent<Text>();

        m_initialText = m_text.text;
    }

    private void Update() {
        if(m_text.enabled) {
            m_text.text = $"{m_initialText}{m_interactionText}";
        }
    }

    public void SetInteractionText(string interactionText) {
        m_interactionText = interactionText;
    }

    public void Enable() {
        m_text.enabled = true;
    }

    public void Disable() {
        m_text.enabled = false;
    }

    public bool IsEnabled() => m_text.enabled;
}
```

The first thing we are going to do in this code is to forcefully bound the script to a GameObject with a Text component.

The class will have a referente to the `Text` component and two attributes for holding the initial text and the interaction text.

We are going to use the `Start` method to store the reference to the text component and setting the `m_initialText` to the text inside the component.

In the `Update` we are just setting the text of the component to a combination of the initial text and the interaction text. With that, we can add a common starting text to all interaction texts if needed. For example prepending "Press X to" to every interaction text.

The last thing is a function for changing the interaction text.

> NOTE: We could enhance the performance by having a flag indicating when the interaction text has changed and only updating it when changed, instead of in every `Update` call.

And now our ActionableObject:

```c#
public abstract class ActionableObject : MonoBehaviour, IActionableObject
{

    [Header("UI")]
    [Tooltip("The text that will appear in the overlay before interacting with this object")]
    [SerializeField] string interactionText = "open";

    [Tooltip("The text that will appear in the overlay after interacting with this object")]
    [SerializeField] string interactionTextReverse = "close";

    protected bool m_interacted = false;

    protected bool m_interactive= true;

    private void Start() {
        InnerStart();
    }

    protected abstract void InnerStart();

    public string GetInteractionText()
    {
        return !m_interacted ? interactionText : interactionTextReverse;
    }

    public void Interact() {
        InnerInteract();
        m_interacted = !m_interacted;
    }

    protected virtual void InnerInteract() {}

    public virtual bool IsInteracterActive() => m_interactive;

    public virtual void SetIsInteractive(bool isInteractive) => m_interactive = isInteractive;
}
```

This class must be abstract because we need to force a specific implementation for each type of actionable object.

First, we define some serialized fields that we will need. The `interactionText` is "open" by default (remember we are prepending it with the `m_initialText` of the `InteractionText`). The `interactionTextReverse` is the text to show to "reverse" the action (if needed). For example open/close.

The attributes `m_interacted` and `m_interactive` indicates whether the object is in the "interacted" state (interacted and not reverted) and if is interactive, respectively.

The `Start` method does nothing else than calling an abstract `InnerStart`. This is done in order to avoid a reimplementation of the generic `Start` without calling the super. This is useful when we need to force some behaviour in the parent class that cannot be overriden (as in the `Interact` function). Is some sort of [template method](https://en.wikipedia.org/wiki/Template_method_pattern#:~:text=The%20template%20method%20is%20a,class%20as%20the%20template%20method.).

`GetInteractionText` just returns the interaction text or the reverse one depending on the `m_interacted` flag.

`Interact` is similar to the `Start` function. It's called when the object is interacted and just calls the `InnerInteract` (which will hold the specific interaction code) and flips the `m_interacted` flag.

`IsInteracterActive` and `SetIsInteractive` are the getter and setter for the `m_interactive` flag.

And that's all for this step! We have just implemented our generic actionable objects! Let's move to the next step!

> Remember, you have the code developed in this step [here](https://github.com/alesanmed-blog-posts/unity-animate-doors/tree/step-2)

## Step 3: Standard actionable objects

Here we are going to implement the specific class for a standard actionable object. This means that it's not actionable by key, that triggers an animation and that is "reversible" (you can, for example, open and close a door). I consider this the standard since it was the first type of actionable object I implemented and will be the one which will be used more extensively in our game.

You may remember that we used some hardcoded strings in the editor when setting up the animations (a parameter called "direction"). Instead of using literals we are going to extract that to constants, to avoid having hardcoded strings across all our codebase.

Create a folder called "Constants" inside our "Scripts" folder and, inside it, create a file called "Animation":

```c#
namespace AnimateDoors.Animation
{
    public static class AnimationConstants {
        public static string ANIMATION_DIRECTION = "direction";
    }
}
```

We are isolating those constants in the `Animation` namespace to avoid collisions with the script name and the Unity Animation component. We are defining a constant called `ANIMATION_DIRECTION` with the direction parameter name.

Now let's create our `StandardActionableObject` class inside our `Scripts/ActionableObjects` folder:

```c#
[RequireComponent(typeof(Animator))]
public class StandardActionableObject : ActionableObject
{
    [Header("Animation")]
    [Tooltip("The animation state to play when interacting with this object")]
    [SerializeField] string animationState;

    protected Animator m_animator;

    protected override void InnerStart()
    {
        m_animator = GetComponent<Animator>();        
    }

    protected override void InnerInteract()
    {
        UpdateAnimation();
    }

    private void UpdateAnimation() {
        m_animator.SetFloat(AnimationConstants.ANIMATION_DIRECTION, !m_interacted ? 1f : -1f);
        m_animator.Play(animationState);
    }
}
```

First we are going to require the Animator component to trigger the animation.

Then, define an attribute to store the name of the state in the animator to play when the object is interacted.

The `InnerStart` will just store a reference to the `Animator`.

The `InnerInteract` will call to `UpdateAnimation` and, that one, will set the "direction" parameter to 1 or -1 depending on the `m_interacted` flag. Remember that the `direction` controls the speed of the animation. If the object has not been interacted we are going to set the speed value to 1 and the animation will be played forward. If it has been interacted, the direction will be -1, so it will be played backwards. Right after that, we will play the state stored in the `animationState` attribute.

And that's all! Easy, right? That's why we invested some time in the parent class, so the children are easy to implement.

> Access the code of this step [here](https://github.com/alesanmed-blog-posts/unity-animate-doors/tree/step-3).


Let me stop here for now and finish the implementation in a following post. In the next one we will implement the specific classes for each type of actionable object and set everything up in the editor to use it.

Please feel free to make any suggestions and propose improvements to the code in this post, as well as any doubt you might have! Stay tuned for the next post in which we will finish implementing our key objects!