A Short Tale of “Retrocade Framework”

A Short Tale of “Retrocade Framework”

Let’s talk a little about a couple of methodologies I use to structure the code of my games. My intention was to tell you more about the framework I made over the years but, really, it’s more of a collection of modules and classes than a fully-fledged framework for good reasons (not to mention it’s made in AS3 and we’re in the world of C# right now). Instead I’ll explain some concepts, modules and ideas that I am copying over to C# while working on Trans Neuronica.

Also a foreword of warning – this post is mostly text, if you are here just for Trans Neuronica progress, scroll to the very bottom for animated gifs!

Dividing the game into states

I call them States. Some other engines call them Scenes or Rooms. But the basic idea is to group a thematically common section of the game in a single place. Like, Title Screen is a state. Intro is a state. Game Over screen can be a state. Actual Gameplay is a state. A typical State class looks like this (in pseudocode):



class State{
    void Create(){
        // Called when the state is created and attached as the main stage
    }
    void Destroy(){
        // Called when a new state replaces this one
    }
    void Update(){
        // Called each step to update the logic
    }
    void Draw(){
        // Called each step to render the stage
    }
}

To show you an example, Monstro: Battle Tactics has the following states:

  1. Preloader – used by the browser version of the game, currently it’s just unreachable code.
  2. ImportProgress – for importing the progress from the old version of the game, it only ever appears once.
  3. DRMFreeUpdateCheck – an update check for the players who play the non-steam version of the game.
  4. Retrocade – Retrocade.ne splash video.
  5. Title – the title screen.
  6. Intro – the intro sequence.
  7. Ingame – the actual game.
  8. Outro – all of the outro sequences.

The benefits of this approach are huge:

  • Easier separation of concerns and code
  • Easier to group common functionalities and thus increase the readability
  • Less error prone as you can easily clean everything when a state changes and initialize what you need when it it created

The only real disadvantage I can think of is when, for some reason, you want to maintain two states at once. Let’s say, you want to switch from the game into a midtro and then back to the game again right where it stopped. If your self-contained states are completely cleaned between switches it won’t work.

Then again this can easily be solved by expanding your State Manager to support multiple states registered at the same time and options to Freeze and Unfreeze them at will (plus necessary methods in the respective classes).

Common window system

One thing I always like to have is some kind of Window Manager that is global and exists outside all States. It’s a very specific piece of functionality that works in a huge amount of games in a similar way:

  1. Add a new object at the top view which represents the window and pauses the underlying game.
  2. Make it possible to add new windows on top of the previous windows, making sure that all the previous ones do not update and do not take input.
  3. Make it possible to remove the top window and unlock the one immediately below it.

In many cases you have the same windows appearing in different states. And to ensure the maximum cross-usability it’s handy to have a separate system for all of this. Granted, instead of a global Window Manager you can create an instance of it in each state and work with that but from my perspective it’s a bit more work, I’d rather just have the code which shows the specific windows in my states.

Global audio management

I am willing to bet that whatever game you take, it’ll use at least two items from this list:

  • background music,
  • sound effects,
  • character voices,
  • environmental sounds/white noise.

Additionally a game really should have volume sliders. At least for Sound (SFX, voices and white noise) and Music. It’s worth thinking about this from the very beginning, because implementing volume changing late in the game is pain. Implementing anything that encompasses a lot of places in an almost finished product is always a huge pain.

Or heaven for refactorers.

As a side not, I’ll be the first to admit that even though all the previous audio managers I’ve written included music playback, too often I had to use a dedicated solution.. Case in point: Monstro: Battle Tactics has a music playback system which fades out the previous music while simultaneously fading in the next track. But if by any chance a third music should start playing before the first one stops, the first one’s fade speed up, the second one starts to fade out and the third one fades in.

Input management

By now you probably know what I’ll say – having a single place that handles the input is good. Having many places which do it is bad for expanding and modification. What’s also important is to, ideally, avoid directly referring to specific inputs in your objects. In Player don’t use Keyboard.IsButtonDown(Options.JumpButton) || Joypad.IsButtonDown(Options.JoypadJump).

A better way is a separate class which exposes specific input functions, likeIsButtonDown() or IsFireReleased().

Better yet, you can have an interface IInput, exposing methods IsDown()IsJustReleased()IsJustPressed(), and then use specific implementations: KeyboardInputMouseInputJoypadInput… RecordedInputSocketInputTwitchInput?

Separation of logic and rendering

The final big thing I want to tackle is whether to have each game object render itself, or whether to separate display and logic.

The easiest to code but worst to expand upon is that each logical object knows how to render itself and does it. It works in Unity, it works in Godot, it works in Game Maker, it works in Clickteam products. It works for an awful lot of cases awfully well but there are a couple of things to keep in mind here:

  • Rendering still has to be strictly dependent on logic, otherwise you might end up with elements animating when the game is paused which at best may look silly (or cool, like the floating clocks in Medievil) or at worst may completely desynchronize things and make them unplayable.
  • More code in the same class decreased the readability. This can be mostly mitigated by having separate Rendererclasses for each object and storing an instance of those in the logical objects, eg. Player with PlayerRenderer.
  • Tight dependency may make some things harder and more bothersome to implement, for example having multiple different ways to render the game, akin to Evoland.

Now don’ get me wrong, for the vast majority of games out there this is more than enough and it’s perfectly easy to keep it that way. But what about games like Mario Maker? You can chose vastly different graphic types for your levels. You could of course store the logic for everything in each object, or have PlayerRendererNESPlayerRendererSNESPlayerRendererDS… and give the Player object the specific renderer you want.

Or you can keep the rendering as a completely separate module of the game, making it much easier to manage and modify. This approach I actually learnt when working on King Dugan’s Dungeon Lite (aka Flash DROD) which required me to get very intimate with the original DROD’s source code. And DROD is a perfect example of a game which does the separation amazingly well. The whole game logic is a separate module, DRODLib, which the actual game frontend and rendering interfaces with.

One other great benefit is simpler optimization. Suppose you have a huge amount of mob enemies, and each enemy is responsible for drawing themselves. And it turns out you can have a single object which caches a lot of the mobs data together and does the same rendering using one-tenth of the draw calls. With separation it’s pretty natural to do things like that.

Monstro has separation of logic and display, but for different reasons. Starling framework uses a display list instead of manual draw calls to render everything, and thus it was easier for me to separately create and maintain the visual objects based on logical object’s state.

Trans Neuronica progress

And even though I’d like to chat even more about design, this post is already way too long, so let me just give you a quick update of TN progress:

A game screen where mouse input reacts correctly to the changes in scale and offset
Mouse input responding to viewport scale and offset!
Trans Neuronica game screen showing drawing paths
Drawing paths...
Trans Neuronica game screen showing removing paths
..and removing paths!

In the first gif you can actually see that the game correctly responds to viewport manipulation, something which initially I forgot to do… But there is real progress now, as you can see the rewrite supports drawing and removing paths. The background is still the ugly shade of white, but that’s changing soon.

[mc4wp_form id="444"]