Retro Games Console

Autor: Basarâm Ștefan
Grupa: 333CD

Introduction

What?

This project consists of a game console featuring popular retro games such as Snake, Pong and Asteroids. It uses as control inputs a button and a joystick, with an ultra-wide display composed of 2 LED 8×8 matrices and a buzzer for audio feedback.

Why?

The purpose of this device is to facilitate a unique gaming experience in a portable package which aims to capture the golden period of arcade games from the 1980s. This console offers a unique handling to players today who are used to playing games on their phones and desktops/laptops.

How come?

My inspiration comes mainly from my existing passion for graphic and game design. Having limited resources, I decided examples from the first generation of video games are the most suited.

For whom?

The biggest audience for this device are the teens who have an affinity for old-school video games who would thoroughly enjoy a similar gaming experience to the one provided by arcade cabinets, all available in a neat and portable package similar to actual game consoles on the market.

Description

Powering ON

  • When the console is powered on, a little start-up animation plays on the main display, scrolling the console name and then the first game out of three (Snake) automatically starts.

User input

  • The analog joystick is used to move the characters and the dedicated button is used for firing or adjusting the game difficulty.
  • If a player wishes to skip a game, they can do so by pressing twice on the button integrated in the joystick in quick succesion.

Game loop

  • Every frame, the game updates its game state, reads user input and draws the result to the main display formed of two 8×8 LED matrices.
  • If the game detects an entity collision or a decision is being prompted, the buzzer will be triggered to provide an auditory feedback.

Lives system

  • For each of the three games the player has 3 lives. These are represented by the 3 color LEDs arranged in an LED array which are colored red, yellow and green from left to right.
  • When a player loses a life, the rightmost LED flashes and turns off. The current game subsequently restarts, or resumes. When a player consumes all lives they will enter into a gameover.
    The red and green LEDs flash for a moment, followed by one of them being active at a time signifying the player's choice to either retry the current game, represented by the red LED, and moving to the next game represented by the green LED.

Hardware Design

Components

  • Arduino Uno R3 ATMega328P-AU CH340G - The brains of the operation. Conveniently already possessed one and its small size allows it to be stored in a compact construct.
  • Module Button - The reason I bought a button with dedicated pins, instead of using one ready-to-go on a breadboard, is precisely because I did not want to use it directly on a breadboard.
  • Analog Joystick - With limited controls, a joystick offers a lot of options for movement, along with a normal button in a single package
  • Two 8x8 LED Matrices - The main display of the console. Unlike a typical LED matrix, these display modules have a microcontroller which control the LEDs via just 5 input pins, which not only simplifies the circuitry massively, they also allow to be used independently of a breadboard. They also have 5 output pins which mirror the input ones which allow daisy-chaining displays by assigning every display an address. I decided against using these however in order to favor a smaller console.
  • Three LEDs of colors red, yellow and green (part of kit) - Secondary display. Shows extra info about the current game state.
  • Resistors 220Ω (part of kit) - For the LEDs.
  • Passive Buzzer - Every respectable console with a small budget should sport an annoying buzzer such as this.
  • 9V Battery + Adapter - Allows the console to be used without being tethered to a computer.
  • Breadboard - Used for wiring together the display, player inputs and the buzzer.

Pin configuration

For all devices, the VCC pins are connected to 5V and GND to a GND pin on the host (Arduino).
The Host ↔ Device column presents which pins are used, as well as the direction that information flows in.

Name Type Host ↔ Device Reason
Module button Digital D2 ← out Binary press state of button is read via INT0 interruption
LED Matrix (A) D8 → DIN Data input, sets the state of any LEDs on the matrix
D9 → CS Chip select
D10 → CLK Clock signal
LED Matrix (B) D5 → DIN Data input, sets the state of any LEDs on the matrix
D6 → CS Chip select
D7 → CLK Clock signal
Red LED D13 → LED Pins power the LED ON/OFF
Yellow LED D12 → LED
Green LED D11 → LED
Joystick Analog A0 ← VRx Continous input from a potentiometer, representing 2D movement
A1 ← VRy
Digital D4 ← SW Button press state can be read digitally
Passive buzzer Digital (PWM) D3 → Buzzer The use of PWM enables adjusting the volume of the buzzer, along with its tone

Software Design

Source code available on Github.

Environment and Libraries

Development includes the use of both .ino files and C++ files, as such I used two IDEs

  • Arduino IDE - for the only .ino file Console.ino which is concerned with interacting with the Arduino
  • Visual Studio Code (VSCode) - programming the games and integrating them with Console.ino. Using VSCode instead of Arduino IDE for source files had many benefits including better syntax highlighting, more functional text-editor and accessible version control via Github.

For 3rd party libraries I used LedControl which was a necessary addition in order to use the LED matrices of type MAX7219 which have a unique hardware interface composed of 5 pins.

Architecture

The code is divided into 3 parts:

  • Console.ino - it has the responsibility of instantiating the game's engine and games and updating them. Finally, it is used for compiling and uploading the code which runs on the Arduino.
  • Games - written in C++, implemented as classes which inherit from a base class (Game.h → Snake.h, Pong.h, Asteroids.h).
  • Engine - written in C++, it allows the games to interact with the Arduino hardware by using functions :
    • reads user input
    • plays sound
    • updates screen using two LedControllers, provided by the library mentioned above, one for each LED matrix

Console.ino

It contains the main loop and has the role of managing and updating the engine and the active game as well as cycling the games. The main functions are setup() and loop(), with the auxiliary functions: switchGame(), handleGames(float deltaTime) and startupSequence()

void setup()

  • runs startupSequence() which plays an animation and also plays sounds

void loop()

  • calculates deltaTime using the built-in function millis() and keeping track of the time last frame
  • runs the game engine: clears the screen, updates the game state and draws it
  • makes sure it runs at a fixed framerate

void handleGames(float deltaTime)

  • checks if the game should be switched
  • calls switchGame() if previous condition is true

void switchGame()

  • cycles the games and instantiates the new game

Engine

Acts as the bridge between the games and Console.ino which handles the hardware. Responsible for reading user input and implementing the system which updates the screen.

Drawing system

The drawing system stores 2 arrays of 8 bytes, with one bit for each LED. Each array is associated with an LED matrix and also each LED matrix gets its own controller of type LedControl provided by the 3rd library mentioned above. When setting a pixel on the screen, it doesn't immediately update the LED matrices. It first determines which LED matrix is affected and updates the correspondent array of bytes using bitwise operations. When the game state is updated, the arrays of bytes are sent row by row to the LED controllers which change the LEDs matrices.
For performance reasons, we cannot update each LED individually. Also, because the actual LED matrices composing the physical screen have different orientations, we must change how each LED matrix is updated.

void updateLoop(float deltaTime)

  • checks for user input

void playSound(int frequency, int duration)

  • plays sound using built-in tone function

void clearScreen()

  • resets LED array of bytes

void setPixel(int x, int y)

  • sets the bit at x, y by calling setPixelToValue(x, y, true)

void setPixelToValue(int x, int y, bool on)

  • changes corespondent array of bytes depending on the coordinates and sets the provided state

void drawToDisplay()

  • called by Console.ino, sends the arrays of bytes to the LED controllers which update the physical LED displays

void setDisplayBrightness(int brightness)

  • utility function for setting the displays brightness using the LED controllers

Games

I took an approach inspired by OOP design principles and implemented the games using polymorphism and inheritance. I made a Game interface with an updateLoop() function which must be implemented by any game which wishes to be added. Console.ino holds a reference to the current game and calls the updateLoop() function which calls the correct derivative function depending on the underlying game class.

Each game holds a reference to the Engine, fact enforced by the Game interface, which allows it to read the current state of user input, play sounds and update the screen as well as access details about the engine such as the delta time and the time since the last move has occurred.

There are 3 games implemented:

  • Snake
  • Asteroids
  • Pong
Snake

Classic snake, but the walls are portals. Pac-Man style.
In the updateLoop() function, the snake is moved according to its current direction and updates it according to the joystick. If the snake head touches an apple, it plays a sound and the snake's size is increased and it calls placeFood() to choose a new apple placement without clipping the snake, using the built-in function random(). In this game implementation, the snake is not killed if it exits the bounds of the display, instead it is transported to the other side.

If the head of the snake occupies the same position as one of its body, the snake is killed and one of the player's lives is taken. If the player has not run out their 3 lives, they may continue playing from the point just before the collision occured, with the game resuming upon the player's next input. When all lives are exausted, an animation is played which shows how large the snake had grown before it died.

Asteroids

The player encompasses a spaceship on the left side of the screen and must shoot incoming asteroids (presumably, since the display resolution allows room for creative interpretation).
In updateLoop(), the spaceship is moved up or down with the joystick and fires with a normal button press. Asteroids are spawned randomly on right side of the screen and approach the player's ship. Firing makes a noise.
If the player is clipped by an asteroid, their ship is destroyed. This game does not have lives and the player will simply be prompted to try again or move to the next game.

Pong

Hang on, isn't pong a 2 player game? Or played with an A.I?
In my version of Pong, the player controls BOTH paddles, one at a time. After a paddle reflects the ball, control is immediately switched to the other paddle and the player must react fast enough to continue to reflect the ball. The game starts with an active paddle and a random ball trajectory.
In updateLoop(), the currently active paddle is moved by the joystick. The paddle is not terribly big, however it moves very fast. If the ball collides with the paddle, the ball is reflected and control is passed to the other paddle. The speed of the ball increases over time.
If the ball goes past the paddle, the player loses a life. If the player has not run out their 3 lives, they get to keep playing with the last speed of the ball preserved and starting from the losing paddle.

Results

References

pm/prj2025/iotelea/stefan.basaram.txt · Last modified: 2025/05/26 20:43 by stefan.basaram
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0