itch.io is community of indie game creators and players

Devlogs

Developing Chimera Rampage and Learning To Create Music & SFX : Post 2

Chimera Rampage
A browser game made in HTML5

This is the second devlog concerning the creation of my entry, "Chimera Rampage", to the "Learn You a Game Jam: Pixel Edition". The first post can be found at this link: https://high-lord.itch.io/chimera-rampage/devlog/548342/developing-chimera-rampa... This entry will focus on:

  • Game design decisions relating to the enemies
  • The code used for enemy spawns
  • Advances on learning about creating music and SFX

Designing the Enemies

The chimera has a varied set of abilities and I wanted for the player to have reasons to switch between them. There  were three situations that I wanted to prompt in particular: using area of effect abilities to hit as many enemies as possible, using a long-range ability to hit a stationary target that's behind a lot of enemies, and having to disable a buff. These types of goals give the player more to do in a combat scenario, it makes the situation more engaging by letting the player be tactical with a fairly simple toolset.

For this, the following three enemies were designed:

  • Twig
    • A low health, low damage enemy that chases the player
    • Many of them spawn, leading to large clumped horde
  • Dryad
    • A medium health, high damage enemy that stands still and fires projectiles at the player
    • Fewer of these spawn, but should be prioritised to avoid a bullet hell forming
  • Myconid
    • A high health enemy that cannot attack, but gradually heals nearby enemies
    • Few of these spawn, but must be prioritised, otherwise they will stack up and make killing enemies very difficult

This is a simple roster of enemies that adheres well to the player's abilities. The dragon's breath provides a damaging AOE to deal with a horde of twigs, while its ember easily takes out a stationary dryad from afar. The goat's wail acts as a useful debuff, disabling the myconid's healing and destroying all active dryad projectiles, while the ram can be used to break through a group of twigs if the player is surrounded. The player constantly switches tactics as the enemies on the field change, meaning an engaging gameplay loop where there isn't a singular tool that's used for every situation.

To cap off, there are many different resources for game design. Particularly good YouTube channels include Game Maker's Toolkit (https://www.youtube.com/@GMTK) and The Architect of Games (https://www.youtube.com/@ArchitectofGames), but I would personally recomend the book "The Art of Game Design: A book of lenses" by Jesse Schell (https://www.amazon.co.uk/Art-Game-Design-book-lenses/dp/0123694965) which is a very good read if you're interested in the how the various aspects of a game fit together.

Coding an Effective Enemy Spawner

For enemy spawning, I wanted to do something slightly interesting. In previous games that I made I used a wave-based system for spawning enemies. Basically a scriptable object would contain the enemies that would spawn, with some sort of system progressing through a list of waves as the game progressed. For Chimera Rampage I wanted a degree of randomness that meant a particular combat could be different on each playthrough (partially for my sanity when doing playtesting) and I wanted a spooling system that cut down on the amount of instantiations the game would have to do.

The script to do this was called "EnemyManager", and its primary functionality of interest was the constantly updating spawn chances it calculated. For a particular combat encounter, there at most three enemy types, as well as the number of each that have to spawn and be dealth with before the encounter ends. The script keeps track of the enemies that need to spawn for the encounter, as well as the number of enemies already spawned. It uses these values in the following method:

private bool CalculateSpawnChances()
    {
        int encounterTotal =
            (encounterTwigs + encounterDryads + encounterMyconids)
            - (spawnedTwigs + spawnedDryads + spawnedMyconids);
        if (encounterTotal <= 0)
        {
            return false;
        }
        twigSpawnChance = (float)(encounterTwigs - spawnedTwigs) / encounterTotal;
        dryadSpawnChance = (float)(encounterDryads - spawnedDryads) / encounterTotal;
        myconidSpawnChance = (float)(encounterMyconids - spawnedMyconids) / encounterTotal;
        return true;
    }

This method is called each time an enemy needs to be spawned, and utilises the percentages of the "spawnChance" variables to decide which enemy to spawn next. This means that a particular encounter could be different on each play, where sometimes a large cluster of twigs will spawn followed by dryads, or myconids might show up first to be easily dealth with. It also means that when a particular enemy spawns, the other enemy types have a higher chance to spawn, leading to a diversity of incoming enemies. Furthermore, the method returns a bool. This returns false if the spawned enemies equal the encounter total, which then leads to the spawning corouting stopping.

The entire script is available for you perusal on the project's Git page, which can be found on the game's page and here (https://github.com/Comedycaper64/Chimera-Rampage).

A Continued Venture Into Audio

Following the previous post's description of me finding a way to record music onto my PC, I then had to work out how I was going to make sound effects for my game. I consider myself (strangely enough) fairly good at making peculiar noises, but I needed some sort of software to make those noises actually sound like effects that fit into a game. This meant learning that...

4. Adding effects to audio clips is essential for them to be useable.

Particularly it's important to: find a correct-sounding pitch for the sound effect you're going for, ensure the audio does not "peak", remove any background noise from audio clips, and adjust the Bass and Treble values to match the intended impact of the sound you want. A useful video I found for the purpose of learning these techniques was the following, titled "How To Make Sounds for Games".

This also led to me learning that:

5. Audacity is a good tool for dealing with audio.

Audacity is a free, open source program that gives the user a suite of tools to interact with audio. It is capable of doing all of the mentioned above and more. It can be found at https://www.audacityteam.org/download/ . I used Audacity to make all of the sound effects in my game, turning mouth noises into sounds that arguably worked for the situation they were used in. It's a fairly intuitive tool that doesn't require much training to properly utilise. Having dealt with the sound effects I then had to compose some music for the game and came to another realisation:

6. It would be difficult to learn composing music while working on a game jam.

So I attempted to settle for the next best thing. I had the ability to come up with short, primitive song samples and record them onto my computer. I then decided to leverage my programming ability to make a somewhat interesting music system for the game. My idea was for each Chimera head to correspond to an instrument, such that the music in-game would reflect what head was actively being used by the player. To that end, I recorded a series of short music samples in three different instruments. 

These samples were then put into three seperate lists in Unity. A Sound Manager script would then have a coroutine which would play a random sample from the appropriate sample list, yielding for the currently playing clip's length. This can be seen below:

private IEnumerator PlayAudioSample()
    {
        List<AudioClip> sampleList;
        switch (stateMachine.activeHead)
        {
            case ChimeraHead.Dragon:
                sampleList = dragonSamples;
                break;
            case ChimeraHead.Lion:
                sampleList = lionSamples;
                break;
            default:
            case ChimeraHead.Goat:
                sampleList = goatSamples;
                break;
        }
        AudioClip sampleClip = sampleList[Random.Range(0, sampleList.Count)];
        audioSource.clip = sampleClip;
        audioSource.Play();
        float sampleLength = sampleClip.length / playbackSpeed;
        sampleLength = sampleLength - playbackOffset;
        yield return new WaitForSeconds(sampleLength);
        StartCoroutine(PlayAudioSample());
    }

This meant the game would have a dynamic music system that was based on short music clips that I was able to compose, hopefully making up for my lack of talent in the area of music composition.

Denouement

This post will be followed by one further entry that will mention the remaining interesting parts of the game, as well as further learnings in the audio department. Thank you for reading, and I hope you gleaned something from this.

Leave a comment