Tag: Events

Clockwork TD Devlog #2: Gameplay Progression & FSMs

This is the second post in an ongoing series about the development of my thesis project, Clockwork TD.

In the last entry, we covered A* Pathfinding for the enemies to find their way to the grid center (check out my Hex Grid Framework here). A major part of my thesis was a Tower Defense game where the enemy spawn point changed throughout the course of the game, requiring the player to counter that somehow. Using the Hex grid math I learned at redblobgames.com, I came up with a system where the spawner will move around each ring of the game map–similar to a clock–working its way from inside to out, giving the player the longer stretch of enemy path they’ll need as the game goes on and the number of enemies multiplies.

In this early version, the spawner simply lasts for one turn at each location, then the tile it’s standing on changes from a path tile to a buildable tile, giving the player an extra location to place a tower. When the spawn tile moves to a new ring, it spawns at the location closest to the last ring’s opening for the first round, to get the player acquainted to the change in the game.

I also built upon the camera system I created for Accumulus, which builds on Unity’s Cinemachine follow camera to move an invisible GameObject around the map with the player’s keyboard input. Here I added a mouse scrollwheel function for Zoom, which simultaneously moves the camera down and forward. Camera control is a huge sticking point for me in top-down games like this, so making sure I nailed my camera was very important to me. The camera control hooks into the GameState and CursorState systems to ensure it’s only active during the correct game phases.

The GameState and CursorState systems are both ScriptableObject-based FSMs; each State has Entered() and Exited() events that game managers and other objects can subscribe to, such as deleting all objects OnNewGameStart, or changing the bottom buttons (not shown yet in these gifs) when the player clicks the OnPlayerPrepared button to start the enemy attack phase.

public abstract class GameState : ScriptableObject
{
    public event Action Entered, Exited;
    [SerializeField] private bool debug;

    public void Enter(GameData gameData)
    {
        if(gameData.CurrentState) gameData.CurrentState.Exit(gameData);
        
        gameData.CurrentState = this;

        if (debug) Debug.Log(this + "::Enter() (" + gameData.CurrentState + ")");
        OnEnter(gameData);
        Entered?.Invoke();
    }

    public virtual void Execute(GameData gameData)
    { }

    protected virtual void OnEnter(GameData gameData)
    { }

    private void Exit(GameData gameData)
    {
        if (debug) Debug.Log(this + "::Exit() (" + gameData.CurrentState + ")");
        OnExit(gameData);
        Exited?.Invoke();
    }

    protected virtual void OnExit(GameData gameData)
    { }

    public void Return(GameData gameData)
    {
        gameData.PreviousState.Enter(gameData);
    }
}

By inheriting from the base state, strongly typed states can be defined that allow each state to have unique behavior and hold any necessary data. For example, the MainMenu state can automatically pause and resume the game when exited and entered:

[CreateAssetMenu (fileName = "State_MainMenu", menuName = "Game States/Main Menu", order = 51)]
public class MainMenuState : GameState
{
    private float oldTimeScale;
    
    protected override void OnEnter(GameData gameData)
    {
        base.OnEnter(gameData);
        
        oldTimeScale = Time.timeScale;
        Time.timeScale = 0;
    }

    protected override void OnExit(GameData gameData)
    {
        base.OnExit(gameData);
        Time.timeScale = oldTimeScale;
    }
}

In the next entry, I’ll cover how this same technique is used to define each of the states the game cursor can be in, which allows for straightforward, durable code flexible enough to handle Building, Moving, Selling, Upgrading, Hovering Over, and Clicking On the game’s Buildables.

2023-07-14T19:44:08-04:00July 10th, 2023|Categories: Devlog, General|Tags: , , , , , , , |2 Comments

Updated Lightweight ScriptableObject Events in Unity

Since my original post on Lightweight ScriptableObject Events, I’ve made some minor variations to the version of code I’ve been using in production. I replaced the Lists with standard Event subscriptions (I like the way the += and -= syntax looks better than RegisterListener() and UnregisterListener(), plus it sticks out better so you know it’s an event subscription:

and updated sampled script:

Try it out and see which solutions works best for your project!

2023-06-03T19:22:34-04:00June 3rd, 2023|Categories: General|Tags: , , , , |0 Comments

About My Work

Phasellus non ante ac dui sagittis volutpat. Curabitur a quam nisl. Nam est elit, congue et quam id, laoreet consequat erat. Aenean porta placerat efficitur. Vestibulum et dictum massa, ac finibus turpis.

Recent Works

Recent Posts