Arduboy Magazine Volume 9

Page 1

Vol. oct

9

2017


Contents: PLAY

New games out this month! Arduventure - The Kickstarter campaign Community Polls - Favourite video game music crait’s Top 3 Favorite under-utilized Arduboy Features

LEARN

Design & Development with filmote - Pipes (Part 3)

Design & Development with filmote - Pipes (Part 3) - Callouts Scoped Enumerations by filmote FixedPoints by Pharap & filmote

EXPLORE

Pixel Page with @maurihelme Questions with Game Developer @JuiceLizard #arduboy

Page 2 of 47


A welcome note:

At the time of writing the Arduventure campagin has hit over 1,000 backers - Thank you everyone who has backed the project! TEAM a.r.g. has been working hard (2 years) on the game and this campaign couldn’t be a more fitting launch for it. Read more about the campaign and the developers behind it on page 8. If you already have an Arduboy and want to learn more about development, I hope you’ve been following filmote’s ‘design & devlopment on his Pipe game. Part 3 is out this month. The Magazine is always looking out for more talented contributors - reach out to us! -celinebins

Do you wish to contribute? Have an interesting article you wish to share with the Arduboy community? A cool project you did, your experience on working on it? A funny joke? We want to hear it! Send arduboymag a message through the Arduboy community page or a DM on Twitter! Page 3 of 47


Thank you to this months contributors! @crait: Creator of various Arduboy games such as Circuit Dude and Train Dodge, crait has written tutorials on how to get started with creating your own game, and developed Arduboy Manager; an easy way to upload games to your Arduboy without using the Arduino IDE. http://www.crait.net/

@filmote: In between writing articles for this magazine, Simon is writing an Arduboy version of the classic 1943: The Battle of Midway game. Download and play it on your Arduboy here!

@JO3RI: Better known as TEAM a.r.g, some of Arduboy’s community favorite games are written by them, as well as their own Arduboy Game Loader, where you can easily upload your favorite Team a.r.g games without using the Arduino IDE. http://www.team-arg.com/

@maurihelme: Game designer, animator and illustrator creating #pixelart and #voxelart from Brazil. Follow him on Twitter @maurihelme.

@TheArduinoGuy: Author of the ArduBoy games - Jet Pac & Arcodia.. Author of ‘Beginning Arduino’. Medway Makers. Maidstone Hackspace. http://thearduinoguy.org/

@Your name: This could be you.. Help contribute to the Arduboy Magazine!

Page 4 of 47


Arduboy

Company

news:

What’s happening at headquarters?

@Celinebins

Ongoing - Arduventure Kickstarter!

Thank you for everyone’s support in funding the Kickstarter! With about a week to go before the campaign ends, steps are already being taken to prepare for manufacturing. Read more about the campaign on page 8.

Upcoming events - October

The Arduboy team will be taking part in Game Start Asia (14-15 Oct), and Portland Retro Gaming Expo (20 - 22 Oct). Drop by and say hello!

Where to buy Arduboy? In order to better deliver the Arduboy to you, a distribution model for sales has been currently implemented. You can now purchase the Arduboy from a variety of different retailers from around the world!

New!

Talk to us!

Page 5 of 47


W

ES AM G

by @Celinebins So many new games were released this month! If you missed them on the community pages, here they are.

FR ES H

N E

PLAY:

That will get you hooked

1943- The of midway

battle

@filmote (dev) @Pharap (Test) Destroy the enemy Zeroes and Bombers. Zeros with solid wings will die on one shot, others will take multiple hits before they die. Watch your fuel, your health and ammunition - replenish these by running over the power ups!

Poitto puzzle

- Action game

@inajob The rules are simple, go to the door while avoiding enemies, use switch blocks, spring blocks, and so on‌

Dark Under Dungeon master clone

@luxregina (&@filmote) Still a WIP - but a game to keep your eyes on. Page 6 of 47


Up coming events ! Join the Arduboy team at these events in October! Come say hi to us, try out some demo units (if you do not have an Arduboy yet), ask us questions, and enjoy special event discounts. ;)

Game start asia Suntec city Singapore Southeast Asia’s Premier Game Convention is back for its 4th edition on 14 & 15 October 2017! This year is set to bring back annual favourites as well as new and exciting content for everyone, including game showcases and previews, esports, indie games, retro gaming, cosplay and more!

Get tickets here: https://gamestart.asia/

Page 7 of 47


Last month the Arduboy team asked what color buttons would be preferred on a custom Arduventure Arduboy.. When the results were in, the Kickstarter for custom Arduventure units was launched! Find out more about the campaign here. Arduventure is the most anticipated Arduboy RPG game yet to be released. For its Kickstarter campaign launch, 700 customised black Arduventure Arduboys were offered as reward pledges; 500 as Special Editions, and 200 as Ultimate Editions which included a special Plush Toy. The special black edition features a clear plastic front and gold buttons (as voted by the community). In the campaign’s second update it was announced that the black back’s orginial cowboy bebop style artwork will be replaced with the Arduventure logo instead, based on popular request.

A big THANK YOU for everyone who helped get the campaign 200% funded within 24 hours!

to be continued next page >> Page 8 of 47


As the hero didn’t want the magical blade to fall into the wrong hands, he decided to split it in 4 pieces and hide them separately across the land, each guarded by a fearsome beast.

Storyline Many years ago, a great evil terrorized our lands and covered it in darkness. Man, woman, young and old stood up against it and fought a seemingly endless battle. United they stood strong but they were no match for the evil that had spread. Many people died, others were taken as prisoners.Those who survived started envying the dead. When all hope seemed gone, a savior appeared out of nowhere. A farmer boy, the son of a well known blacksmith and mage couple, came to battle. Wielding a magical blade, he single handedly drove back all evil and brought peace back to the lands. The boy was praised for his deeds by many, though some envied him and got obsessed with the magical blade he carried.

The blade has long been lost but the legend remains. Now, as darkness is about to spread over our lands once again, will the magical blade of legends be recovered and a new hero rise? Ardventure hero, Flynn

Use Flynn’s backpack to store your custom Arduboy!

to be continued next page >> Page 9 of 47


No stranger to the Arduboy community, TEAM a.r.g. is a group of amazing game developers working together to create incredible games for the Arduboy. With only 32KB of memory space available, Arduventure uses multiple compression methods to fit hours of story driven gameplay, many unique characters and a map that is larger than the one in Zelda: Link’s Awakening! Tiana Wicker, a Kickstarter backer said games like this that manage so much detail despite 1-bit restrictions are both amazing and inspiring, and she would like to hear a little from the team on what it’s like trying to make such beautiful sprites and tile sets under Arduboy’s limitations. So we ask TEAM a.r.g. what the hardest part was for them when creating Arduventure. Olivier: Ok so for me the hardest part was to create a drawing version of the sprite to put some life into it with his weapon. Another tricky part was to create a map similar to the real ingame one but with a more drawing style. Siegfried: The hardest part for me was designing the enemies, having a lot of different ones without using too much memory. Joeri: The hardest part for me is to convince Siegfried about changes like the sub title, the way the enemies had to exist out of an upper part and lower part so that we could combine halfs. :)

No actually the hardest part was to find ways over and over again to shrink the code without compromising on game play. Gavin: The most difficult challenge for me was figuring out how to combine (at Joeri’s suggestion) the compression techniques I made for Mystic Balloon with the compression methods used in other TEAM a.r.g. games — such as dungeons while keeping the ability to place unique areas.

Can’t get enough of TEAM a.r.g.? Play their games here: http://www.team-arg.com/games.html Use their tools to make an Arduboy game here: http://www.team-arg.com/tools.html to be continued next page >> Page 10 of 47


Some FAQ: Does Arduventure save my progress? Yes! There is a menu in-game that will allow you to save your progress at any time you like. These saves will be preserved even if you switch games on your Arduboy. Will Arduventure be released as open source? Eventually, but much, much later. Please consider backing this campaign to support the developers TEAM a.r.g. if you want to see more awesome games like this! Can sound be muted? Yes, it can be through in game controls.

I want to support Arduventure! How? Get a Plush, or back a standard Arduboy (at $39, the cheapest it’s been since the initial Kickstarter!) The white one is a standard Arduboy that comes pre-loaded with Arduventure. It doesn’t have the black circuit board, black metal back (It’s just aluminum color) or the custom laser engraved artwork. It also ships just after the Special Edition Arduboys as well. It’s the lowest price for the Arduboy since our first Kickstarter, so if you’re thinking about grabbing one it’s a great opportunity!

Visit the Kickstarter

Page 11 of 47


Community POLLs! by @Celinebins

What is everyone’s favourite it be just a tune in the backg or a catchy jingle dur These are

Mario Galaxy

All photos taken from wikipedia

Ragnarok online

Little Kings Story

Legend of Zelda Ocarina of time Pokemon Ruby & Sapphire Also mentioned: Tony Hawk 2 & John Cage’s 4:33

League of Legends log in screen


Favourite video game music

e video game music? Should ground, an epic battle song, ring the pause menu? the results!

Chrono Trigger Magus Theme NES Battletoads pause music

Golden sun

F-Zero

Final fantasy 7 Boss Music

Tetris ;) Thank you everyone for being part of this discussion!

Secret of Mana - Matango

Page 13 of 47


Top 3 Favorite underutilized Arduboy Features by @crait I love the Arduboy! There’s a bunch of great games and it looks so different than any other modern game console. It’s a really great system for many reasons, but I still don’t think the Arduboy has reached its maximum potential. There’s some features that I really want to share my opinion on that either does not get enough attention or isn’t talked about enough. Here’s the list of my top 3 list of under-utilized Arduboy features.

HID Support HID means “Human Interface Device.” When devices have HID mode, they are able to be seen by a computer as some kind of input device. Keyboards, computer mouses, track pads.... They’re all input devices. What’s super cool is that the Arduboy supports HID mode, meaning that it could interface with your computer as if it were one of those input devices. Connecting the Arduboy through USB and writing a program for it, your Arduboy could turn into a mouse, gamepad, keyboard, or even an HID musical instrument. Using this mode, I’ve seen some cool creations, but there are so many possibilities out there. Fuopy created the Arduboy Gamepad program that turns your Arduboy into a keyboard to control PC games! Bateskecom was even able to control a CNC machine with the Arduboy using HID mode! People can control all sorts of stuff and I am really excited to see what they might come up with.

Piezo Speaker The Arduboy has a small speaker inside of it. Some games do not utilize it, so some people don’t even know it exists!

I made sure to include sound support in Circuit Dude, but I never was able to fit music in properly. There’s some other games that have bleeps and boops in them, but there are some really cool programs out there for Arduboy that take full advantage of the Arduboy’s speaker! There have been a few games that feature music tracks, like Arduventure by Team ARG, but there’s also music sequencers, like the one that Possan made. The most advanced, to me, at least, music library for the Arduboy has definitely been the ArdVoice library by Igvina! He’s been able to do all sorts of stuff with it, including making his Arduboy sing We Wish You A Merry Christmas! There sure is a lot of possibilities with the Arduboy’s simple speaker.

TV-Out The Arduboy has the ability to transfer information via USB, as most Arduino-based devices can. Sending data through the cable can be used for all sorts of stuff, but one cool thing that’s possible is sending the frame buffer through USB. The frame buffer is pretty much what’s going to be displayed on the screen, except stored in a certain way in memory. Sending over that data to a PC or Android device, it’s actually possible for the PC to re-organize that data and display it back as though the Arduboy has TV-Out to be continued next page >> Page 14 of 47


capabilities! I have included this feature in many of my games and made it easy to display the data through the Arduboy Manager that I released a while back. However, there’s yet to be anyone to take advantage of the 2-way communication the Arduboy could do over USB! It’d be so awesome to see someone make a 2-player game where one person uses the Arduboy and the other uses a PC... You could even have the Arduboy output different information through USB to display on a computer for additional screen

space, similar to how the Dreamcast could display information both on the screen as well as the VMU inside of the controller. If you think about it, you could even combine this idea where multiple players connect their Arduboys to the PC and all have their own screens that they can view information on and use the main PC monitor to see the game being played out! There’s a lot of possibilities with this, too! What is your favourite under-utilized Arduboy feature?

Circuit Dude is a top-down, tile-based puzzler with 100 levels. Help Circuit Dude build his ultimate secret invention by plugging in chips, moving blocks, rotating walls, and much more!

Get it on Steam here!

Page 15 of 47


Level: Intermediate

Laying Pipes - Part 3 by @filmote In the last article, we managed to get a basic version of the Pipes application running – albeit a simple one that only plays a single 5 x 5 puzzle.

In this instalment, we will add the code to handle puzzles of different sizes and the ability to select a level and puzzle number.

Adding Levels To date, our game play has been limited to two 5 x 5 puzzles. By now you are probably sick of them and can do both with your eyes closed! Let’s fix that immediately by adding a puzzles of different sizes to the Puzzles.h file. const byte puzzles_5x5_count = 2; const byte PROGMEM puzzles_5x5[] = 0x10, 0x20, 0x40, 0x00, 0x30, 0x50, 0x00, 0x00, 0x00, 0x02, 0x04, 0x00, 0x01, 0x35, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x24, 0x30, 0x10, 0x30, 0x00, 0x20, } const byte puzzles_6x6_count = 2; const byte PROGMEM puzzles_6x6[] = const byte puzzles_7x7_count = 2; const byte PROGMEM puzzles_7x7[] = const byte puzzles_8x8_count = 2; const byte PROGMEM puzzles_8x8[] = const byte puzzles_9x9_count = 2; const byte PROGMEM puzzles_9x9[] =

Due to the different sizes of the puzzles, they must all be declared in their own array. A subset of the puzzles are shown below.

{

{ ... } { ... } { ... } {

to be continued next page >> Page 16 of 47


0x00, 0x01, 0x00, 0x00, 0x06, 0x75, 0x08, 0x09, 0x00,

0x00, 0x23, 0x02, 0x00, 0x06, 0x00, 0x70, 0x00, 0x00,

0x00, 0x00, 0x40, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x43, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,

... }

Loading the Board (Revisited) The initBoard() function has undergone a few changes to date and the final tweak is to change it to accept a level parameter in addition to the original puzzle number one. The original code in the function remains unchanged however additional functionality has been added to allow the program to read from the different puzzle arrays we declared above. The puzzleType parameter is a byte which indicates the size of the puzzle from 5 to 9. Using this number, we can work out how many bytes to read per puzzle. In our original code, this value was hardcoded to 15 bytes which equated to 3 bytes per row multiplied by 5 rows. As you may have worked out by now, a 6 x 6 puzzle also uses three bytes per row but has 6 rows. #define #define #define #define #define

PUZZLE_5X5 PUZZLE_6X6 PUZZLE_7X7 PUZZLE_8X8 PUZZLE_9X9

When determining how many bytes to read, we determine whether the puzzle size is an even number - puzzleType % 2 == 0 - and, if so, we need to read in one half of the width multiplied by the number of row bytes (puzzleType / 2) * puzzleType. If the puzzle size is an odd number, we need to round the puzzle size up to the next even number, halve that and multiply in by the number of rows ((puzzleType + 1) / 2) * puzzleType. When reading the actual puzzle data from the array, a simple switch statement is used to retrieve from the correct array.

5 6 7 8 9

void initBoard(byte puzzleType, byte puzzleNumber) { byte x = 0; byte y = 0; byte byteRead = 0; byte bytesToRead = (puzzleType % 2 == 0 ? (puzzleType / 2) * puzzleType : ((puzzleType / 2) + 1) * puzzleType); for (int i = (puzzleNumber * bytesToRead); i < ((puzzleNumber + 1) * bytesToRead); i++) { switch (puzzleType) {

case PUZZLE_5X5: byteRead = pgm_read_byte(&puzzles_5x5[i]); break; to be continued next page >> Page 17 of 47


case PUZZLE_6X6: byteRead = pgm_read_byte(&puzzles_6x6[i]); break; case PUZZLE_7X7: byteRead = pgm_read_byte(&puzzles_7x7[i]); break; case PUZZLE_8X8: byteRead = pgm_read_byte(&puzzles_8x8[i]); break; case PUZZLE_9X9: byteRead = pgm_read_byte(&puzzles_9x9[i]); break; } ... } }

Rendering the board The existing code to render the board will happily render puzzles of any size – however with the larger puzzles portions of the board will not be visible.

Later in this article, we will look at how we can make the board scroll as the user moves around.

Storing the Player’s Progress My version of the Pipes game will not let a player advance to the next puzzle in each level until they have completed the one before that. I store and retrieve the progress of each level in the EEPROM using two simple functions as shown below.

#define #define #define #define

EEPROM_START EEPROM_START_C1 EEPROM_START_C2 EEPROM_PUZZLE_OFFSET

Note that they use the Arduboy2 constant EEPROM_STORAGE_SPACE_START to ensure they do not overwrite any system settings. The offset is used to calculate a memory position that takes into account the fact that the puzzleLevel variable is a value between 5 and 9. Subtracting three from this value ensures that it uses contiguous memory spaces.

EEPROM_STORAGE_SPACE_START EEPROM_START EEPROM_START + 1 EEPROM_START - 3

// Game indicator ‘L’ // Game indicator ‘P’

void updateEEPROM(byte puzzleLevel, byte index) { EEPROM.update(EEPROM_PUZZLE_OFFSET + puzzleLevel, index); } byte readEEPROM(byte puzzleLevel) { return EEPROM.read(EEPROM_PUZZLE_OFFSET + puzzleLevel); } to be continued next page >> Page 18 of 47


But what if the game has been loaded onto an Arduboy that already has data in these locations from other applications – at best the player will be able to play levels they have not actually achieved but at worst, there may be data that causes the program to try to retrieve a non-existent level resulting in errors.

I have included a function that initializes the EEPROM if it hasn’t already done so. The function looks for the letters ‘L’ and ‘P’ (Laying Pipe) in the first two characters of the memory range. If these values are not found then it assumes that you have not played the game before and initializes the memory. This function is called once in the setup() routine.

bool initEEPROM () { byte c1 = EEPROM.read(EEPROM_START_C1); byte c2 = EEPROM.read(EEPROM_START_C2); if (c1 != 76 || c2 != 80) { // LP EEPROM.update(EEPROM_START_C1, 76); EEPROM.update(EEPROM_START_C2, 80); ... } }

Selecting a Level Now that we have all of the levels defined and have a way of tracking progress, we need a way for the player to select which of the five levels they would like to play. In a later article, I will add some interest to the menus by circling them with pipes. The code utilizes two variables for capturing the player’s currently selected menu item and the top item to display as the user scrolls through the options. These are the variables levelSelect_selectedItem and levelSelect_ topItem respectively.

The levelSelect_selectedItem is incremented or decremented as the user clicks the up or down buttons within the range of the six level options. If the user presses the ‘B’ button, control is returned back to the splash screen.

byte levelSelect_selectedItem = 0; byte levelSelect_topItem = 0; void levelSelect() { arduboy.clear(); if (arduboy.justPressed(UP_BUTTON) && levelSelect_selectedItem > 0) if (arduboy.justPressed(DOWN_BUTTON) && levelSelect_selectedItem < sizeof(levels) - 1) if (arduboy.justPressed(B_BUTTON))

If the player presses the ‘A’ button, the progress of the selected level is retrieved from the EEPROM. If they select a level where they have completed at least one of the puzzles, they are invited to clear the level and start

{ levelSelect_selectedItem--; } { levelSelect_selectedItem++; } { gameState = STATE_INTRO; }

again or continue on from the last completed puzzle. If the player selects a new level, they are first puzzle is automatically selected.

to be continued next page >> Page 19 of 47


In either scenario the gameplay variable is updated to indicate which routine the main loop() should call on the next pass. I have also included a second variable, prevState, and have set this to STATE_LEVEL_SELECT.

This variable will be used later to determine how a user got to the puzzle select screen and is described in the section Selecting a Puzzle a below.

if (arduboy.justPressed(A_BUTTON)) { puzzle.level = levels[levelSelect_selectedItem]; puzzle.index = 0; if (readEEPROM(puzzle.level) > 0) { gameState = STATE_PUZZLE_SELECT; prevState = STATE_LEVEL_SELECT; puzzleSelect_selectedItem = 0; } else { gameState = STATE_INIT_GAME;

} }

Before rendering the menu, the top item to display is calculated based on the currently selected item. This calculation keeps the selected row in the middle of the three rows being displayed except when the player has scrolled to the top or bottom of the list, as shown in the table here.

The logic seems over the top for five items but the code will work for any number of menu items and visible positions.

Selected Item

Top Row

0

(5 x 5, Practice)

0

1

(6 x 6, Easy)

0

2

(7 x 7, Medium)

1

3 4

(8 x 8, Hard) (9 x 9, Extreme)

2 2

if (levelSelect_selectedItem < levelSelect_noOfItems - 1) { levelSelect_topItem = 0; } else { if (levelSelect_selectedItem > sizeof(levels) - levelSelect_noOfItems + 1) { levelSelect_topItem = sizeof(levels) - levelSelect_noOfItems; } else { levelSelect_topItem = levelSelect_selectedItem - 1; } }

to be continued next page >> Page 20 of 47


Once the top item is calculated, we can render out the three visible menu items. With a little effort this code could be converted to a loop that would support n visible items. For now, the code is hardcoded. renderLevelDetail(MENU_ITEM_3_X, MENU_ITEM_3_1_Y, levels[levelSelect_topItem], (levelSelect_topItem == levelSelect_selectedItem)); renderLevelDetail(MENU_ITEM_3_X, MENU_ITEM_3_2_Y, levels[levelSelect_topItem + 1], (levelSelect_topItem + 1 == levelSelect_selectedItem)); renderLevelDetail(MENU_ITEM_3_X, MENU_ITEM_3_3_Y, levels[levelSelect_topItem + 2], (levelSelect_topItem + 2 == levelSelect_selectedItem)); }

The renderLevelDetail() function renders a single menu item at positions X and Y. If the variable highlight is true, it first renders a white filled rectangle and sets the text colour to black on a white background.

At the end of the routine, it reverts the text back to white text on a black background.

void renderLevelDetail(byte x, byte y, byte level, byte highlight) {

if (highlight) { arduboy.setTextColor(BLACK); arduboy.setTextBackground(WHITE); arduboy.fillRect(x - 1, y - 1, MENU_ITEM_3_WIDTH, MENU_ITEM_3_HEIGHT, WHITE); } arduboy.setCursor(x, y); switch (level) { case PUZZLE_5X5: arduboy.print(“Practice “); arduboy.print(readEEPROM(PUZZLE_5X5) < 10 ? “ “ : “”); arduboy.print(readEEPROM(PUZZLE_5X5)); arduboy.print(“/”); arduboy.print(puzzles_5x5_count); break; case PUZZLE_6X6: ... case PUZZLE_7X7: ... case PUZZLE_8X8: ... case PUZZLE_9X9: ... } arduboy.setTextColor(WHITE); arduboy.setTextBackground(BLACK);

}

to be continued next page >> Page 21 of 47


Selecting a Puzzle As stated earlier, if the player selects a level where they have completed at least one of the puzzles, they are invited to clear the level and start again or continue on from the last completed puzzle. If the player selects a new level, they are first puzzle is automatically selected. The puzzle selection screen looks like this:

The code below handles the selection of a puzzle. I will not dissect it as it is functionally similar to the level section above but there are a few variations.

The first is when the user selects the option to continue playing the next puzzle from the level they have chosen. If the retrieved EEPROM setting reveals that the user has completed all puzzles in the current level, then they are presented with the last puzzle again. You will also notice that when rendering the first menu item, the words Continue Playing are rendered if the player has progressed from the level selection screen. If they have pressed the ‘B’ button whilst playing an actual puzzle, they will be presented with the option Restart Puzzle instead.

void puzzleSelect() { arduboy.clear(); if (arduboy.justPressed(UP_BUTTON) && puzzleSelect_selectedItem > 0) if (arduboy.justPressed(DOWN_BUTTON) && puzzleSelect_selectedItem < 1) if (arduboy.justPressed(B_BUTTON))

{ puzzleSelect_selectedItem--; } { puzzleSelect_selectedItem++; } { gameState = STATE_LEVEL_SELECT; }

if (arduboy.justPressed(A_BUTTON)) {

if (puzzleSelect_selectedItem == 1) { puzzle.index = 0; updateEEPROM(puzzle.level, puzzle.index);

} if (puzzleSelect_selectedItem == 0) { // If all puzzles in the current level are completed, simply re-show the last puzzle ..

puzzle.index = (readEEPROM(puzzle.level) == getNumberOfPuzzles(puzzle.level) ? readEEPROM(puzzle.level) -1 : readEEPROM(puzzle.level)); } gameState = STATE_INIT_GAME; } renderPuzzleOption(MENU_ITEM_2_X, MENU_ITEM_2_1_Y, (prevState == STATE_LEVEL_SELECT ? “Continue Playing” : “ Restart Puzzle “), (puzzleSelect_selectedItem == 0)); renderPuzzleOption(MENU_ITEM_2_X, MENU_ITEM_2_2_Y, “ Reset Level”, (puzzleSelect_selectedItem == 1)); }

to be continued next page >> Page 22 of 47


Enabling Scrolling and Centering the Board To complete the game play, we need to enable scrolling so that the player can reach the lower rows of the bigger puzzles. While modifying the code to support this, we will add some extra functionality to center the board in the screen and to add a ‘scrollbar’ on the right hand side that indicates where the player’s highlighted cell is relative to the rest of the board. When finished, the game play will look like this : To start with, I have defined

const 36, 27, 21, 16, 10, };

byte PROGMEM 4, 0, 0, 4, 10, 42, 4, 8, 36, 4, 7, 31, 4, 6, 28,

another array in the Puzzles.h file that stores a row of information per puzzle type. For each puzzle type we define four parameters that include the X and Y position at which to render the board and a ‘unit length’ and ‘bar length’ for the scrollbar. The scrollbar parameters are used simulate the behaviour of a scroll bar where the ‘slider’ length is proportional to the visible screen length compared to the full puzzle size. More about that later.

puzzles_details[] = { // 5x5 // 6x6 // 7x7 // 8x8 // 9x9

The puzzle structure that is populated at the start of a game and maintains game play information has been extended to capture this additional information. When populating the puzzle structure via the initBoard() function, these additional parameters are retrieved and stored along with the puzzle definition itself. struct Slider { byte unit; byte overall; };

// Number of pixels / row for the slider. // Height of the slider portion in pixels.

struct Puzzle { byte level; byte index; Node maximum; Node offset; Slider slider; byte board[9][9]; } puzzle;

In the previous article we looked at the two functions that handle the node selection and pipe laying, play_NoSelection() and play_NodeSelected() respectively. If you recall, both of these functions finished with a call to renderBoard() which simply rendered the board in the top left corner of the page.

To cater for the offsets, I have added some additional parameters for them as shown in the next page:

to be continued next page >> Page 23 of 47


void renderBoard(int xOffset, int yOffset, byte topRow) { arduboy.clear(); ... for (int y = 0; y < puzzle.maximum.y; y++) { for (int x = 0; x < puzzle.maximum.x; x++) { if (isPipe(x,y)) { (x * GRID_WIDTH) + xOffset, (y * GRID_HEIGHT) + yOffset sprites.drawExternalMask((x yOffset, pipes[getPipeValue(x, y)], pipe_mask, frame, frame); } } } ... }

When rendering the any component of the board, the X and Y offsets are included in position calculations. To implement the scrolling, I have altered the play_NoSelection() and play_ NodeSelected() to calculate a ‘Y’ offset that takes into account the current highlighted row.

The function to calculate the top row to display is shown below. The code is very similar to that which we utilised when displaying the levels menu and aims to keep the highlighted cell in the middle of the screen where possible.

byte calculateTopRow() { byte topRow = 0; if (player.highlightedNode.y <= 2) { topRow = 0; } else { if (player.highlightedNode.y >= 3 && player.highlightedNode.y <= puzzle.maximum.y - 4) { topRow = player.highlightedNode.y - 2; } else { topRow = (puzzle.maximum.y - 5 >= 0 ? puzzle.maximum.y - 5 : 0); } } return topRow; } to be continued next page >> Page 24 of 47


The play_NoSelection() and play_ NodeSelected() utilize the new calculateTopRow() function to determine the top row to display. When the top row evaluates to zero, the rendering of the puzzle starts at the offset position defined in puzzle. offset.x and offset.position.y, as retrieved from the puzzles_details[] array.

As the top row increases the calculated offset gets increasingly larger and negative which forces the board to be rendered from a negative ‘Y’ position.

renderBoard(puzzle.offset.x, puzzle.offset.y - calculateTopRow() * GRID_HEIGHT, calculateTopRow());

The renderBoard() routine will simply render the items regardless of their ‘Y’ values. The Arduboy2 Sprites library contains code that detects when an image will be completely rendered outside the dimensions of the screen. However, it needs to calculate this and this adds some overhead to the rendering.

This does not pose a problem for a game like Pipes but would be a consideration for a fast moving, action game.

Rendering a Scroll Bar Our last little visual trick for his edition of the series is to add a scroll bar that will indicate to the player that the puzzle they are playing is bigger than what the Arduboy can display and where their highlighted cell is relative to the board. The renderBoard() function is altered once again to include some logic to render the scrollbar. The 5 x 5 puzzle does not need a scroll bar but all other puzzle sizes do. To cater for this, the rendering of the puzzle is conditioned by the puzzle.slider.unit value

(one of the values retrieved from the puzzles_ details[] array earlier) being greater than 0. You may have noticed earlier that the 5 x 5 puzzle had this value set to zero. The ‘slider’ part of the toolbar is positioned on the scrollbar to indicate the relative position of the highlighted cell. This is done by calculating the top position using the topRow parameter multiplied by the puzzle. slider.unit value. The height of the slider is a constant per puzzle type.

void renderBoard(int xOffset, int yOffset, byte topRow) { … if (puzzle.slider.unit > 0) { arduboy.fillRect(SCROLLBAR_X - 1, SCROLLBAR_Y, SCROLLBAR_WIDTH + 1, SCROLLBAR_HEIGHT, BLACK); arduboy.drawRect(SCROLLBAR_X, SCROLLBAR_Y, SCROLLBAR_WIDTH, SCROLLBAR_HEIGHT, WHITE); arduboy.fillRect(SCROLLBAR_X + 2, SCROLLBAR_Y + 6 + (topRow * puzzle.slider.unit), SCROLLBAR_WIDTH - 4, puzzle.slider.overall, WHITE); // Top arrow .. arduboy.fillTriangle(SCROLLBAR_X + 4, SCROLLBAR_Y + 2, SCROLLBAR_X + 2, SCROLLBAR_Y + 4, SCROLLBAR_X + 6, SCROLLBAR_Y + 4, WHITE); // Bottom arrow .. arduboy.fillTriangle(SCROLLBAR_X + 2, SCROLLBAR_Y + SCROLLBAR_HEIGHT - 5, SCROLLBAR_X + 6, SCROLLBAR_Y + SCROLLBAR_HEIGHT - 5, SCROLLBAR_X + 4, SCROLLBAR_Y + SCROLLBAR_HEIGHT - 3, WHITE); } to be continued next page >> Page 25 of 47


Where is the code? The sample code detailed in this article can be found in the repository at https://github. com/filmote/Pipes_Article3. The game is totally playable now but needs more cowbells to make it complete.

If you don’t understand that reference, you need to watch the SNL comedy sketch here: https://www.youtube.com/ watch?v=DLeNQKk4uuI

Next Month In the next article, we will add some additional features to make the game a little more professional – sound, an animated splash screen and other decorations.

If we have space, I will discuss how to create a distributable HEX file and an .arduboy file.

Stay tuned for next month’s article.

Laying Pipes - Part 3 (Callouts) by @filmote

Saving User Settings The Arduboy has three different types of memory – 32Kb of memory to store actual programs (PROGMEM) which can only be updated when downloading a new program; 2.5Kb of RAM to hold variables at run time. This memory is volatile and is lost when you turn the power off; and 1Kb of EEPROM which can be updated within a program and is retained even when the power is turned off. EEPROM is ideal for saving user settings, high scores and other information between sessions. EEPROM stands for Electrically Erasable Programmable Read-Only Memory but this is a misnomer as the memory can actually be updated. EEPROMs have a limited life and will

eventually fail after they have been erased and rewritten too many times – this number may be in the millions of operations but a poorly written program that attempts to use it as working memory could easily reach that. The EEPROM class provides three basic functions to read and write a byte of memory, as shown below. The memory location can be anywhere in the 1Kb and equates to a value between 0 and 1023. The update() function differs from the write() function in that it checks the value to be written against what is already stored in order to minimize the number of updates thus prolonging the life of the EEPROM.

EEPROM.read(memory_location); EEPROM.update(memory_location, value); EEPROM.write(memory_location, value); to be continued next page >> Page 26 of 47


An example application might use functions such as those below: #define SAVED_LEVEL

200

byte getSavedLevel() { return EEPROM.read(SAVED_LEVEL); } void updateSavedLevel(byte levelNo) { EEPROM.update(SAVED_LEVEL, levelNo);

The library also offers two other functions that can save and retrieve datatypes other than a byte, such as a float, int or even a structure. EEPROM.put(memory_location, value); EEPROM.get(memory_location, value);

An example of application might save the current game status using the following functions : #define SAVED_STATUS

201

struct GamePlay { byte level; int score; byte board[5][5]; } gamePlay; void saveGameStatus(GamePlay gamePlay) { EEPROM.put(SAVED_STATUS, gamePlay); } void getGamePlay(GamePlay *gamePlay) { EEPROM.get(SAVED_STATUS, *gamePlay); }

A final note: the Arduboy2 library uses the first 16 bytes of EEPROM to store settings such as the unit’s name and the current audio settings. The library also contains a constant, EEPROM_ STORAGE_SPACE_START, which defines the starting memory location for user applications to use.

You are free to use the memory above this location anyway you like however to ensure compatibility with this and future versions of the library, you should define the constants used when saving and retrieving EEPROM values relative to the EEPROM_STORAGE_ SPACE_START constant. An example of this is shown below.

#define SAVED_LEVEL EEPROM_STORAGE_SPACE_START #define SAVED_STATUS EEPROM_STORAGE_SPACE_START + 1

More information about the EEPROM functions can be found at the Arduino site ( https://www.arduino.cc/en/Reference/EEPROM)

Modulus in C / C++ A modulus (modulo, mod) is simply the remainder when dividing one number by another. When we divide 7 by 2 the result is 3 with 1 remainder. Serial.println(7 % 2);

>>

In C / C++ the operand % returns the modulus between two numbers, for example:

Outputs ‘1’.

Page 27 of 47


One ‘gotcha’ with the modulus operation is that it returns the modulus using the same sign as the dividend. Serial.println(7 % 2); Serial.println(-7 % 2); Serial.println(7 % -2); Serial.println(-7 % -2);

>> >> >> >>

Outputs Outputs Outputs Outputs

The sign of the divisor is irrelevant. The examples below shows this in action: ‘1’. ‘-1’. ‘1’. ‘-1’.

Consider the function below. It tests to see if a number is odd by checking the modulus is one. Of course, this will fail if the numberToTest is negative. bool isAnOddNumber(int numberToTest) { return numberToTest % 2 == 1; }

Changing the function to test for a non-zero number - a one or a negative one – accounts for both positive or negative inputs. bool isAnOddNumber(int numberToTest) { return numberToTest % 2 != 0; }

Stay tuned for next month’s article.

Workshop: Make a retro style game in Bangkok

by Gearwalker

Date: 22 October 2017 Time: 10:30 - 16:00 Where: Fab Cafe, Bangkok, Thailand

Sign up here!

Thai maker, Gearwalker, will be holding a game making workshop in one of Bangkok’s trendy cafes. Just bring your laptop, as an Arduboy will be provided in the course fee. If you’ve always wanted to make a game for the Arduboy, and are in Bangkok, this is your chance! Page 28 of 47


Level: Advanced

Scoped Enumerations by @filmote

Scoped Enumerations Hopefully by the time this article is published I will have finished a stripped-down version of the classic 1943 game that I have been working on over the last month. I think I have rewritten sections of the code four or five times as I learnt new tricks in C++ !

over the standard enumeration found in C++. This article briefly describes scoped enumerations in C++11 and then delves into a concept called ‘Operator Overloading’ which allows the standard functions like ++, --, <, > and == to be extended to suit your needs.

One ‘trick’ I discovered was the scoped enumeration (also known as an enumeration class) that provides additional functionality

What are Scoped Enumerations? Almost all programming languages provide a concept of an enumeration – a set of related constant values. In most languages, enumerations simply resolve to integer values and can be cast back and forth as needed.

In C++ this casting is done implicitly and it is this capability of swapping from enumeration elements to the underlying value that can lead to problems. A simple enumeration that describes the suits in a deck of cards is shown below:

enum { Spades, Clubs, Diamonds, Hearts } ; int suitInPlay = Hearts; suitInPlay++;

A couple of things to note about this code is that it is not ‘type safe’ due to the variable suitInPlay being declared as a simple integer and therefore being able to accept any valid int value rather than only those nominated in the enumeration. The increment is also problematic as it takes a valid declaration of the suitInPlay (Hearts which in turn has

an underlying value of three) and then increments it to a value outside of the enumeration. C++ 11 extends the basic enumeration type with scoped enumerations that overcomes this problem. The following code shows a scoped enumeration definition:

enum class Suits { Spades, Clubs, Diamonds, Hearts } ; Suits suitInPlay Suits::Clubs;

to be continued next page >> Page 29 of 47


Attempting to increment the variable or convert it to a numeric will fail. The declaration does not imply an integral data type - byte, integer or long - and the developer should not make any assumptions as to the type.

The underlying data type can be specified if there is a need to do so, as shown below:

enum class Suits : uint8_t { Spades, Clubs, Diamonds, Hearts } ; Suits suitInPlay = Clubs; Serial.print(“suitInPlay = “); Serial.println((uint8_t)suitInPlay);

Output: suitInPlay = 3;

However, attempting to assign a numeric value to a variable of type Suits will fail. Likewise attempting to assign an element of the Suits enumeration to an integer variable will also fail.

Overloading Operators One major benefit of using a class enumerator is the ability to override the standard operators like ++, --, <, > and == to suit your needs. In my 1943 game, the enemy boats have gun turrets that rotate to point towards the player as they pass each other. To record the direction of the turret, I have created a scoped class called Direction as shown below. As you would expect, the enumeration lists the

various directions from North to NorthEast in order. In my code, the actual values assigned to these elements are irrelevant and the default value of zero has been assigned to North, one to NorthEast and so forth up with NorthWest having a value of 7. I have an additional direction, called Unknown, which is assigned the value of 8.

enum class Direction : uint8_t { North, NorthEast, East, SouthEast, South, SouthWest, West, NorthWest, Unknown };

to be continued next page >> Page 30 of 47


A simple override .. Values in an enumeration can be compared together using the standard equality and inequality comparisons. However, the introduction of the Unknown element in the Direction enumeration allows me to illustrate how we can override the standard operation.

When comparing two directions together, the comparison should return false if either or both arguments are equal to Direction::Unknown. The following operator override demonstrates this:

inline bool operator==(const Direction lhs, const Direction rhs) { return ((uint8_t)lhs == (uint8_t)Direction::Unknown ? false : (uint8_t)lhs == (uint8_t)rhs); }

Putting this operator to work reveals the following results: Direction a = Direction::East; Direction b = Direction::East; if (a == b) { Serial.println(“1 a is equal to b”); } a = Direction::NorthWest; b = Direction::North; if (!(a == b)) { Serial.println(“2 a is not equal to b”); } a = Direction::Unknown; b = Direction::Unknown; if (!(a == b)) { Serial.println(“3 a is not equal to b”); }

Output: 1 a is equal to b 2 a is not equal to b 3 a is not equal to b

Note that in the last test, the comparison between the two Unknown directions returned false due to the rule created in the equality comparison. Hang on, what’s with the (!(a == b)) syntax? What about using the standard ‘not equals’ syntax?

As we have overridden the equality operator we need to override the inequality operator as well. The inequality operator shown below simply calls the modified equality operator and returns the NOT of it.

inline bool operator!=(const Direction lhs, const Direction rhs) return ! (lhs == rhs); }

{

With this new operator override in place, we can use the standard notation shown in the next page:

to be continued next page >> Page 31 of 47


... a = Direction::NorthWest; b = Direction::North; if (a != b) { Serial.println(“2 a is not equal to b”); } a = Direction::Unknown; b = Direction::Unknown; if (a != b) { Serial.println(“3 a is not equal to b”); }

Output: 1 a is equal to b 2 a is not equal to b 3 a is not equal to b

Getting more complex .. So now we can compare directions, what about comparing values? Assume the boat is at the bottom of the screen entering from the right and leaving towards the left. If the player is stationary and located at the top-centre of the screen then to shoot towards the player the gun will need to be pointed towards the North-West initially and will swing through North and then towards the North-East as the boat leaves the screen. In my code, I was hoping to compare the current direction of the turret to the player’s position and simply add or subtract one to swing it around as required. Although the enumeration details an order to the directions where South is greater than SouthEast (in

a clockwise view of the world), it does not consider that North is greater than NorthWest, ie it has no concept of the circular nature of a compass. The following operators override the standard ‘less than’ and ‘greater than’ operators. Before calculating a return value, the operator determines whether it needs to consider the resetting of values at North by subtracting the integer values from each other. Where the comparison must cater for North, adding 8 (a full circle) to the either the right or left hand side argument (for ‘less than’ or ‘greater than’ respectively) ensures that the comparison caters for this appropriately.

inline bool operator<(const Direction lhs, const Direction rhs)

{

return (abs((uint8_t)lhs - (uint8_t)rhs) < 4 ? (uint8_t)lhs - (uint8_t)rhs : (uint8_t)lhs (8 + (uint8_t)rhs)) < 0; } inline bool operator>(const Direction

lhs, const Direction

rhs)

{

return (abs((uint8_t)lhs - (uint8_t)rhs) < 4 ? (uint8_t)lhs - (uint8_t)rhs : (8 + (uint8_t) lhs) - (uint8_t)rhs) > 0; }

Putting these two operators through their paces reveals the correct results shown on the next page:

to be continued next page >> Page 32 of 47


Direction a = Direction::East; Direction b = Direction::South; if (a < b) { Serial.println(“1 East is less than South”); } if (b > a) { Serial.println(“2 South is greater than East”); } a = Direction::NorthWest; b = Direction::North; if (a < b) { Serial.println(“3 NorthWest is less than North”); } if (b > a) { Serial.println(“4 North is greater than NorthWest”); } a = Direction::NorthWest; b = Direction::NorthEast; if (a < b) { Serial.println(“5 NorthWest is less than NorthEast”); } if (b > a) { Serial.println(“6 NorthEast is greater than NorthWest”); } a = Direction::North; b = Direction::NorthEast; if (a < b) { Serial.println(“7 North is less than NorthEast”); } if (b > a) { Serial.println(“8 NorthEast is greater than North”); }

Output: 1 2 3 4 5 6 7 8

East is less than South South is greater than East NorthWest is less than North North is greater than NorthWest NorthWest is less than NorthEast NorthEast is greater than NorthWest North is less than NorthEast NorthEast is greater than North

Now that we have the ‘equality’, ‘greater than’ and ‘less than’ operators in place, we can add the following two operators to complete the comparisons.

Note they perform the logical NOT of those operators previously defined.

inline bool operator<=(const Direction lhs, const Direction rhs) { return !(lhs > rhs); } inline bool operator>=(const Direction lhs, const Direction rhs) { return !(lhs < rhs); }

And just plain confusing .. Not really confusing but overriding the standard increment and decrement operators reveal an interesting compiler trick that you just have to take for granted actually works. The operator below describes a pre-increment operator.

If the supplied direction is NorthWest it returns North otherwise it returns the direction adjacent to the supplied on in the enumeration.

inline Direction &operator++( Direction &c ) { c = ( c == Direction::NorthWest ) ? Direction::North : static_cast<Direction>( static_cast<uint8_t>(c) + 1 ); return c; } to be continued next page >> Page 33 of 47


A post-increment operator includes an anonymous second parameter of type integer. This parameter is not used in the code and is simply there to differentiate between the pre- and post- increment functions.

The compiler must recognise this method signature and assume it is a post-increment function. The implementation of this operator simply calls the pre-incrementer and returns its value.

inline Direction operator++( Direction &c, int ) { Direction result = c; ++c; return result; }

The decrement functions are nearly identical to the increment operators and are shown below for completeness. inline Direction &operator--( Direction & c ) { c = ( c == Direction::North ) ? Direction::NorthWest : static_cast<Direction>( static_cast<uint8_t>(c) - 1 ); return c; } inline Direction operator--( Direction & c, int ) { Direction result = c; --c; return result; }

Conclusion As you can see, the scoped enumerations provide an extra layer of functionality beyond what simple enumerations offer. The ability to override the operators encapsulates the logic of the enumeration itself and saves the developer having to repeat similar logic across an application. In the examples shown in this article, the developer does not need to remember the intrinsic values underlying the directions North or East nor does he need to consider how to increase the value when it reaches the end of the enumeration.

In fact, all of the code would work if you change the initial enumeration declaration to (say) North = 57 .. I have included a simple application on GitHub that shows the enumeration in action : https://github.com/filmote/ScopedEnums

Page 34 of 47


Level: Advanced

FixedPoints by @Pharap & @filmote A document describing the use of FixedPoint numbers in the Arduino / Arduboy environment.

Floating Point versus Fixed Point Numbers Most of us are familiar with the integer data types used by the Arduboy environment – of various lengths, both signed and unsigned. We take care to choose appropriate data types that consider both the range of our data as well as the memory required to store them. Operations involving integer values are incredibly efficient and for many sketches are all that are required. Sometimes however, integer values are simply not flexible enough. Imagine trying to calculate the movement of an accelerating rocket or calculate the trajectory of a missile in an Arduboy version of Choplifter. (Now there’s a good idea for an Arduboy project!) To make these calculations, you need numbers with both an integer and fractional component. The typical way to represent such numbers on a computer is through floating points. C++ includes two floating point data types of different precision – the float and the double. On most platforms the float is 4 bytes and the double is 8 bytes, however on the Arduino (and thus the Arduboy) they are actually same precision (4 bytes) and are therefore interchangeable. These data types can store numbers in the range of -3.4028235 x 1038 to +3. 4028235 x 1038 (for comparison there is estimated to be 7.5 x 1018 grains of sand in the world) but at a precision of only 6 or 7 decimal digits. For the Arduboy, in contrast to integer operations, floating point calculations are internally quite complicated and thus incredibly processor intensive. Including numerous calculations to reposition the player and enemies within the main loop of a program might prevent the game from performing well or worse yet even being playable. However, there is an alternative to floating point arithmetic. This alternative is fixed point arithmetic. Fixed point numbers behave more similarly to the integer values you are familiar

with. As such, the processing power required to manipulate fixed point numbers is more similar to integer calculations than to floating point calculations. Using fixed point numbers, that version of Choplifter might be possible after all! There is however a tradeoff for the benefit that fixed point data types bring. The tradeoff is that you must explicitly nominate the size of both the integer and fractional portion of the variable – much in the same way you nominate the available range when choosing integer data types. In addition to the obvious data range restrictions of the integer component, the resolution (or ‘granularity’) of the fractional component must also be chosen. For example, an unsigned 8 bit fixed point type with a 4 bit integer part and a 4 bit fractional part would permit an integer range of 0 to 15 and a fractional range of 0/16 to 15/16. This equates to a range of 0 to 15.9375 (15/16 is equivalent to 0.9375). Note that the fractional part can only go up in increments of 1/16 (i.e. 0.0625), hence that particular format cannot accurately represent numbers at a lower resolution, for example the value 0.05 could not be accurately represented, it would have to be rounded to 0.0625. However, by using larger types or by increasing the number of bits afforded to the fractional part at the expense of the integer part you can balance the format to suit your needs. For example, an unsigned 16 bit type with 8 bits of integer part and 8 bits of fractional part could represent all the positions on the Arduboy’s screen, whilst providing a reasonable range of fractional steps in between the precise pixel locations. This would mean having much smoother movement than using regular integers, but in a way that’s cheaper than floating points in terms of both code use and processing power. to be continued next page >> Page 35 of 47


Fixed Points in Action In the examples below, I am using an early alpha release of @Pharap’s FixedPoints library @Pharap’s library defines a number of signed and unsigned data types which are listed at the end of this article. It also includes overrides for the common arithmetic and comparison operators (+, -, / , *, <, >, etc) as well as some handy functions like ceilFixed(), floorFixed() and roundFixed().

Please note that an extra ‘level of precision’ argument is being used in the print code here. Without supplying this extra argument, the printed results suffer from rounding errors made by the Serial.print and Serial.println functions to simplify the code for printing floats. Firstly, some simple assignments and calculations.

SQ7x8 myVar = 7.0; myVar = myVar + 3; Serial.print(“static_cast<float>(myVar) : “); Serial.println(static_cast<float>(myVar), 4);

Output: static_cast<float>(myVar) : 10.0000

Simple. Now let’s introduce a decimal place. SQ7x8 myVar = 3.7; Serial.print(“myVar.getInteger() : “); Serial.println(myVar.getInteger()); Serial.print(“static_cast<float>(myVar) : “); Serial.println(static_cast<float>(myVar), 8);

Output: myVar.getInteger() : 3 static_cast<float>(myVar) :

3.69921875

But hang on, we set the variable to 3.7 and it printed out 3.69921875. Why so? As mentioned earlier, both floating point and fixed point numbers introduce a degree of inaccuracy when storing numbers. With Fixed Point numbers, the precision (and by extension the accuracy) is specified by the programmer. In the sample code above, the variable chosen was a two-byte incarnation and it can store a number roughly between -128 and +128. The integer component is stored in 7 bits which allows a range of 0 to 127 and the fractional part is stored in 8 bits and has a range between 0 / 256 to 255 / 256. The largest fractional portion (255 / 256) converted to decimal is 0.9961 hence the data range cannot quite store +128, but it can store up to 127.9961.

If you could view the bits in the fractional part of the variable (which you can through myVar. getFraction()), you would see that it is stored as 179. As it turns out 179 / 256 is equal to 0.69921875. The inaccuracy introduced by a relatively small (two-byte) fixed point is irrelevant if all you are trying to do is simulate some realistic movements of characters in a game. If you have a need to calculate more accurate values, you can simply increase the size of the fixed point variable to either an SQ15x16 or an SQ31x32 (though be aware that some operations do not work on SQ31x32 due to presently unavoidable restrictions). The code below illustrates the accuracy of each data type.

to be continued next page >> Page 36 of 47


SQ7x8 myVar7x8 = 4; for (uint8_t i = 0; i <= 10; ++i) { Serial.print(static_cast<float>(myVar7x8), 8); Serial.print(‘ ‘); myVar7x8 = myVar7x8 + 0.1; } Serial.println(); SQ15x16 myVar15x16 = 4; for (uint8_t i = 0; i <= 10; ++i) { Serial.print(static_cast<float>(myVar15x16), 8); Serial.print(‘ ‘); myVar15x16 = myVar15x16 + 0.1; } Serial.println(); SQ31x32 myVar31x32 = 4; for (uint8_t i = 0; i <= 10; ++i) { Serial.print(static_cast<float>(myVar31x32), 8); Serial.print(‘ ‘); myVar31x32 = myVar31x32 + 0.1; } Serial.println();

Output: 4.00000000 4.78125000 4.00000000 4.79992675 4.00000000 4.80000019

4.09765625 4.87890625 4.09999084 4.89991760 4.09999990 4.90000009

4.19531250 4.29296875 4.39062500 4.48828125 4.58593750 4.68359375 4.97656250 4.19998168 4.29997253 4.39996337 4.49995422 4.59994506 4.69993591 4.99990844 4.19999980 4.30000019 4.40000009 4.50000000 4.59999990 4.69999980 5.00000000

One thing to note with the output is the compounding effect that adding two inaccurate numbers results in. This can be seen easily if we repeat the same exercise on the smallest, one-byte data type as shown below. The fractional part of an SQ3x4 number is stored in 1/16th increments or 0.0625 in decimal.

When we add the first increment of 0.1 we immediately introduce an inaccuracy as the number gets rounded down to the nearest 1/16th of a number. As we repeat the addition, the error increases. The following code shows that.

SQ3x4 myVar3x4 = 4; for (uint8_t i = 0; i <= 10; ++i) { Serial.print(static_cast<float>(myVar3x4), 4); Serial.print(“ “); myVar3x4 = myVar3x4 + 0.1; }

Output: 4.0000 4.0625 4.1250 4.1875 4.2500 4.3125 4.3750 4.4375 4.5000 4.5625 4.6250

Choosing larger fixed point data types increases the range of the number and accuracy of the fractional component at the expense of memory.

to be continued next page >> Page 37 of 47


Why would I bother with Fixed Points at all? Imagine you are building a sideways-scrolling game where aliens (or cars, planes or other moving objects) are moving towards you at various speeds. With each pass through the program’s loop you could decrement the x coordinate of each alien but this would result in all aliens moving at the same speed. You could try to move aliens every second or third time through the loop by keeping some sort of counter but this can be error prone and

may result in the aliens moving slowly. To really allow flexibility in the aliens’ movement, you could store the x and y in fixed point variables and increment these by a decimal value allowing the aliens to move at similar but slightly different speeds. Consider the Alien class below. Each alien is described with an x and y coordinate and takes care of its own movement.

class Alien { public: Alien(SQ7x8 x, SQ7x8 y, SQ7x8 incX, SQ7x8 incY, const uint8_t *bitmap) { _x = x; _y = y; _incX = incX; _incY = incY; _bitmap = bitmap; } SQ7x8 getX() const { return _x; } SQ7x8 getY() const { return _y; } void setX(const SQ7x8 value) { _x = value; } void setY(const SQ7x8 value) { _y = value; } inline void Alien::move(uint8_t frame) { _x = _x - _incX; _y = _y - _incY; Sprites::drawOverwrite(_x.getInteger(), _y.getInteger(), _bitmap, frame); } private: SQ7x8 SQ7x8 SQ7x8 SQ7x8 const

_x; _y; _incX; _incY; uint8_t *_bitmap;

};

And our main .ino file: Alien alien1 = {127, 16, (SQ7x8)0.9, (SQ7x8)1.34, alienBitmap }; Alien alien2 = {127, 32, (SQ7x8)1.7, (SQ7x8)1.76, alienBitmap }; Alien alien3 = {127, 48, (SQ7x8)1.5, (SQ7x8)2.13, alienBitmap }; void loop() { alien1.move(frame); alien2.move(frame); alien3.move(frame); }; to be continued next page >> Page 38 of 47


With fixed point data types, we can have our aliens move at slightly different speeds without the overhead of using floating points.

Common Type Aliases @Pharap’s library includes the following data types. However the library is implemented with templates and it is a relatively simple matter to declare your own type aliases.

The predeclared aliases (which can be used as an example of how to declare such an alias) can be found in src\ FixedPointsCommon\SFixedCommon.h and src\ FixedPointsCommon\UfixedCommon.h.

Unsigned Data Type Aliases:

Data Type

Size

Integer Range Fractional Resolution

Effective Range*

UQ4x4

8 bit (1 byte)

0 – 15

1 / 16th

0 to 16

UQ8x8

16 bit (2 bytes)

0 to 255

1 / 256th

0 to 256

UQ16x16

32 bit (4 bytes)

0 to 65,355

1 / 65,536th

0 to 65,356

UQ32x32

64 bit (8 bytes)

1 / 4,294,967,296th

UQ1x7

8 bit (1 byte)

0 to 4,294,967,295 0 to 1

1 / 128th

0 to 4,294,967,296 0 to 1

UQ1x15

16 bit (2 byte)

0 to 1

1 / 32,768th

0 to 1

UQ1x31

32 bit (4 byte)

0 to 1

1 / 2,147,483,648th

0 to 1

UQ1x63

64-bit (8 byte)

0 to 1

1/ 9,223,372,036,854,775,808th

0 to 1

Signed Data Type Aliases:

Data Type

Size

Integer Range Fractional Resolution

Effective Range*

SQ3x4

8 bit (1 byte)

-15 to +15

1 / 16th

-16 to +16

SQ7x8

16 bit (2 bytes)

-127 to +127

1 / 256th

-128 to +128

SQ15x16

32 bit (4 bytes)

SQ31x32

64 bit (8 bytes)

-32,768 to +32,768 2,147,483,648 to +2,147,483,648

SQ1x6

8 bit (1 byte)

-32,767 to 1 / 65,536th +32,767 -2,147,483,647 1 / 4,294,967,296th to +2,147,483,647 -1 to +1 1 / 64th

SQ1x14

16 bit (2 byte)

-1 to +1

1 / 16,384th

-2 to +2

SQ1x30

32 bit (4 byte)

-1 to +1

1 / 1,073,741,824th

-2 to +2

SQ1x62

64-bit (8 byte)

-1 to +1

1/ 4,611,686,018,427,387,904th

-2 to +2

-2 to +2

* Lower bound is inclusive, upper bound is exclusive

FixedPoints can be found through the Arduino IDE library manager. If you have not used thelibrary manager before see here: https://arduboy.com/download-and-learn-arduino/ Page 39 of 47


In this segment of the magazine we feature work from pixel artists. Do contact them if you would like to work with them or use their work. Their contact information is listed below.

“When I was invited to contribute to this page, I decide to recreate all the sprites from one of my first games, using only black and white like the Arduboy screen. Working with such limited palette is quite challenging, but also a great creative exercise. I really liked the result and now I’m seriously considering to develop Arduboy versions of my games.”

Artisit: @maurihelme Twitter: https://twitter.com/maurihelme Website: https://maurihelme.com


Are you a pixel artist? Done some Arduboy Fan art? Wish to have your work shared with the Arduboy community? Send arduboymag a message through the Arduboy community page or a DM on Twitter!


EXPLORE:

In this section we’ll ask the same 10 questions to a different developer in every new issue.

Questions with game developers by @celinebins / @widehaslet

This issue we interview @JuiceLizard. Creator of the recent The guy who never complains; Arduboy’s first e-book!

1. Tell us a little bit about yourself.. Hi, I am Juice Lizard, male 30 years old. I live in France, near Paris. I studied fine art and now I try to sell my paintings and drawings through exhibitions. I also give some drawing lessons to people from 7 to 77 years old, not in schools but in their home directly. I began to make drawings and play video games 20 years ago. But I learn programming for only one year and a half. “The guy who never complains” is my first release as a programmer. Coding feeds my work and I like to mix it with painting. But I don’t know if I will do that in a professional way. Now it is just something I make for fun. I don’t do that with a lot of pressure. I am still learning. One of my friends told me to begin programming with something simple, so I tried to make a little ebook, not a game, because it just shows some text on the screen with very simple interactions. I still had some difficulties, that I resolved with time and luck. I first wrote all the text on a regular computer. But I really wanted to present it on Arduboy, not on

paper or Internet. It was very important for me. In my work as an artist, chosing the appropriate medium is as important as chosing a good subject or good tools. Literature on Arduboy is a sort of electric gadget from a funny retro-future. It fits for something we wear in our pocket and we take a small bit sometimes. If we compare a standard book we buy ten dollars to a meal in a restaurant, “The guy who never complains” is a pack of candy. When I first tried to implement my text to the Arduboy screen, it didn’t work because the scrolling produced flickering each time a new line appears and the memory was quickly filled. So I used still screens and I discovered the “F()” macro to put all the text in the program memory. I spent time cutting my text to put it in the screen. The arduboy can show 21 characters for each line and 8 lines by screen. So I counted every letter until 21 and made the hyphenations in the correct places. I used online hyphenation tools to help me because English is not my native language.

to be continued next page >> Page 42 of 47


Finally, I found in the Arduboy website forum how to display special characters on the screen, without using sprites. Because some letters needed accents. I could finish my ebook as I wanted, even thinking about adding some extras. But I didn’t try to use all the memory in the device. So now you know: if you want to make an ebook on Arduboy, it could contains roughly 20 minutes of reading at max. Not to bad for something free.

2. Where did you find out about Arduboy the first time? It was on Internet with the Kickstarter campaign. I remember the video where we see Kevin Bates throwing the device on the floor to show how strong it is and telling “Don’t try this at home!”. I also remember that I waited a long time before finding the Arduboy in my french mail box.

3. What is the first program/game you created for Arduboy? “The guy who never complains” is the first program I created and published for Arduboy. First I wrote the text entirely on sketchbook or computer and then tried to code to put it inside the Arduboy. It was clear when I began to write it that it will be for fun and it will fit specially to the Arduboy. I own other open source gaming devices (Pocket Chip, Gamebuino, Kitco) but Arduboy seemed to be the best for my little ebook. Did you have app / game development experience before Arduboy? I learned about Arduino (hardware and software) with some french massive open online courses. I particularly appreciated “ABC du langage C” and “Programer en C” with Rémi Sharrock on www.fun-mooc. fr . We could do coding exercises directly on their website and it was great for true beginners like me.

4. What programs do you use for creating Arduboy apps/games? I use the Arduino IDE on my MacBook. Gimp is a free software I use for creating the sprites. I convert them with ToChars from Crait or Image Converter from TEAM a.r.g.

5. What do you read to learn how to code for Arduboy? A friend who is working in programming offered me a french book to learn C language: “Programmer en langage C, Cours et exercices corrigés” by Claude Delannoy. It is a good book.

6. What app/game do you currently have on your Arduboy. “The guy who never complains” on my golden Arduboy and “Bip Bipbip” on my standard one. I made a pledge on Kickstarter for an Arduboy Arduventure edition. I think the game Arduventure will stay a long time on that Arduboy because I am not sure to be able to load it again in the device if I load an other game. On my MacBook I can’t have the latest version of the Arduino IDE and for some games the compiling says the sketch is too big (over 100%). I had to delete some big sprites in the Circuit Dude code to be able to play it.

7. What app/program is your favorite? “Leanna the lion”. My high score is 310.

to be continued next page >> Page 43 of 47


8. What is the next app/program for Arduboy we can expect?

9. What app/game would you love to see on the Arduboy?

I have planned some new games for Arduboy, and as I began to create different things for this device, I have more projects in mind.

Ardu Meat Boy Forever!

You could expect a quick time event, a karaoke with a funny song that I wrote and a platform game inspired by “Super Meat Boy”, “The Binding of Isaac” and “Super Crate Box”: I called it “The Binding of Sugar Boy”. Maybe I will publish those games soon, maybe next decade, maybe never. We’ll see..

10. What is your best tip for other people who want to start creating apps/games for Arduboy? My advice is to look inside the codes from different games already done and to change them by different ways: the sprites, the text, the speed, the gameplay, etc. and to have fun. Learn step by step, and for your first game, don’t try to implement all the possible features of the Arduboy. Try to focus on one or two simple things (for example: a game with only rectangles displayed on screen).

View more of @JuiceLizard’s work here: http://juicelizard.canalblog.com/

Are you a developer and want to be featured here? Reach out to the Magazine through Twitter DM @arduboymag Page 44 of 47


Up coming events ! Join the Arduboy team at these events in October! Come say hi to us, try out some demo units (if you do not have an Arduboy yet), ask us questions, and enjoy special event discounts. ;)

Portland Retro Gaming Expo Portland, ORegon usa Retro Gaming Expo, Inc. is a Portland, Oregon-based 501(c)4 non-profit cooperative organization dedicated to creating awareness of, and appreciation for classic video and arcade games through the presentation of events and conventions that celebrate the historic contribution video games have made and continue to make in popular culture. Look out for the 7th Tetris World Championship.. It’s extra special this year ;)

Get tickets here: http://www. retrogamingexpo.com/ Page 45 of 47


#arduboy: Arduboy

is

W.I.P

Arduventure was a work in progress for a long time, and this month we are celebrating all WIPs! These are taken from Instagram and Twitter. Do you recognize any completed games?

@rockasoo

@garycmartin

@JOERI

@ShdwWzrd

@tadfmac

@Lemmo

@crait

@DavitMasia

@a1k0n

Post your photos with Arduboy on Instagram and Twitter with #arduboy. We want to feature them in this segment! Page 46 of 47


Thank

you!

Thank you for reading the Magazine! Hope you enjoyed it as much as we did putting it together. We want to know how to make Volume 10 better than the last, so write in to us to tell us what you think!

https://twitter.com/arduboymag


Turn static files into dynamic content formats.

Create a flipbook
Issuu converts static files into: digital portfolios, online yearbooks, online catalogs, digital photo albums and more. Sign up and create your flipbook.