Implementing a Finite State Machine Using C# in Unity – Part 2

Share the Post

In this tutorial, we will learn about Finite State Machines and implement a Finite State Machine using C# in Unity. We will then apply the implemented FSM in two different scenarios to illustrate the concept.

The tutorial is divided into three parts.

Part 1 introduces a Finite State Machine and implements a generic Finite State Machine in C#.


Part 2 uses the Finite State Machine created in part 1 and applies to a Unity project in a simple straightforward UI implementation of a Splash Screen.


Part 3 uses the same Finite State Machine and applies to a complex Unity project which handles multiple animation states of a 3D animated character. We will target the build for this implementation for a touch screen controlled devices such as Android or iOS (coming soon). See a video of the example implementation.


Read Also: Solving 8 puzzle problem using A* star search

Download the 8 Puzzle Unlimited App from Google Play.



Applying the Finite State Machine to Create a Splash Screen in Unity

This is a continuation of the tutorial from Part 1. If you have not read Part 1 of this tutorial then I strongly suggest that you read Part 1 first. In this Part 2 of the tutorial, we will apply the FSM that we created in Part 1 into a real Unity application.

Finite State Machine in Unity – Implement a Splash Screen

In the first demonstration, we will create a simple one-way state machine to implement a Splash Screen.

The state diagram for our splash screen is given in the diagram below.

It starts with the FadeIn state where our logo is displayed gradually from completely transparent to a completely opaque state. After a certain amount of duration, it transits to the PlayAudio state where we play the Splash Audio. After the audio is completed we fade out the logo in FadeOut state from completely opaque to a completely transparent an exit the state machine.

Create the Unity Project

Create a new Unity 3d project and name it FSM or any other name that you fancy. Rename the default scene to FSMSplashScreen.

Create three folders in Assets called Audio, Scripts and Textures. Download the Sample Logo and the Sample Audio. Or if you have your own audio and image then you can use that as well.

Put the SampleLogo.png image in the Textures folder and the SampleAudio.wav audio in the Audio folder.

Convert the SampleLogo.png to a Sprite and then drag and drop into the scene. Reset the Transform to make set all the initial values to be at the origin with no rotation and unit scale.

1000

Adjust the camera position to be a 0, 0, -7 so that the whole sprite is visible.

Set the clear color for the camera to be black.

Scripts

Select the MainCamera and add a New Script component to it. Name the script to be SplashScreen.cs

Go to Assets and move this script to the Scripts folder. Double click on the SplashScreen.cs and open in your favorite editor. I prefer Visual Studio.

Add two variables. One for the sprite and one for the audio.

public GameObject spriteLogo;
public AudioClip audioLogo;

Now go to the Unity editor, select MainCamera and drag and drop the SampleLogo sprite to the Sprite Logo field in the SplashScreen script in the Inspector window.

Similarly, drag and drop the SampleAudio to the Audio Logo field.

Add a new component called AudioSource into the MainCamera.

The Finite State Machine

Create a file called FSM.cs and copy and paste the code that we created for our reusable FSM. We will use this State as the base class for our new concrete state classes associated with SplashScreen.

Looking at the state diagram we find that there are three states for splash screen. We represent their IDs as enumeration types.

/* 
 * We create enums to represent the 
 * unique ids of the different states.
 */
enum SplashStates
{
    FADE_IN = 0,
    PLAY_AUDIO,
    FADE_OUT,
}

We will implement the concrete state classes in a moment. But before that let’s set up the necessary variables and implementations for the SplashScreen script.

Firstly, we will need a variable for the FSM. We then create a new instance of FSM and subsequently call the Update method of the FSM in the Update method of the MonoBehaviour script.

See below for the implementation.

public GameObject spriteLogo;
public AudioClip audioLogo;

/* 
 * We create enums to represent the 
 * unique ids of the different states.
 */
public enum SplashStates
{
    FADE_IN = 0,
    PLAY_AUDIO,
    FADE_OUT,
}

private FSM m_fsm = new FSM();

// Start is called before the first frame update
void Start()
{
    /*
     * Create the three states and add
     * to the fsm.
     */


    /* Set the current state (initial state)
     * of the FSM.
     */
}

// Update is called once per frame
void Update()
{
    /*
     * We call the FSM update here
     */
    if(m_fsm != null)
    {
        m_fsm.Update();
    }
}

We will also add one more function which will be called when the FSM exits. Typically this will be handled by your game by either loading a new scene or to show some UI. For us, we will put up a Debug.Log.

/*
 * Exit function to handle the end of the FSM.
 */
public void Exit()
{
    Debug.Log("Splash screen with FSM has exited.");
    m_fsm = null;
}

Well, we are almost done. Now, all we need are the actual states. Let’s implement them.

FadeIn and FadeOut States

Both these states have similar functionality which is to change the opacity of the sprite. In FadeIn we will go from 0 (completely transparent) to 1 (completely opaque) in a certain duration of time whereas in FadeOut we will go from 1 to 0 in another duration of time. So both these states can be represented by one class.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Patterns;

public class Fade : State
{
    public float Duration { get; set; } = 2.0f;

    private float deltaTime = 0.0f;
    private FadeType m_fadeType;
    private SpriteRenderer m_spriteRenderer;
    private SplashScreen m_splash;

    public enum FadeType
    {
        FADE_IN,
        FADE_OUT,
    }

    public Fade(FSM fsm, SplashScreen splash, FadeType fadeType = FadeType.FADE_IN) : base(fsm)
    {
        m_splash = splash;
        m_spriteRenderer = splash.spriteLogo.GetComponent<SpriteRenderer>();
        m_fadeType = fadeType;
    }

    public override void Enter()
    {
        deltaTime = Time.deltaTime;
        base.Enter();
        switch (m_fadeType)
        {
            case FadeType.FADE_IN:
                Debug.Log("Entering: FadeIn State");
                break;
            case FadeType.FADE_OUT:
                Debug.Log("Entering: FadeOut State");
                break;
        }
    }
    public override void Update()
    {
        deltaTime += Time.deltaTime;
        if (deltaTime > Duration)
        {
            switch (m_fadeType)
            {
                case FadeType.FADE_IN:
                    int nextid = (int)SplashScreen.SplashStates.PLAY_AUDIO;
                    State nextState = m_fsm.GetState(nextid);
                    m_fsm.SetCurrentState(nextState);
                    break;
                case FadeType.FADE_OUT:
                    m_splash.Exit();
                    break;
            }
        }
        if (m_spriteRenderer != null)
        {
            switch(m_fadeType)
            {
                case FadeType.FADE_IN:
                    m_spriteRenderer.material.color = 
                        new Color(1.0f, 1.0f, 1.0f, deltaTime/Duration);
                    break;
                case FadeType.FADE_OUT:
                    m_spriteRenderer.material.color = 
                        new Color(1.0f, 1.0f, 1.0f, 1.0f - deltaTime/Duration);
                    break;
            }
        }
    }
}

PlayAudio State

The second state in the splash screen application is the PlayAudio. In this state the AudioClip is played and the transits to the FadeOut state.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Patterns;

public class PlayAudio : State
{
    public float Duration { get; set; } = 1.0f;
    private float deltaTime = 0.0f;
    private SplashScreen m_splash;

    public PlayAudio(FSM fsm, SplashScreen splash) : base(fsm)
    {
        m_splash = splash;
    }

    // lets discuss what generic functions need to be there in a State.
    public override void Enter()
    {
        deltaTime = Time.deltaTime;
        m_splash.GetComponent<AudioSource>().PlayOneShot(m_splash.audioLogo);
        base.Enter();
        Debug.Log("Entering: PlayAudio State");
    }

    public override void Update()
    {
        deltaTime += Time.deltaTime;
        if (deltaTime > 1.0f)
        {
            int nextId = (int)SplashScreen.SplashStates.FADE_OUT;
            State nextState = m_fsm.GetState(nextId);
            m_fsm.SetCurrentState(nextState);
        }
    }
}

Create these States and Add to the FSM

We will now create these states and add them to the FSM. We will also set the FadeIn state as the initial current state.

    // Start is called before the first frame update
    void Start()
    {
        /*
         * Create the three states and add
         * to the fsm.
         */
        m_fsm.Add((int)SplashStates.FADE_IN, new Fade(m_fsm, this));
        m_fsm.Add((int)SplashStates.PLAY_AUDIO, new PlayAudio(m_fsm, this));
        m_fsm.Add((int)SplashStates.FADE_OUT, new Fade(m_fsm, this, Fade.FadeType.FADE_OUT));



        /* Set the current state (initial state)
         * of the FSM.
         */
        m_fsm.SetCurrentState(m_fsm.GetState((int)SplashStates.FADE_IN));
    }

Save the scripts, save the scene and click Play in the Unity editor. You will see the splash screen transitions. Of course, the look from an artistic point of view is not there. But you get the idea.

Conclusion

In this tutorial, we have created a simple Splash Screen using the FSM that we created earlier. This is in no way the only way to create a Splash Screen. This is also perhaps not the best way to create a Splash Screen. However, this tutorial serves it well to see how we can use an FSM to implement simple UI in our game.

Continue to Part 3 of this tutorial to see how we can use the same FSM to create a slightly more complex character control in a third person style shooter game. See the video below to see what we will create in Part 3. We will target the build for Part 3 to be for touch screen controlled devices such as Android or iOS (coming soon. I am in the process of writing now).

References

  1. https://en.wikipedia.org/wiki/Finite-state_machine
  2. https://gameprogrammingpatterns.com/state.html
  3. https://gamedevelopment.tutsplus.com/tutorials/finite-state-machines-theory-and-implementation–gamedev-11867

Leave a Reply

Your email address will not be published. Required fields are marked *