BBBBBB, a VVVVVV inspired game written in C with SDL2

02/23/2020
BBBBBB game play gif

Recently, indie hit VVVVVV was open sourced. VVVVVV was one of those games that always inspired me to want to make games on my own. It has a very simplistic design that focuses on fun game play and pleasing retro sounds and graphics. Many developers reacted with horror when they reviewed the source code. The most egregious example being a many hundred switch case. This really made something click for me about game development, all it just takes will power and vision. So using this inspiration and started a two week long side project to make my own version of VVVVVV called BBBBBB.

The Game Play

BBBBBB is a simple game where you move the player from the starting point to the goal. The goal is a white diamond shape. Once you reach the goal you will progress to the next level automatically. Players can press the space bar to flip gravity and turn upside down. If you fall off the top or bottom you will be reset to the start position. When you reach the final level the background will be blue fire! I referenced the PSX Doom Fire technique that Fabian Sanglard covered here.

Inspirado

In addition to being inspired by Terry Cavanagh I also really enjoy fantasy consoles like PICO-8. I like the idea of having a small sandbox to build your game in. After having experimented with PICO-8. I wanted build my own customized sandbox for games. I figured the best way to work towards that was to just build games and see what patterns emerge. So BBBBBB was built with a future fantasy console in mind. The resolution is fixed to 640x480. All of the assets in BBBBBB are generated by the code. The game is also its own level editor; by pressing CTRL-E you enter edit mode. In edit mode you can change the platform locations and move the player start and goal locations. Similarly, I made a small tool for drawing sprites that allows quick generation of sprite data.

Graphics

The game is a fixed resolution of 640x480 pixels. The 640x480 is then subdivided into 32 columns and 24 rows. Each cell of the 32x24 grid represents game block. There are 4 different types of game blocks: nothing, platform, start and goal. There is only ever a single start and goal at a time and many different nothing blocks and platform blocks. The player is a special game entity that is sized at 10x10 pixels. Drawing of the individual entities involves subdividing their block cells into an 8x8 grid. Each block type is a drawn as single color using a something I called a grid pattern. The grid pattern is simply the indices of the active pixels in the linearized array for each block. To help with determining what those indices are I wrote a simple drawing tool. The drawing tool allowed me to click on the squares of the 8x8 grid that should be active and then press space bar to view all of the active indices. I also wrote two different background layers for the game, a simple star field and the aforementioned PSX Doom Fire (with a modified color palette).

Drawing Grid Patterns

void draw_grid_pattern(int *pattern, int len, int stride, int size, int x, int y, int r, int g, int b)
{
	int i;
	SDL_Rect rect;

	rect.w = size;
	rect.h = size;

	SDL_SetRenderDrawColor(renderer, r, g, b, 0xFF);
	for (i = 0; i < len; i++) {
		rect.x = x + ((pattern[i] % stride) * size);
		rect.y = y + ((pattern[i] / stride) * size);
		SDL_RenderFillRect(renderer, &rect);
	}
}

Game Loop

The game loop is divided into three sections: handling input, updating the game state and rendering the graphics. Initially I didn't present that game via VSYNC so the game speed was un-playably fast on a modern computer. I added SDL_RENDERER_PRESENTVSYNC to the renderer flags to ensure that SDL_RenderPresent delays to make the game 60FPS. I wasn't satisfied having the renderer be in control of the game speed to I added timing to the game update function. The timing uses the SDL_GetTicks function to ensure that the update function is called no more than 60 times per second. This does not apply to input however, so if the game is presented without VSYNC in theory the controls could be operated at whatever frame rate the CPU could support. The game world is still only updated at 60 frames per second. Inside the update function the gravity is applied to the player and collision checks are run. The update function also determines if a sound effect needs to be played and if so plays it at that moment.

Input and Controls

One area I focused on when making this game was having good feeling controls. In the past I had used SDL's event system to detect key down and key up events to mixed results. Rather than rely on the SDL event system for keyboard events I opted to poll the keyboard state manually. At first I only queried the keyboard state for the current frame which worked perfectly fine for key down events that have an infinite repeat, i.e. left and right player movement. This did prove problematic for the gravity flipping since it is a toggle. The result was that before you released the key you had already flipped an unpredictable amount of times. This had a very bad game feel. I tried many different solutions to this that involved globals before I finally landed on simply keeping the previous frames keyboard state and memory. With the last frames keyboard state I could easily say, "only flip if the space bar is down this frame and it wasn't down in the previous frame." I ended up creating two different input handlers for play mode and for edit mode. The edit input handler is the only portion of the program that polls the mouse state.

Saving the Last Frame's Keyboard State

SDL_PumpEvents();
const Uint8 *state = SDL_GetKeyboardState(NULL);

...

if (state[SDL_SCANCODE_SPACE] && !last_kstate[SDL_SCANCODE_SPACE]) {
	if (!flipping) {
		should_flip = 1;
		flipping = 1;
	}
}

...

memcpy(last_kstate, state, 512);

Sound

Rather than user SDL_Mixer to playback pre recorded sound files I opted to generate the sound on the fly using SDL's sound API. Doing this was a little more complex than I initially realized since it requires you to specify all of the details for generating PCM audio. I kept it simple and created a function that can play one of 4 square wave tones for a given duration. To pick the 4 tones I experimented with different values to get a wide range of sound effects. I utilized SDL's threading capabilities to apply the duration to a given sound effect.

Closing Thoughts

Overall I am pretty happy with the results. Since this is just my take on someone else's game I didn't want to take this super far but I had a lot of fun working on it. Looking back there are a few things I know could be done better and I plan on tackling them with my next game programming project. One thing I would improve is the state management and globals. They are not organized in a way now that makes it super clear what causes what to happen in the game. I also didn't bother with any UI code like a title and game over screen. Checkout the code on github!