The Scales of Sauthvia

Try it right now, in browser!

A snippet of gameplay fairly late into a run. I must consider my current upgrades carefully when picking a new one.

The Scales of Sauthvia is a game jam game, created over a 4 day period for GMTK Game Jam 2024. The game is an arcade game inspired by my father’s love for the game Galaga, with some modern twists in the form of roguelike power ups.

The gameplay loop is deceptively simple, but addicting; much of the work was focused on nailing down the “feel” or “juice” of the game. The sound effects, the way the screen shakes as the, and the weight of the dragon moving up and down, all contribute to a cohesive and satisfying experience.

Genre: Arcade Roguelike
Timeline: Created for GMTK Game Jam 2024, which lasted from August 16th 2024 to August 20th 2024
Engine: Unity 2022
Platform: WebGL, Windows
Team size: 3
Responsibilities: Concept, Programming, Design
Itch Page: https://buildsgames.itch.io/the-scales-of-sauthvia (Play in browser!)
Source Code: https://github.com/WillyBMiles/Scales-of-Sauthvia

Highlights

  • Enemies were designed with a breadth-first mindset, allowing many enemies to be designed relatively easily using splines and inspector tools.
  • Upgrades have a simple but expandable polymorphic system that prevents unnecessary boilerplate and allows a wide variety of upgrades with relatively little coding.

Challenges

  • For a game jam, the development went smoothly. We kept the scope in mind and had an idea that was executable easily in the time frame. Having realistic expectations was absolutely vital to finishing this project on time.
  • The biggest challenge was getting enough content to be satisfied. Game jams generally require less to feel “complete” due to the short expected playtime. However, we wanted the game to be fun with longer playtimes and over multiple playthroughs. We accomplished this goal with our wide variety of upgrades which was relatively easy due to our straightforward backend.

Spotlight: Enemy Design

Each spline represents a path the pixie can follow on their way to attack the player or enter the battlefield.

This mess of splines represents all the different ways that the pixie enemy can move across the screen. The pixie follows the spline using a Spline Animate component. There are two types of splines here: Entrance splines, that are only used when the enemy enters for the first time; and Charge splines which are used whenever the enemy attacks. The highlighted spline is a Charge spline.

The enemy chooses one of the possible splines at random for the situation. All of the Charge splines loop back on themselves because at the end of a Charge enemies return to the formation. If there was no tail then the enemies would pass through the battlefield backwards which would feel unfair to the player (enemies should come from the front!), this was an easy way to prevent that.

The particular shape of the movement is inspired by the enemy ships in Galaga that loop around as they charge you.

This allows all enemies to share pretty much the same scripts as each other while feeling and acting quite different from each other.

Also inspired by Galaga, the enemies fly in formation and the formation itself triggers when particular enemies charge, usually only one or two at a time to allow the player time to react.

A single pixie breaks formation to charge the player.

A look at the inspector for one particular enemy. BumpyDamage is a simple script that deals damage to the player when they touch them. This is separate form the enemy script because the same functionality needed to be applied to the towers.


Spotlight: Roguelike Upgrades

C#
using UnityEngine;

public class Ability : MonoBehaviour
{
    public string DisplayName;
    public string DisplayDescription;

    public bool repeatable; //Can this ability be taken multiple times?

    protected static Player player { get
        {
            if (p == null)
            {
                p = FindObjectOfType<Player>();
            }
            return p;
        } }
    static Player p;

    public static PlayerController playerController { get
        {
            if (pc == null)
            {
                pc = FindObjectOfType<PlayerController>();
            }
            return pc;
        } }
    static PlayerController pc;

    protected int count = -1;
    protected bool CheckCount(int number)
    {
        if (count == -1)
        {
            count = Random.Range(0, number); //Initialize to a random value to prevent "sync up"
        }

        count++;
        if (count >= number)
        {
            count = 0;
            return true;
        }
        return false;
    }
    public void Add() //Called when this ability is taken
    {
        OnAdd();
    }    
    
    protected virtual void OnAdd()
    {
        playerController.OnLaunchAttack += OnAttack;
        Projectile.Hit += OnProjectileHit;
        player.OnHit += OnGotHit;
    }

    protected virtual void OnProjectileHit(Projectile projectile) //Called when one of your attacks hits
    { }

    protected virtual void OnGotHit() //Called when you get hit
    { }
    public virtual void OnAttack(Projectile projectile, bool special) //Called when you attack
    {  }

    private void OnDisable()
    {
        Projectile.Hit -= OnProjectileHit;
    }
}

A fairly straight forward base class with several virtual methods to serve different types of abilities.

We knew we wanted upgrades in the game. The specific system was inspired specifically by the upgrades in the 2v2 Arena mode of League of Legends, but this “pick one of three” style of upgrades is quite common in the Roguelike genre.

The system is designed such that adding new upgrades is a breeze. Each upgrade is a tiny derived class that contains just the code that runs when the upgrade is picked. In the inspector the description and title are filled in and the upgrade is pretty much complete.

Abilities are MonoBehaviours that stick around for the rest of the game. The Ability class has a number of virtual methods to provide different functionality to derived classes representing the common types of abilities for design. For example, OnAdd is called once when the ability is earned, so it works well for simple abilities like a permanent increase in health or a speed boost.

OnAttack is a particularly common one callback. We wanted to keep the single action button that is common in early arcade games, so if we wanted a triggered ability it would trigger after a certain number of attacks. If it was straightforward, like firing an additional explosive fireball, it would trigger after 3 to 8 normal attacks. Powerful effects, like attack blocking shields or wide breath attacks, would be gated behind 20 or more normal attacks.

C#
using UnityEngine;

public class SuperSize : Ability
{
    protected override void OnAdd()
    {
        base.OnAdd();
        playerController.transform.localScale += new Vector3(.4f, .4f, .4f);
        player.maxHealth += 4;
        player.health += 4;
    }

}

An ability that triggers once when you take the ability.

C#
using UnityEngine;

public class OverwriteAttack : Ability
{
    public int number;
    public GameObject newAttack;

    public override void OnAttack(Projectile projectile, bool special)
    {
        base.OnAttack(projectile, special);
        if (!special) //Special attacks cannot chain
        {
            if (CheckCount(number))
            {
                playerController.Attack(overrideChecks:true, //Attack even if it's on "cooldown"
                    specialAttack:true, newAttack);
            }
        }
    }
}

The most used ability class in the game, in terms of the number of unique upgrades that use it. It replaces the basic fireball attack with a different one after a certain number of attacks (represented by the “number” variable).

A full run of The Scales of Sauthvia.