In my previous post Portable Arduino Game Console - Part 1 - Endless Runner, we talked about how to create a Portable Arduino-based Game Console.
This console gave you a portable game development environment using a 16x2 Char LCD, two buttons for control, and 2 piezo speakers for background music and sound effects, all in a portable enclosure using ProtoStax.
Of course, what good is a portable game console if it doesn't have some demo games? :-) We started with an Endless Runner Game with a forest-y theme along with Background music and Sound Effects, where a Harry Potter-esque Runner with a Wand had to jump over Obstacles (Trees) to collect as many points as possible. This built on an existing Arduino-based Endless Runner game to make the game play more arcade-like - add some background music, sound effects, track high score, etc. You could switch between a Harry Potter, Game of Thrones or Legend of Zelda theme (or create your own easily).
In this post, we'll showcase the 2nd demo game - a Snake Game.
If you haven't already seen the Snake Game that was ubiquitous on old (non-smart) flip-style mobile phones, the objective of the game is to avoid obstacles and eat apples. Every apple the snake eats makes its length increase by 1 and the game moves a little bit faster! The snake cannot run into itself either (this becomes a danger when the snake gets longer!)
Press Button1 or Button2 to start the game. During gameplay, press Button1 to make the snake turn counter-clockwise/left and Button2 to make it turn clockwise/right.
The game stops when the snake runs into an obstacle - either one of the 4 edges/walls, or running into itself. Points are awarded based on length of the snake.
You have sound effects when the snake turns clockwise, counter-clockwise, when it eats an apple, or when it hits and obstacle and the game ends. There is some celebratory music when you hit a high score! The game displays the reason you lost, as well as the high score to aspire to!
This game builds upon the "Arduino 1602 Snake Game" by SadaleNet (see attribution below for link to original code)
Preparing the Portable Game ConsoleThe Portable Arduino Game Console - Part 1 - Endless Runner dealt with the hardware prep - please refer to that post for details. The short video below shows the highlights:
For this demo, I decided to use ProtoStax in Obsidian (black) color for a different look and feel.
Step 2: Prepare the SoftwareYou can use the example sketch ProtoStaxSnakeGame from the GitHub page (link below), which also has instructions on how to get it running.
The sketch utilizes the following libraries:
- ProtoStax_MmlMusicPlayer
- MmlMusic ( ProtoStax_MmlMusicPlayer extends the MmlMusic library, so we need the base library)
- LiquidCrystal_I2C (for the LCD display)
You'll need to follow the instructions in the README to install the above libraries as well.
Step 3: Running itCompile and upload the sketch to your Portable Arduino Game Console.
After the brief introductory music (an homage to Pac-Man!), press either Button1 (the button closer to you, and connected to pin 2 on the Arduino), or Button2 (the button further away from you, and connected to pin 3 on the Arduino) to start the game.
When the game is in progress, Button1 is used to make your snake turn left or counter-clockwise. Button2 makes the snake turn right or clockwise.
You have different sound effects when the snake turns (so you also get audio cues about which button is which), or when the snake hits an obstacle (the edges/walls, or runs into/eats itself!). Every apple consumed increases its length by 1, and also increases the speed of the game by a bit.
The game will continue until the snake hits an obstacle. Points are awarded based on the length of the snake. The game tracks and displays the high score at the start. If you've surpassed the last known high score at the end of the game, you'll also get a little celebratory music (an homage to Mappy!) π
Modifications to SadaleNet's Arduino 1602 Snake Game EngineWhile I have kept the basic game engine from the original code intact, I made a bunch of modifications.
The code is modified to use the LiquidCrystal_I2C display which is part of the ProtoStax LCD Kit V2. The buttons are connected to pins 2 and 3 as in the schematic for the ProtoStax Portable Arduino Game Console.
The console additionally has two piezo speakers connected to digital pins 10 and 13, and uses the ProtoStax_MmlMusicPlayer library for music and sound effects. Calls to play these sound effects are injected at suitable points in the game play.
I added music to be played when the game is in play - introductory music, sound effects with the snake turns left or right, or hits an obstacle, and celebratory music when the high score is surpassed, using the ProtoStax_MmlMusicPlayer library and the piezo speakers. The console input is blocked when the introductory music and celebratory music are played, similar to an arcade game, so the player waits for the short music clip to end. The addition of the music and sound effects makes the game more immersive with audio feedback.
I keep track of the high score, and display the high score on the screen when the play ends. I also keep track of the reason the game ended (running into an obstacle, or the snake running into itself), and display that as a message to the player, so the reason for the loss is clear - this is especially useful for new players.
How the Sprites workOne nuance to note relates to the sprites - the snake and the apple.
In LCD screens, characters are displayed as 8x5 elements. Apart from the usual ascii characters, you can also define a custom character to display, which will be stored in memory (the first argument to createChar tells it which position in memory to store the character)
byte smiley[8] = {
B00000,
B10001,
B00000,
B00000,
B10001,
B01110,
B00000,
};
lcd.createChar(0, smiley);
You can then display the smiley character simply using that position in memory as follows:
lcd.write(byte(0));
You can only define 8 such custom characters β the first argument of createChar takes the index of one of the 8 custom characters to store in memory (0 to 7).
In the snake game, in order to expand the number of available positions to move, each snake segment and the apple only occupy half of the 8x5 character (in fact, less than half, as the sprites themselves are only 3x5, leaving the 4th row as empty space in between)
byte block[3] = {
B01110,
B01110,
B01110,
};
byte apple[3] = {
B00100,
B01010,
B00100,
};
In graphic_generate_characters(), 8 custom characters are defined, based on the permutations of whether the top half of the custom character will have an empty space, a snake segment or an apple, and whether the bottom half will similarly have an empty space(0), a snake segment (S) or an apple (A).
0: 0 A
1: 0 S
2: A 0
3: A A
4: A S
5: S 0
6: S A
7: S S
This can be a litte hard to wrap your head around just looking at the code - hope this explanation helps you! You can modify the sprites if you like - easiest way is to just update the block and apple byte arrays (as shown above) in the code.
Going ForwardAs always, I like to end by talking about how you can take this project forward with your own additions! Once you get comfortable playing around with the code sample and understanding the code, it is always nice to try to extend your learning by doing more.
Here are a few suggestions on how you can take this project forward:
- Create your own sprites using the LCD Custom Character Generator to create your own variation of the game. There is some nuance to note here. Since each snake/apple sprite only takes half a character, there are 8 custom characters, each with either snake, or apple, or empty space in it to cover the various cases of there is a snake on top, apple at the bottom, or snake on top and bottom (when it is turning around, for example). Since you can only create 8 custom characters on the LCD, this restricts the number of special sprites you can have. For instance, it would be nice to have the head, body and tail of the snake all look different, but that may require creating custom characters on the fly and may also affect the performance of the game.
- Create your own intro music, celebratory music and sound effects by creating your own MML strings that be played.
- Allow the player to enter his/her name when they get a high score. Have a list of high scores and player names.
- Use the Button1/Button2 to toggle a settings/high-score menu screen before game starts - the user can use this settings screen to choose the background music and sound FX settings, for example.
- The code does its own debouncing of the buttons. You can replace that part with a library such as JC_Button, which apart from debouncing will also allow you to easily handle things such as short and long presses, or both buttons being pressed, etc. You can, for example, treat both buttons being held down in a long press as a request to reset the game high scores (or just play the delightful Pac-Man introductory music again without actually reseting the high scores!) π
Can you think of other ways to extend this project? Share it with us below! π You can also use the Portable Arduino Game Console to create your own game(s) using the help of the different libraries to focus on your main game algorithm - use these demo games as inspiration and also help getting started with your own game development. Also do share photos/videos of your creation on your social media platform of choice - just tag it #ProtoStaxGameBoy #ProtoStax so we can find you!
Happy Making! π
Sridhar
Comments