Implement a Splash Screen Using a Finite State Machine in Unity

Share the Post

In this tutorial, we will implement a splash screen using a Finite State Machine in Unity. This is a continuation of the tutorial from Part 1. Here, 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 this 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 in Part 1. 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.


Proceed to Part 3 where we use the FSM and apply it to a complex Unity project which handles multiple animation states of a 3D animated character to create Player Controls. See a video of the example implementation.


Proceed to Part 4where we will use delegates to create a Finite State Machine. Thereafter we will demonstrate the use of the Finite State Machine by creating a simple key press based application in Unity.

Proceed to Part 5 where we will use the delegate based FSM created in Part 4 to create an enemy NPC behaviour that handles multiple animation states of a 3D animated character.

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 *