Our goal for this tutorial is to show you how to develop a simple game for the Tiny Arcade that is written entirely in the Arduino IDE. Many of our other games have been developed using game engines. As effective as this method may be, it is outside the scope of this tutorial. While some tasks may become more tedious or time-consuming, the barrier to entry is much lower, allowing users with minimal coding knowledge to begin experimenting with game development in the C language. We'll start with some basics and concepts, then work our way up to developing a LITE version of Tiny Brick. Let's get started!
If this is your first time using a Tiny Arcade or a TinyScreen+, follow this tutorial for information on how to set up the boards and libraries needed to work with your Tiny Arcade.
While developing your game, you can plug your arcade into a USB port on your PC. Once it's plugged in, hold down both push-buttons while powering on the unit. A black screen reading "TinyArcade Bootloader Menu" should appear. Your arcade should now be visible to the Arduino IDE and have an assigned COM port. In order to upload, your selected "Programmer" should be "Arduino as ISP" as shown below.
Now we can begin our programming. Add some comments at the very top of the code, including the program title with a brief description, your name, today's date, and a line that will indicate the date of the last code update.
I typically follow this same format each time I write code, as shown above. Note that as you update your code and implement changes, you'll need to updated the date. I usually do this each time I open code, and each time I close, or leave code (e.g. minimizing the IDE then coming back to it later).
After these comment lines, we'll add our library files. We'll need our header files, GameTutorialSprites.h and TinyArcade.h, as well as the TinyScreen library, Wire library, and SPI library. Wire and SPI can be downloaded in the Arduino Library Manager. Click here to download TinyArcade.h, and copy it to the same folder as your sketch.
The image below shows how to reference your header files and your libraries in your code and shows the tabs for your two header files at the top of the editing environment. If you cannot see your TinyArcade.h file, you may need to close and reopen your Arduino IDE.
Once all of the libraries and header files are set up, we'll start by defining any extra pieces we'll need throughout our game code and our setup routine. We'll be defining a type that holds information for a sprite, a 2-D image that is a part of the graphics for our game. This type needs to hold the code that generates the sprite's pixels, the dimensions of the sprite, the sprite's initial position coordinates, and any other information that may be relevant to your gameplay. We'll be using a struct to accomplish this. (Read more about structs here.)
For our tutorial's sprite type, we'll define our sprite's initial x and y coordinates, its height and width, and the number of times another sprite has collided with it. We will also include an "unsigned" int that references the code used to generate the sprite. We will name this type "ts_sprite," which comes at the end as shown above. We can now use the type name "ts_sprite" to make a sprite in our code.
We'll then specify our screen as the TinyScreen+ and create the display object.
In the setup routine, we'll fully configure the screen settings and initialize the processor's serial port. This will allow us to access the port directly and print Serial data to the Serial Monitor during code execution. This will also allow us to program the Tiny Arcade without putting it into bootloader mode each time we want to update the program.
At this point, you should have the following:
Notice how this code contains all 3 tabs at the top (main sketch, sprites header file, and TinyArcade.h file). We'll come back to the main sketch, but let's move over to the sprites header file for now.
Click on the GameTutorialSprites.h tab to open and edit it. We will need to include the TinyScreen library in our header file using the same code in the main sketch. We will declare ALPHA as an external variable, which we will use as a placeholder for transparent or "empty" pixels. Next, we'll create our first bitmap! We are going to make a simple ball using white pixels.
We want our ball to look like the image below. Each pixel has been labeled with its color.
In order to code this into our game, we have to turn these pixels into an array of colors. (Click here for more information on arrays.) The code below demonstrates how to do that.
The image above shows what we now have in our sprites header file. The ballBitmap is a linear array of colors, arranged how we would like the ball to appear in the game using line breaks. Let's go back to the main sketch and add the code to make the ball appear on the screen!
NOTE: You can find our TinyScreenReferenceManual online at GitHub. In it, you'll find useful information about the library's functions and how to use them in your code. The last few pages also include the pre-defined colors that can you can use in creating bitmaps/sprites. You can also declare your own 8-bit or 16-bit colors.
In order to manipulate our screen image, we'll need to update the screen each time we want our graphics to move. For this task, we'll use the drawBuffer() routine that was used in our FlappyBirdz game.
The drawBuffer routine uses pointers to increment through the sprites and draws the screen bit by bit, line by line from the top to the bottom. It also finds the pixels named ALPHA and assigns them the same color as the background, essentially making them transparent. You can manually copy the function from here, or pull it from the FlappyBirdz or TinyBrick game code. We place this function outside of the setup and loop routine, and simply call it when we want to update the screen from the loop routine (usually every loop execution).
Now that we have a bitmap to draw and a method for drawing it, let's complete the task of declaring the sprite in our main code. Above the setup routine, we will declare our sprite named ball. Just like a variable, it has a type (ts_sprite), a name (ball) and a value or values. As described above, these values represent that the ball's initial location will be at (44,28) on the screen, its bitmap is 4 pixels wide and 4 high, it hasn't collided with any other bitmaps, and is drawn by using ballBitmap from the header file. (To declare any other sprites, we would use this same list of variables in the same order between curly braces.)
The variable amtSprites will need to be set equal to the number of sprites used in the code. For now, we only have 1. As we add more and more sprites, we will need to change the value of this variable to match. The next variable, *spriteList, is an array containing the addresses of each of our sprites (the & symbol indicates an address). The drawBuffer() function uses this array to go through each sprite and draw it on the screen. We can also use this array in other areas of our code when we need to go through each of our sprites. I have also added another variable named backgroundColor, which we can use to easily change the background color of the screen. To use drawBuffer in our game, we add it to our loop() routine as shown above.
After all of that setup work, we can finally upload the code to our TinyArcade. Plug in the TinyArcade and power it on while holding down the 2 push-buttons. Then, release them. Remember, this will only be necessary during the first programming. After that, the USB port on the chip will be enabled and we can reprogram our TinyArcade without putting it into bootloader mode each time. To upload the code, simply press the upload button in the Arduino IDE!
If you were successful, you should now see a small white ball in the center of a black screen. Nothing too exciting, but now we can begin to do the fun work of making things move, collide, deflect, etc.
Now that we know we can create and display a sprite image, let's make it move around on the screen! To do this, we will need to read the inputs from the joystick and the two push-buttons. These have been configured by arcadeInit(), which we added to the setup routine. If we open the TinyArcade.h header file, we can see that these have already been assigned names that we can use to read their data as well as functions to accomplish this task.
Note: The header file can also adjust for the newer or older style of arcade by reading a digital pin on the PCB that indicates which is which. This is important because the older style arcade used a true analog joystick, while the newer units use a digital joystick. The header file's two built-in functions, checkJoystick() and checkButton() can be used to check the inputs, rather than using the digitalRead() function and pinouts. Adding the following lines to the code will make the ball move around on the screen as you would expect. The speed of the ball is dictated by the value which we increment or decrement the coordinate values. Here, we use all 1s. If we made this greater, the ball would move faster, or even "jump" several pixels at once. Play around with this to see what combination of speed and pixel distance looks good. You could also make a speed variable to replace the 1, and adjust it throughout your code.
Pressing the left button will reset the x position to zero, and the right button will reset the y position of the ball to 0. We can cut these lines of code out of our loop routine and put them in a function that we can call from loop() (i.e. moveBall). This will keep our loop routine clean and easy to follow.
Now, if you've uploaded this code and tested it out, you may have noticed a slight problem. Our ball is traveling off the screen and into oblivion! How can we keep the ball from disappearing?
Our next step will be to add simple collision detection in order to prevent the ball from moving past the screen boundaries. We can do this by testing where the ball is located in relation to the screen. We know that the screen's dimensions are 96x64, so we can use this to measure the bounds. As with all screens, the origin is located in the top left-hand corner of the screen at pixel (0,0) and extending to the largest value in the bottom right-hand corner (95, 63). We also need to consider that we have 4 different walls or screen sides that we could collide with. For the ceiling and left wall, we can compare the x and y values of the ball to 0. As for the floor and the right wall, we will want to compare the values on the bottom of the ball, and the right side of the ball. These would be the x coordinate + the ball's width, or the y coordinate + the ball's height. This is implemented as shown below:
We will place these statements inside the readInputs() function. This means that before the ball can be moved, it must be within the screen boundaries. The ball can now move freely within the boundaries of the screen! Since the screen never moves, it's easy to detect a collision with its boundaries. But what if we wanted to test the collision of the ball with another sprite? Let's create another sprite and test it!
To test for collisions between bitmaps and sprites, we'll borrow a few more functions that were created for the Flappy Birdz and Tiny Brick games. These routines use pointers to analyze two different sprites in relation to each other, looks for overlap in their bitmap attributes, and then returns true if there is a possibility the two bitmap sprites are in collision.
We can now call testPixelCollision(x,y) from the loop routine to test for a collision between sprites x and y. (Note that a collision between two "transparent" pixels is NOT a true collision.) If it returns true, we know the sprites have collided and can react accordingly. Let's make another bitmap image, a simple red brick. Just as we did before, we'll define each bit color in the header file, then add it to the main code.
Now that we have two sprites, we can make them interact. We'll continue using the ball as our movable piece, and we'll use our brick as a reactionary piece. If we upload our code as is, what would happen? Without implementing any collision detection, the ball will simply pass behind the brick. Why behind and not in front of? If you look again at our array named spriteList, you'll see that the ball is listed before the brick. This means the ball is rendered first, then the brick is rendered, placing it over top of the ball. Swap the order of these to get the ball to travel over the top of the brick. This holds true for any additional bricks we add to the spriteList array. Those listed later are rendered later, and are drawn over-top of the previously drawn sprites.
Next, we will add the collision. We have already copied over the functions needed; we just need to test for collisions and determine what reaction to take upon the collision of these two sprites. For now, we'll make the brick move to a new random location on the screen when we collide, like a game of tag. We can use the random function and constrain the limits to the bounds of the screen. These few lines of code will do the trick.
We use the ampersand (&) in front of the sprites we want to test in order to pass the address in memory of each sprite object to the testPixelCollision function. If you turn the game off and back on and collide with the brick a few times, you'll notice that it jumps as expected. Now turn the arcade off and back on. You'll notice that the brick is following the same pattern of motion as it did before. How can this be if we used the random() function? The random() function is actually a pseudo-random number generator. It follows a pattern that does produce random results, but the pattern will be the same in each iteration of your game or program. Can you find the Arduino function that helps to avoid this repeatability? (Hint: Look at the reference for random() here.)
Let's work on creating some "automatic motion" where the brick moves incrementally rather than just jumping to a random location. We will have to make the ball collide with a moving brick, giving more of a "chase" to this game of tag. Let's experiment with making the brick "teleport" off the right side of the screen and back through the left side of the screen. Enter the following code within the loop routine.
Now we have a brick that slides across the screen smoothly. (Does this motion remind you of another classic game?) Let's try slowing the brick down a bit. Rather than trying to increment it a half a pixel (which would be impossible), we'll declare a variable named frame and increment it upon each cycle through loop(). Then we can evaluate that and move the brick on every other loop cycle (i.e, every odd numbered cycle). We initialize frame as a global variable, and make it an unsigned int set to 0. "frame++" will increase the value of frame by 1. then we'll test it's lowest bit using & to compare it with 1. The & symbol is a bit-wise AND operator, and compares the LSB of each value.
Want the brick to move even slower? Compare the frame with a higher number! Speed alteration is a good way to introduce variation and challenge to your game!
Another method that will cause the same effect is to use the modulo operator which returns the remainder of a division. Rather than using (frame & 1) we could use (frame%2 !=0). These are two different methods to acquire the same task, so chose whichever you feel comfortable with!
You may have noticed that this tutorial uses the same ball and red brick bitmap as was used in one of our newly released retro games, Tiny Brick. What you may not have noticed is that the programming concepts we've just covered are also the same! Let's make a few adjustments to our code to see if we can start to recreate the Tiny Brick game based on what we already know!
We'll add three more bitmaps to our header file, a yellow brick and a green brick (of the same dimensions as our red brick), and a platform.
Then we'll add them in our main code, ensuring that we assign them different initial x and y locations so they aren't stacked on top of eachother! Just like before, they are initialized and also added to our spriteList, as well as increasing our amtSprites variable to match.
I've assigned the x and y coordinates of the bricks so that they will appear in the top left hand corner of the screen, and the platform in the lower center of the screen.
We'll modify the loop routine such that the red brick no longer moves by erasing this portion of code.
And rather than making it reappear on the screen when the ball touches it, we will move the ball off the screen so it is no longer visible or accessible by the ball. We'll move it too the arbitrary coordinates of (-100, -100). Our updated loop looks like this.
It looks like we've forgotten about our yellow and green bricks! We'll add those in too to make sure they also disappear when the ball touches them. Our updated loop looks like this:
Upload this code and give it a try! You'll see the bricks in the upper left hand corner of the scree, the ball centered, and the platform in the lower center of the screen. Move the joystick around and the ball will move just as before. Colliding the ball into the bricks will make them disappear!
Okay, so now that we have some of the game mechanics in place, we'll do some fine tuning. It is important to be patient while developing. Implementing too many changes at once may result in bugs with unintended side effects that are hard to find. Taking small steps allows us to test that our revisions do everything we intend, and nothing we don't. The "Implement, Test, Repeat" cycle will actually take less time than hunting down errors or conflicting code arguments. Slow and steady wins the race!
Next, we'll correct our motion and control so that the platform is our moveable "character", the bricks will be reactionary, and the ball will serve as the intermediary, moving as it chooses (based on angle calculations).
To make the platform move we'll use the same approach as we did in making the ball move. Check boundaries, then apply movement, in this instance however, the platform will only move along the x-axis, so we will no longer be using the joystick Up and Down directions. We'll change the readInputs function to be a movePlatform function, then move the button checking to a separate routine (we'll revamp this later on to reset the bricks). These will be our new functions. Don't forget to rename to movePlatform in the loop routine and add a call checkPushbuttons beneath that.
Now we need to make the ball move, otherwise we would just be moving a platform back and forth on the screen and it would be awkward... To make the ball move, we'll use a similar method as we did when we made the brick move, although this time, we need to have the code calculate angles, or move the ball along discrete paths. For the simplicity of this tutorial, we will make the ball move only in 45 degree paths. If you're interested in the more advanced method of making the code calculate the trigonometric angles, check out our code for Tiny Brick! We'll start by adding a new variable that will keep track of the ball direction for us.
Notice that we've also initialized a variable named offscreen and set it equal to -100. This will be a tidier method of moving our sprites off the screen. Next we'll add two more functions to make the ball move, and to detect wall collisions and redirect the ball. The theory is: the ball can only move in 45-degree angles, meaning that it will increment and/or decrement by a single pixel on both axes simultaneously. Because of this, the ball has 4 possible directions: 0) down and to the left, 1) down and to the right, 2) up and to the right, 3) up and to the left. The move ball function uses this to increment and decrement the ball coordinates based upon the ballDirection variable. The ballWallCollision function will check the ball coordinates to see if any of the edges are touching the walls. If they are, it will redirect the ball at the appropriate angle. There are 8 cases because the ball can approach any of the 4 walls from two different angles, and will therefore have different deflections.
Be sure to add these functions to the main loop!
Upload your code and test it. You'll find the all bouncing around on the screen at 45-degree angles. If the ball collides with any of the bricks, they will disappear, just as we expect, however the ball is not bouncing off the platform. Lets write a function to accomplish this task, and add it to the main loop. The function below works by testing to see if the ball's x-coordinates are above the platform, and if the y-coordinates are overlapping the platform. If both of these conditions are true, the ball must be touching the top of the platform, and we can now redirect the ball away from the platform.
You may be wondering, why not use our sprite collision function? The reason is, the sprite collision function will return true if any pixels on any side are touching, which means if we move the platform sideways into the ball, the platform and ball will collide on their sides. What we want to do is limit the interaction to the very top layer of the platform. This is the reason we have used a custom function for this collision as opposed to our already created function that tests for pixel collisions.
Now that we have the ball colliding with the platform, walls and bricks, let's alter the function of our buttons to reset the bricks to their "home" location on the screen. We'll write another function that resets their x and y coordinate positions.
As great as this is, 3 bricks is really not a fun challenge. So let's add some more bricks! We'll use the same bitmaps we've already created, and add onto our rows of bricks. Go back up to the top of the code change the sprite names to redBrick1, yelBrick1, and grnBrick1 (the consecutive bricks will be numbered incrementally). We'll also have to change these names in the spriteList, the ballBrickCollision function, and the resetBricks function. The fasted method to accomplish this is to do a find & replace. Use Ctrl + f on the keyboard to find instances of redBrick and change them to redBrick1. Do the same for the other bricks. *Be sure not to alter the names of the bitmaps that they are using, we don't need to number the bitmaps.
Now that we've added them to the spriteList, let's add them to the ballBrickCollision function so that they also disappear when hit, and also to our brickReset function.
Looking at the functions above, you can easily see how they will become lengthy and unmanageable if we continue to add individual bricks to them. We can revise these functions to make them more concise and presentable by implementing a few loops, arrays and pointers! Pointers are often an intimidating concept, especially for beginners, but don't lose hope, we only need a basic understanding of them.
If you're interested in learning more about pointers, this excellent tutorial/lesson focuses heavily on the background basics and builds up to pointers in C. We highly recommend reviewing it! http://home.netcom.com/~tjensen/ptr/ch1x.htm
The basic concept of the tutorial shows us that variables have
1) a type,
3) a name,
3) a value,
4) and an address where this information is stored.
We clearly recognize the first three because we use them each time we declare a variable and change its value(e.g, int x = 10;), however because the address is never directly made visible to the programmer (at least not in the way we will be writing code in the Arduino IDE) it seems to be a more elusive and less familiar concept. It is, perhaps also unfamiliar because any discussion of memory addresses could be difficult to grasp without a basic understanding of the internal workings of the processor hardware and its registers.
Pointers can be thought of as address variables because they store the address of another variable. Pointers are useful because rather than copying the data of each variable to another register, we can use a pointer to direct the program to where that variable data is already located, which saves us significant processing time as we are dealing with many variables and a lot of sprite data.
& is called a Reference Operator - this will be used to copy an ADDRESS to the pointer
* is called a Dereference Operator - this will be used to copy a DATA value to the pointer. On a basic level, once we have been directed to an address by the pointer, we have access to the data that is stored there. Please note that * is also used to declare a new pointer variable.
-> is essentially used in the place of a decimal when dealing with pointers. Rather than writing redBrick1.x, we would write cs->x (if cs is our pointer variable name and it is currently pointing to the address of redBrick1) to reference the same data.
We'll also add the variable numBricks to hold the number of bricks in our code. Create this variable above the amtSprites variable. Rewriting the resetBricks function using pointers will yield the following:
While this code is in fact longer in length than the previous, it has gained an incredible amount of flexibility. No matter how many new bricks we add, this function won't grow any larger in size! As long as we update the rstX and rstY arrays (which hold the "home coordinates" of our bricks) then our function will work as expected. It automatically calculates everything else, which is the ultimate goal when creating functions. Now we'll rewrite the ballBrickCollision function using the same principals. Can you do it on your own? Give it a try before proceeding. Experimenting with pointers is an effective way to learn how to use them.
*NOTE: We have effectively used the spriteList array, which already contains our brick sprites, to reset each one of them. Because of this, we must list all of the bricks as the first elements in the array in order for this to work! If you haven't done so, move the other sprites in the array to come after the bricks.
The new ballBrickCollision function using pointers will look like this:
This function has become significantly shorter! As before, using pointers allows us to add many more bricks without increasing the size of this piece of code! Just imagine if we had an if, else statement to check every single brick in the whole game! Let's get back to adding a few more bricks and see how it effects our code and our game play. Whether or not you realized, the dimensions of the bricks and their locations on the screen were calculated ahead of time such that we can fit 8 bricks across the screen. We'll add in the additional bricks to the sprite list, and update the rstX array in the resetBricks function. We should now have 3 rows and 8 columns of bricks spanning across the top of the screen. Notice how I've broken up the spriteList to make it easier to read and edit.
Just a few more features and we'll have a working version of Tiny Brick Lite! Rather than having to press the button to reset the bricks, let's make them reset themselves by adding a game score variable! Each time a brick is hit and eliminated, we'll add a point. When the number of points is equal to the number of bricks, we'll reset the bricks. We declare the new variable, score, along with our other global variables and initialize it to 0. Adding (score += 1) in the same function that tests for ball and brick collisions will increase the score as we eliminate bricks. When we reset the bricks, we'll also reset score to 0 in the to keep the cycle going.
We only have a few more details to refine in order to get a single-level version of Tiny Brick LITE. We'll add:
- A simple "Splashscreen" that appears upon game startup or after losing
- Brick deflection to make the ball bounce off the bricks,
- Lives that can be lost when the ball hits the floor,
- Frame rate control
Let's begin our final few steps by adding the "Splashscreen" that will keep the game halted until pressing a pushbutton. We'll copy the Tiny Brick logo from the full version of the game and paste it into our sprites header file, then initialize it, as you should be well familiar with by now. The only difference is that we'll set the initial x and y coordinates to be offscreen. That way, we can make a simple animation that will move it on-screen. We will also need a variable to toggle the game on/off. I have called it start and initialized it to 0. Our new showLogo function will check to see what state its in and react accordingly. When pressing the button, the logo will slide off the screen, and start the ball in a random location with an upward direction of travel. When all the bricks are gone, the reset function will set the start variable back to 0 which will make the logo reappear and halt game execution.
Onto the brick deflection code! For simplicity sake, we will only assume a few of the possible cases of collision between the ball and the brick. The most common collisions will be the ball hitting the bottom of the brick, and the left side of the brick. We can add this code into the ballBrickCollision function. We'll make another function named ballBrickBounce and we will pass to it the brick pointer. It will then check the direction of and redirect the ball. These two functions interact to give us a very basic ball deflection effect. While the ball will not always move in the direction we would like, it is most often correct, which is within the scope of this basic tutorial.
Next, we'll add the lives and remove the possibility for the ball to bounce off the floor. We'll need another global variable to keep track of the lives, and some sort of indicator that shows the user how many lives they have left. Modifying our ballWallCollision function will prevent the ball from bouncing off the floor. It will also take away a life if the ball goes past the boundary, and will move our lives indicator sprite to show 1 less life.
A simple sprite in the bottom right hand corner of the screen will serve this purpose nicely and keeps as much of the screen open for game-play as possible. The lives bitmap is simply a repeating set of ball type images. We can slide this sprite to the left and right to show more or less lives as the player loses them. As usual, declare a global variable, I named it gameLives and initialized it to 3. I also created a bitmap/sprite and initialized it offscreen. When the game starts, it will be moved to the bottom right hand portion of the screen. A restart function will allow us to reset the ball position and platform when the ball is lost and a life is lost. It is similar to the showLogo function, though it doesn't make the game logo appear and disappear.
And for our final topic of this tutorial, we will discuss frame rate control. Frame rate control is important because as we add and take away features in the code, our program will begin to run at slightly different speeds. To avoid this side effect, we implement some timing that will only update the loop when a certain amount of time has passed. To do this, we'll use two more global variables and a local variable.
All three of these variables combined together will give us control over how fast the code executes. Adding and taking away features won't give us different speed results. Upload the game again and notice how it appears to be going slower, yet the relative speeds are all the same. Play around with the frameTime variable to see how it effects the speed of the game! By using the processors built in timers, we avoid using delay functions that would slow down our code considerably! This is good practice to keep things running efficiently and without wasting clock cycles on the processor.
All of this in just 300 lines of code! There is certainly potential for refinement, though we have taken a simple approach to making an easy game and discussed the underlying basics that your future games will be built upon!
If you've made it to this point in the tutorial, congratulations! We've covered quite a large number of topics within this tutorial and you may even feel overwhelmed by how much you've learned. But it is also important to discuss the limitation that exist within our game, and the potential for improvement! Limitation include:
- Because we are using discrete 45-degree angles only, the ball can often get "stuck" on the same trajectory in which it continues to miss the remaining bricks
- The ball deflection off the bricks does not account for every possibility, we have simplified the logic by ignoring half of the possibilities.
- The game is only a single level
- The ball speed is fixed
- The ball deflection angle off the platform is also fixed, giving the player very little actual control over the motion of the ball.
- There are no win/lose screens, only a single logo "Splashscreen"
- There's no sound effects
- We didn't use the collisions portion of the type we created.
In light of each of these lacking features, what can you do to improve the game play and to correct these shortcomings? The full version of Tiny Brick has implemented slightly more complex techniques to add these features in, though the ground work for it is exactly what we have covered in this tutorial (you'll even recognize several of the functions we used here!). Play around with the code to see what you can come up with! Can you add Power-Up bricks? A changing background color? Different Levels and Win/Lose screens? Can you make the ball change speed based on which brick it has deflected off of, or use the collisions piece of our type to require that bricks need to be hit multiple times in order to break? Explore the skills you've learned and see what you can come up with!
To export your game in the proper format needed to access it from the TinyArcade Menu, you'll need to export your code as a binary. Once you are happy with your game, you can export it. With the Arduino IDE open, go to Tools -> Build Option: -> Binary for SD Card.
Next, you'll need to go to Sketch -> Export Compiled binary. Once you click on this, the Arduino IDE will compile and export your code as binary. You can follow along in the GIF below.
It will automatically be saved to the same folder that your code is saved in. Find the .bin file in the folder and rename it Game_Title.bin. Create a new folder with the same name as your game and place the binary code inside of it. If you have a .tsv file that goes with your game, you will also save that within this folder, making sure again, that the name matches.
See HERE for creating and converting to .tsv files for the arcade menu.
Now, plug in your SD card via an adapter and move your game folder onto it. Once the file has been transferred, safely eject the SD card. With the Tiny Arcade menu loaded, insert the SD card and power on your Tiny Arcade. Scroll through the menu, and you should be able to find your game with it's accompanying .tsv indicator! Select it and enjoy playing your game from the menu selector!
You can download the code for Tiny Brick LITE Here.
You can download the sprite header file for Tiny Brick LITE Here.