Skip to content

CalmyJane/myPong

Repository files navigation

myPONG - the RIO based Game Console

Gameover Loop Gif

Console Image Console inside Image

Introduction

This repo containst the LabVIEW Code for a myRIO based game console. All code is written in LabVIEW, LabVIEW Real-Time and LabVIEW FPGA. The console currently features 3 Games, Pong, Snake and Bouncer (Brick-Breaker), as well as a variation of Pong called Gravity-Pong where the ball has gravity towards the bottom of the screen and bounces off the screen edge, resulting in a ping-pong game. In Pong and G-Pong the Padels can not only move up/down, but also left/right, resulting in a much more "engaging gameplay" .. ;)

Also each game is build for one or two players. All games also have algorithmic "AIs" that can play the game. This way e.g. Pong can be played against a bot, and also two bots can play any game for demo purpouses.

Infocard for console

Pong

Pong Loop Gif

G Pong

Gameover Loop Gif

Snake

Snake Loop Gif

Bounce

Bounce Loop Gif

Technical Overview

To display the games on a screen, the myRIO generates the VGA-Signals that can be used with older monitors. To support current hardware, an VGA-To-HDMI Adapter is added. This way the myRIO can connect directly to any HDMI- or VGA-screen and display it's generated images.

The Code is mainly split into two parts, the FPGA and the Real-Time.

FPGA Graphics Module

The FPGA acts as an old fashioned graphics-chip like it was used in old game consoles. It's able to display Tile-Based graphics on a 30x40 Grid with 16x16 Pixels each. The Tiles can be updated from the Real-Time controller. Additionally there are 3 Sprites available. Sprites are images that are drawn over the Tiles, they can have a pixel-precise position and Size. Also some of their pixels color can be defined as transparent, this way a sprite can have non-square shapes (like the pong-ball). A combination of Tiles (for backgrounds, and text) and Sprites (for gameplay-objects) can bring a wholesome look and feel to a game console with very limited ressources. Reason for these limitation mainly is the myRIOs FPGA memory size. It doesnt feature enough memory to store a single full frame (pixel-by-pixel) at any given time. By using tiles and sprites, the limited memory can still be used to generate an image, much bigger than the data used for the generation. A similar technique was used on the gameboy and many similar consoles.

FPGA Sound & Controls

The smaller portion of the FPGA is the control-input which is basically just forwarding of several digital inputs, and sound output. The sound features "music" and "sounds" which are mixed on the FPGA. This way samples can be played very quickly, and interrupted if necessary, and music stays continuous in the background. There are two FIFOs from the Real-Time to the FPGA for music and sounds. Currently there are about 10 sounds in use and 5 background tracks. These are also limited by the myRIOs file storage capacity.

Real-Time Game Engine

To use this graphics module for games (or other applications), there needs to be code that feeds it the correct tiles, and sprite-settings. This is done in a game engine on the Real-Time. It holds functions for drawing text or shapes on the screen, loading new games, transitioning between scenes, playing sounds and much more. All of the code is written in LabVIEW and new games can be added by adding a new child of the game.lvclass.

FPGA Details

Overview of the FPGA Loops

The FPGA has a lot of jobs, but the most complex is the rendering and vga-signal generation. The rest is simply loops reading from a fifo (coming from the game engine) and storing the information in memories, forwarding boolean control information to the game engine or reading audio samples from a fifo and outputting them to the audio outputs.

Graphicsmodule Overview

The Rendering Process

In the rendering loop there are tight timing criterias. The pixels must output with 25MHz, each containing a blanking interval. So the loop itself needs to run with 50MHz, sending a 0 for each color on every other iteration. In the same time the sync pulses need to be generated inside the same loops in sync with the Rendering. The Syncpulse-Calculation also generates the current pixel coordinates for the rendering. Every other Iteration of the rendering loop, one pixel is calculated.

Rendering Overview

The X- and Y-Pixels can be shifted to create a pixel-wise shift of the whole image in any direction. This is very useful to generate transitions between scenes. The two Color-Shift and replace-color operations allow for multiple fullscreen-effects, as well as tile-only or sprite-only effects for all pixels.

Rendering Tiles

Here is a more detailled view on the tile-rendering process. The tiles reside in a 40x30 (actually a bit bigger) tile memory that can be updated from the RT. While iterating through all pixels of the image, the FPGA constantly checks what tile it is in. It then reads the tile-value ("TileAddress" in the code). The TileAddress is a U32 bit Integer containing a U16 Tile-Number that indicates which tile to load, and a U16 Modifiers:

  • 2 bit rotation (0, 90°, 180°, 270°)
  • 2 bit flip (none, horizontally, vertically, both)
  • 3 bit color to replace 1
  • 3 bit replacement color 1
  • 3 bit color to replace 2
  • 3 bit replace color 2

These Modifiers can be set for each Tile individually allowing for a huge range of modifications of tiles from within a game. Once the tile address is read, and modifiers are applied, the actual pixel-value is retrieved from the tilesheet-memory.

Rendering Tiles Overview

Rendering Sprites

The sprite rendering is simpler. Sprite information (position, size, transparency color ...) are stored in a fixed-size array containing 3 clusters for 3 independent sprites. Their images are loaded as a continuous stream at the beginning of the game. They might be switched ingame, but glitches/delays may occur.

Rendering Sprites Overview

Game Engine

The Game Engine features several classes that are important.

GameEngine.lvclass

This class contains the main.vi of the Game Engine. This class is the counterpart to the FPGA. It writes the Tiles, Sprites and Sound Information to the corresponding FIFOs/Memories and retrieves the User Inputs from the FPGA. The Class has a Subfolder "Public API" containing a set of VIs to e.g. draw text on the screen, change a sprites image, and much more. This class is very big and could propably be splitted into multiple classes/modules at some point.

Game.lvclass

The Game.lvclass uses the GameEngine.lvclass to implement Games. It is also called by GameEngine.lvclass periodically. There are two main Methods that need to be overwritten to create a game:

  • NewGame.vi - called once at the beginning/restart of the game
  • Update.vi - called on every frame of the game

To write a new game a class can inherit from game.lvclass, implement these methods and use the Public API of GameEngine.lvclass to retrieve user input and draw images or text to the screen.

Game.lvclass class diagram

The Game.lvclass itself handles several things for game development, most important an ingame-menu that can be activated, pauses the games Update.vi and provides possibility to:

  • Resume
  • Restart
  • Change between single- or multiplayer
  • Exit to main menu

The game also contains a highscore table, and it can "slide out" meaning it's generating a moving transition to the next game (e.g. the main menu).

All states are sketched here:

Game.lvclass state diagram

GameObject.lvclass

GameObjects are the building blocks of a game. Currently they mainly contain the games physics- and collision logic. It offers methods to calculate collisions with other gameobjects. Usually a game object is tied to a sprite, but theoretically a gameobject that's tile-based is possible, this could be used for platformer-games where the character is a sprite, but the level is tile-based. Also objects that don't use the collision logic should be implemented as gameobjects to keep a streamlined workflow for all game components.

GameObject.lvclass class diagram

File Structure

on the myRIO there is a data directory here: //ni-myrio-1900-03038019.local/files/home/lvuser/natinst/bin/data

This contains subfolders for all necessary game data, tilesets, tile maps, music and sounds.

Game Design

When creating a game, you will need a few different components.

Tilemap

You probably will have some kind of static tile-background for many games, or for displaying the Header of a game and generating the rest of the tiles dynamically. To create Tilemaps an awesome tool is Tiled 2D which can be downloaded for free and creates tmx-files which can be directly copied to the myRIO. The files basically contain the tile-number form the tile-set, in Tiled2D you can easily draw using these tiles, which is a bless!

For most games the default tileset should be sufficient, but you can of course load completely different tilesheets. There might be glitches when loading (all tiles changing appruptly, though, so rather do this when the screen is blank.

Sprites

For sprites you only need an image but it needs to have only the 7 colors mentioned. A sprite also has a transparency color to remove background from the box-shaped image.

Music

The console has default music running all the time. The music was generated using suno.ai, an AI Music Generator and it's unique soundtrack for this game console. To add your own Music, you can use the Music Loader VI on the Host-part of the project to generate a .music file which you can transfer to the myrios data/music directory.

Sounds

The myRIO also has default sounds. You can use CreateSound.vi to create .sound files and transfer them to the data/sounds directory

Challenges

This Project offered a lot of challenges to be solved, here are some interesting ones:

FPGA-based games

My first thought about this project was to make one bitfile per game, and implement the game logic entirely on the FPGA. This quickly turned out to be a bad idea, since the compile time was painful when doing small adjustments and changes to the game logic. Also the myRIO FPGA is quite limited in logic cells, so it could easily be full with very simple games.

RT-Based graphics

My next thought was to use the FPGA only for VGA Signal generation and implement the actual graphics (caluclation of each pixel) on the Real-Time. But this also turned out to be a bad idea, since a 420x640 pixel array easily overloaded the cpu and memory. This led me to the concept of a graphics module which turned out to fit the myRIO architecture perfectly.

Sliding transitions

After implementing a pixel-wise shift on the FPGA, I wanted to have smooth transitions between different games/screens. To implement this I needed a bigger tile-buffer so that when I'm shifting the image, I could already generate a few more new pixels. Therefore the whole architecture had to be changed. This was a huge part of the project, also shifting the whole screen, while generating the new tiles line-by-line was a lot of work to get pixel-perfect.

Sliding Transition Gif

About

LabVIEW Project with example how to output VGA-Signals from FPGA and how to build a game on it.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors