Froglore Technicals #1: Development Environment and Game Engine


This is the first technical blog post about Froglore: Turning Tides, a game I am making for the Game Boy Color. I will cover the resources and tools I use, and I will give a quick introduction to the game engine.

Development environment

I am writing the game in assembly, building it up from scratch, and using a few tools for compilation and graphics creation. When I say I am building the game up from scratch, I mean I am not using any existing code or libraries, with the exception of hardware.inc. I am taking this approach because I want to be able to get as much out of the Game Boy Color as possible, and writing each component of the game engine with just one game in mind should help me do that. It also means I don't need to go through someone else's code if I want to change something low level, which is a bonus.

For tooling, I use vim for my text editing and the rgbds assembler, linker and fixer for building the rom. Graphics have currently been created with the Game Boy Tile Designer (GBTD) and Game Boy Map Builder (GBMB), though the tools work well their capabilities are limited and I'll be looking for something better when it is time to make more graphical assets for the game.

When I have written a new piece of code,  I build the rom and  load it up in Emulicious. Emulcious has great debugging features that let me set breakpoints and step through the new code. It is way more enjoyable to deal with issues this way than by staring at assembly. Once the inevitable bugs are exterminated I load the rom onto a flash cart and make sure it runs on my Game Boy Color.

Introduction to the game engine

At the moment, I have a game engine that can transition between game states, handle button/directional input, and play very basic "songs". I will go through the first two points and leave the audio engine to its own post.

The purpose of the game engine is to provide a system that lets me easily swap between different animation and game state functions while having as small a footprint as possible. This boils down to dealing with function pointers within a simple game loop that runs once per frame:

  1. Wait for the screen to finish updating so we can edit graphics memory
  2. If the animate flag is set, load the address of the current animation function and run it
  3. Load the address of the current game state function and run it
  4. Go back to step 1.

I chose to go with function pointers so that I can separate the game logic from the engine logic. The engine here doesn't care or need to know what the current game state is, I can go and add as many game states as I like and this code won't need to be updated.

Game states

Each game state has the following components:

  • One init function
    • Called to transition to this game state.
  • Any number of game state functions
    • Called every frame to perform the "logic" for the game state.
  • Any number of animation functions
    • Called at the start of each frame when the animate flag is set.
  • Game state variables
    • Values that change, such as player health, current menu item selected, etc.
  • Game state constants
    • values that won't change, such as maps, item names, etc.

Let's have a look at the intro animation game state, it consists of the following:

  • One init function
    • Called once the graphics and audio systems have been initialised on boot.
  • One game state function
    • Updates the current animation function in the engine in order to play the intro scroll, and handles skipping to the title screen.
  • 41 animation functions
    • 32 for animating the intro scroll.
    • 9 for transitioning straight to the title screen.
  • Game state variables
    • next_keyframe
      • The address of the next keyframe to play
    • frame_counter
      • A counter of how many frames the current keyframe has been played for
    • animation_skipped
      • A flag that is set when user presses a button to skip to the title screen
  • Game state constants
    • keyframe_list
      • A list of animation functions and how many frames to wait before moving on to the next one
    • skipping_keyframe_list
      • A list the same structure as above, but much shorter, for skipping to the title screen

That's a lot of animation functions. Because there is such a short window of time for editing graphics memory each frame the animation has been cut up into little segments that are executed as needed, each one doing a few things like printing two lines of text, animating the waves,  scrolling the screen and then exiting. All the "thinking" is left up to the game state function.

The game state function is pretty straightforward, and doesn't do much other than checking when an animation has been played for as many frames as specified and loading up the next one. If the player presses a button to skip the animation it changes to a shorter animation It follows a similar philosphy to the core game engine, the gamestate function here doesn't care or need to know about what the animation functions are currently doing, this made it easier for me to build up the animation by writing the keyframes and editing the keyframe lists.

Once the last keyframe of the animation has been played, the init function for the title screen is called and the intro animation game state is over.

You can see the intro animation in action on Twitter here.

Input handling

Button presses are handled by a function that game state functions can choose to call. The game engine keeps track of what the previous inputs where, this allows for button press and button release events to be handled. If press duration matters then the game state will need to have its own counters to track press length.

That's it for this technical post. As you can see i've implemented a pretty bare-bones engine and offloaded all the complex stuff to the game states. The intro animation plays smoothly, and the design decisions I have made are yet to trip me up. As I mentioned above, my next technical post will be about the audio system where it's pointers all the way down.

Leave a comment

Log in with itch.io to leave a comment.