Why Using NonBlockingGame?

NonBlockingGames is a Bridges abstraction that allows you to program simple games.

At the core, a game is concerned with four things:

  1. Reading Inputs (which buttons are pressed).
  2. Updating the game state using custom game mechanics (where is the protagonist).
  3. Rendering to the Screen (what the screen should look like).
  4. Maintaining a Framerate of 30 frame per seconds (getting a smooth experience).
NonBlockingGame in BRIDGES deals with Inputs, Rendering, and Framerate by making some assumptions about the kinds of game one can play. More precisely, the NonBlockingGame only enables games to be on a small grid (up to 1024 cells), to use only a few sprites (up to 256 predefined sprites), and to use only a few keys (up to 10 keys). These constraints still enable building many games and applications, and the programmer only focuses on implementing updates to the game state.

The benefits is that within these constraints, writing a game is very easy. We will look at how to work with a NonBlockingGame in three steps, 1) Getting something running; 2) Displaying something on the screen; 3) Capturing user input.

See also

This tutorial gives an introduction to the usage of non blocking games. You can find the complete documentation of the features in the Doxygen documentation of the following classes and functions:

  1. NonBlockingGame [Java] [C++] [Python]

1. Getting Started

A game in Bridges is made by creating a new class for your game that is derived from NonBlockingGame. Your class has to construct the underlying NonBlockingGame object by passing the usual Bridges credentials, the assignment number, and the size of the game board. We will use a 10x10 board as an example. Then the game object needs to be instantiated and the game started.

Minimally, you create a game by writing two functions:

  1. initialize() is called once when the game originally starts. It is typically used to set up all the game logic in its initial state. Think, for instance, about setting up a chess board at the beginning of the game.
  2. gameLoop() is called once for each frame of the game. This is the function that you will write to update the game at each frame based on the player's input.
Here is a minimal game pre-written for you.

Java
C++
Python

Make sure that you can run the minimal game.

If you follow the URL given to you when the application runs, it will get to to the Bridges webpage that show your game. To see it, you need to 1) be logged in your account, and, 2) click on the "Connect to Game" button.

If you want to convince yourself that the game is actually running, you can print to the console a simple message when the GameLoop() function is called. You will see that the message will appear about 30 times per second.

2. What can be Displayed using NonBlockingGame?

The display of a NonBlockingGame is based on a grid. The size of the grid can be as small as a single cell, and as big as 1024 cells in total. The biggest square game board is 32x32. However, the board does not need to square, it could be rectangular: for instance a 16x64 board, or a 128x8 boards are possible. The size of the board can be controlled by the last two parameter to the constructor of NonBlockingGame.

Each cell has a background color and can display a symbol painted with a particular color. These colors and symbols enable to compose a wide variety of displays.

Only 256 colors can be used for either background color or symbols color. The list of colors is available as part of the NamedColor are the SVG Version of X11 color. One can set the background color of a cell using the setBGColor() function. The setBGColor() function takes the row and column of the cell, and the color to paint that cell.

The usable symbols are NamedSymbol and are graphically depicted here. One can display an symbol on a cell and select a color for it using the drawSymbol() function.

The following code paints the screen's background and writes "HELLO" with symbols.

Java
C++
Python

Note that the code has been written in the initialize() function. Indeed the gameLoop() function is not going to change the state of the board, so a one time set up is sufficient.

Some case study

Take a minute to consider the following displays that can be created for games Bugstomp, Snake, 2048, and FallingSand

Bugstomp is a very simple game on a 30x30 grid, where the background is all black and all symbols are white. The character controlled by the player is shown using the 'man' symbol and the bug to stomp is shown using the 'bug3' symbol. The letter and number symbols are used to display the score.

In Snake, a 30x30 grid is used. The cells' background are first colored with two alternating colors to help the player figure out locations easily. The actual snake is displayed also using the background color in grey to show the body and in white to show the head of the snake. The apple to pick are shown with an actual red apple symbol.

The 2048 game is based on tiles arranged in 4 rows and 4 columns. Each tile need to display its value which can not be done on a single cell of the NonBlockingGame, So each tile of the game logic is rendered using 6x6 cells of the NonBlockingGame, and the entire game uses a 24x24 board (4x4 tiles each of 6x6 cells). This enables the game to show more complex information such as the value of each tile, using a combination background color to show the value of the time, but also symbols to print an actual number. Also the decomposition of tile in 6x6 cells allows the animation of the game by sliding the tiles one cell at a time when moves happen.

Finally, Falling Sand operates on a 30x30 grid and show a simulation of metal, sand and water. The metal is shown with 'gray' 'squares', sand is shown with 'sandybrown' 'waves', water is shown with 'blue' 'droplet', and the cursor is shown with a 'star'.

3. Capturing User Input.

Using the keyboard raw state

At each frame of the game, the gameLoop() function is executed. The programmer can query the user input with functions which return booleans that indicate whether that particular key is pressed. The programmer can query the arrows with keyUp(), keyDown(), keyLeft(), keyRight(); four buttons with keyW(), keyA(), keyS(), keyD(); and two special button keySpace(), and keyQ().

While there is no mouse or gamepad support, you can think about the key as mapping to a gamepad like that:

The following code demonsrates how the keys can be used to display the "HELLO" message only if the Up Arrow is pressed.

Java
C++
Python

Note that the code checking for the input has been written in the gameLoop() function while the code changing the background color has been written in initialize(). The code inside initialize() would not be able to capture user input since it is only executed once, so it has to be inside of gameLoop(). Because the game board is not reset at each frame, the board starts at the beginning of a frame in the same state it was at the end of the previous frame. Since the background color does not need changing, the code that sets the background can be left in initialize().

However, the code that prints "HELLO" uses symbols to print the message. So the symbols are on the board at the beginning of the gameLoop() function if the up arrow was pressed at the frame before. Therefore, if the up arrow is no longer pressed, it is necessary to remove the symbols by setting them to 'none'.

Using the Fire interface

The raw state of the keyboard will have an action executed at each frame of the game if the key stays pressed. In many games, this is not what you want. Think of a navigating a menu, you usually do not want that pressing a key to change which option of the menu is selected at each frame.

The Fire interface enables to only have an event happen every so-many frames when the button is left pressed. You can configure the cooldown of a button (how many frames will pass between two activations) with the SetupFire functions. (There is a function for each key: keyUpSetupFire(), keyDownSetupFire(), ...). Each key has a Fire function such as keyUpFire(), keyDownFire() to know whether the current frame is a fire frame for the particular key.

If you run the following code, you will see that as long as you to not press the up key, the state of the fire event is false. If you leave the key pressed, the fire event will happen every 20 frames. That cooldown of 20 frames is setup in the initialize() function.

Java
C++
Python

Using the pressed/released interface

Finally, you may prefer to use an interface that looks more like a pressed/released interface. Think of a button in your typical graphical interface. When you click on it, something happens when you push the button and when you release the button.

Bridges non blocking game framework provides you an interface to enable these types of behavior more easily. A key can be in one of four state: Just Pressed, Still Pressed, Just Not Pressed, and Still Not Pressed. At rest, a key is in the Still Not Pressed state. When you push on the key, it will go in the Just Pressed state for a single frame. On following frames it will be in the Still Pressed state. Once you release the key it will be in the Just Not Pressed state for a single frame; and it will be in the Still Not Pressed state on the following frames.

Each key has four functions that tell you whether you are in a particular state or not: keyUpJustPressed(), keyUpStillPressed(), keyUpJustNotPressed(), keyUpStillNotPressed(), keyDownJustPressed(), keyDownStillPressed(), keyDownJustNotPressed(), keyDownStillNotPressed(), ...

Running the following code will show you how the key cycle through the four states when you press and release it.

Java
C++
Python

Going Further

Despite the NonBlockingGame API is very simple, it is sufficient to make a wide variety of games. Really most games from the early history of video games followed a similar pattern. Adventure, Rogue, Snake, Tron's Light Cycles, 2048, Minesweeper, Ultima, Crossfire, Tetris, Advance Wars, Sokoban, Chess all follow a similar pattern.

Check Bridges assignment page for games to make. The simpler ones to start with are probably Bugstomp, 2048, and Snake.

NonBlocking games can also be used not for games, but for cellular automata simulations. See for instance assignments Spreading Fire, and Falling Sand.