What is it?
- Inspired by a request from mathew77, this native homebrew game aims to revive and remix the classic handheld game often known as "Brick Game 9999-in-1" or something similar. It's still quite early into development but it's going well!
What Games Are Included/In Progress?
- The Menu
- Tetris
- Snake
- Racecar
- Pong
- Rowfiller
- Rowsmash
- HiOrLo
What Games Are Planned?
- Breakout
- Vertical Shooter
- And More...!
What Modes and Features?
- Classic Size Game Screen [ 10 x 20 ]
- Remix Size Game Screen [ 20 x 20 ]
- Rotatable Screen for Standard or Vertical Viewing
- Music and Sound Effects
- Hold Trigger for Fast
- Scores and Highscores
Great with Flip Grip!
How Can You Help?
While working on the game, I'm also building a sort of Brick Game API, a small game engine to run these subgames. I'm attempting to make it easy enough for anyone to just add their own single .cpp file to the project, with little to no experience needed, based on my example and have it be added to the main game list! (Pull requests and messaging for assistance welcome and encouraged!) I'm adding new features every day, so check back on that!
New version records and loads high scores per game.
Thanks for bearing with me while it becomes a more and more complex “snake” simulator lol. I’m using snake as the testbed while I build features so that once the framework is there, I can easily knock out games like it’s nothing.
PS, are you guys interested in technical write ups? Every once in a while I have to do some clever programming things to make things work. Wanna hear the nitty gritty? (I’ll do one on this post or just below shortly so lemme know what you think. )
New version records and loads high scores per game.
Thanks for bearing with me while it becomes a more and more complex “snake” simulator lol. I’m using snake as the testbed while I build features so that once the framework is there, I can easily knock out games like it’s nothing.
PS, are you guys interested in technical write ups? Every once in a while I have to do some clever programming things to make things work. Wanna hear the nitty gritty? (I’ll do one on this post or just below shortly so lemme know what you think. )
Programming natively for the Switch in C or C++ is a relatively low level operation. Unlike when working with a studio (or even as an indie), you don't have the luxuries of using libraries and dev environments built with millions of dollars like Unity and Unreal. For the most part, you have to rely on free, open source, community made projects, which are great and I'm thankful for, but robustness and especially documentation tend to suffer.
I'm open to the fact that everything I'm about to describe having trouble with has a simple solution, but that lends to my point since I read basically all the documentation available and through countless forum topics discussing it. (If you know the answer, let me know, please!)
For this project, I'm using a port of nanovg, a handy shape and graphic drawing lib, on top of deko3d, the raw screen drawing calls.
What's the problem?
At a certain number of draw calls, about 500, the app will crash with an out of memory error. This counts for everything. Every sprite, letter of text, colored rectangle, etc. As you can imagine, with the 20x20 grid of sprites plus a few lines of info text on the screen and we're already out of slots.
/
What's my solution?
Instead of drawing every plip in the grid, I decided that it might be a good idea to instead draw 2x2 plip graphics. (We can do larger sizes but that's for later if needed.)
...
...
...
( 4 out of 16 combinations )
But how do we do this the best way?
----------------------------
If we know that 0 is off and 1 is on, we can map things out like this:
Binary 0 - Decimal 0
Binary 1 - Decimal 1
Binary 00 - Decimal 0
Binary 01 - Decimal 1
Binary 10 - Decimal 2
Binary 11 - Decimal 3
Binary 0000 - Decimal 0
00
00
Binary 0001 - Decimal 1
00
01
Binary 0010 - Decimal 2
00
10
Binary 0011 - Decimal 3
00
11
Binary 0100 - Decimal 4
01
00
Binary 0101 - Decimal 5
01
01
Binary 0110 - Decimal 6
01
10
Binary 0111 - Decimal 7
01
11
Binary 1000 - Decimal 8
10
00
Binary 1001 - Decimal 9
10
01
Binary 1010 - Decimal 10
10
10
Binary 1011 - Decimal 11
10
11
Binary 1100 - Decimal 12
11
00
Binary 1101 - Decimal 13
11
01
Binary 1110 - Decimal 14
11
10
Binary 1111 - Decimal 15
11
11
----------------------------
Great! So how do we draw this?
Now that we've mapped out which graphic is which above, we can easily to refer to each, assuming they're named based on the decimal (for example, cells_00.png, cells_01.png, ..., cells_15.png),
Let's say that we have the boolean true/false for each of the 4 locations stored to the variables:
top_left : TL
top_right : TR
bottom_left : BL
bottom_right. : BR
The slow, boring, sad way to do this is to just write out every single possibility.
Code:
// none active
if (!TL and !TR and !BL and !BR)
draw_sprite("cells_00.png");
// just bottom right
if (!TL and !TR and !BL and BR)
draw_sprite("cells_01.png");
// ... now repeat this all 16 times lol
// all filled
if (TL and TR and BL and BR)
draw_sprite("cells_15.png");
As you can see, this is so much work that I didn't even want to write them all out for this example!
For 2x2, that's 2^4, 16 options.
If we wanted to draw a 3x3 grid, that would be 2^9, 512 options.
4x4? 2^16, you'd have to write 65,536 if statements.
Surely there must be a better way. Maybe a way that only takes a couple lines no matter how big the grid.
Bitwise tricks!!
Let's talk about how binary numbers are represented.
Each position is a power of two, laid out something like this:
0000 <- Binary positions
8421 <- Value of each position
Based on that, just add up the values of the activated positions.
The trick now is to somehow encode the four plip statuses into this format!
As we know from my chart above, each position in the binary could be compared to each of the plips. This next part is where I might lose some of you, but I'm not quite sure (while writing this at 5 am) how to bridge the gap any smoother.
If we want to convert a 0/1 false/true relationship into the four places, we can do something like this:
And that's it! No matter how big our grid gets, using those two lines of code, we can retrieve the right graphic, instead of needing exponentially more if statements for each size upgrade.
There is a more in-depth way to explain why to multiply those numbers and how to shift bits, but I'm trying to find a place between overly technical and not technical enough with this post. If any of it isn't clear or you have any questions and would like to learn more, feel free to ask! And if you liked this write-up, let me know and I'll see if I can do more in the future for interesting topics.
As this project is open source on my github, you can find the code in question right here! Just a warning, though, since this is early in turbulent development, the linked code may be rewritten or moved somewhere else sometime soon! (Also, in my implementation I reversed the order of the bits because it was a little prettier to me, but the explained method is the standard order.)
Programming natively for the Switch in C or C++ is a relatively low level operation. Unlike when working with a studio (or even as an indie), you don't have the luxuries of using libraries and dev environments built with millions of dollars like Unity and Unreal. For the most part, you have to rely on free, open source, community made projects, which are great and I'm thankful for, but robustness and especially documentation tend to suffer.
I'm open to the fact that everything I'm about to describe having trouble with has a simple solution, but that lends to my point since I read basically all the documentation available and through countless forum topics discussing it. (If you know the answer, let me know, please!)
For this project, I'm using a port of nanovg, a handy shape and graphic drawing lib, on top of deko3d, the raw screen drawing calls.
What's the problem?
At a certain number of draw calls, about 500, the app will crash with an out of memory error. This counts for everything. Every sprite, letter of text, colored rectangle, etc. As you can imagine, with the 20x20 grid of sprites plus a few lines of info text on the screen and we're already out of slots.
/
What's my solution?
Instead of drawing every plip in the grid, I decided that it might be a good idea to instead draw 2x2 plip graphics. (We can do larger sizes but that's for later if needed.)
...
...
...
( 4 out of 16 combinations )
But how do we do this the best way?
----------------------------
If we know that 0 is off and 1 is on, we can map things out like this:
Binary 0 - Decimal 0
Binary 1 - Decimal 1
Binary 00 - Decimal 0
Binary 01 - Decimal 1
Binary 10 - Decimal 2
Binary 11 - Decimal 3
Binary 0000 - Decimal 0
00
00
Binary 0001 - Decimal 1
00
01
Binary 0010 - Decimal 2
00
10
Binary 0011 - Decimal 3
00
11
Binary 0100 - Decimal 4
01
00
Binary 0101 - Decimal 5
01
01
Binary 0110 - Decimal 6
01
10
Binary 0111 - Decimal 7
01
11
Binary 1000 - Decimal 8
10
00
Binary 1001 - Decimal 9
10
01
Binary 1010 - Decimal 10
10
10
Binary 1011 - Decimal 11
10
11
Binary 1100 - Decimal 12
11
00
Binary 1101 - Decimal 13
11
01
Binary 1110 - Decimal 14
11
10
Binary 1111 - Decimal 15
11
11
----------------------------
Great! So how do we draw this?
Now that we've mapped out which graphic is which above, we can easily to refer to each, assuming they're named based on the decimal (for example, cells_00.png, cells_01.png, ..., cells_15.png),
Let's say that we have the boolean true/false for each of the 4 locations stored to the variables:
top_left : TL
top_right : TR
bottom_left : BL
bottom_right. : BR
The slow, boring, sad way to do this is to just write out every single possibility.
Code:
// none active
if (!TL and !TR and !BL and !BR)
draw_sprite("cells_00.png");
// just bottom right
if (!TL and !TR and !BL and BR)
draw_sprite("cells_01.png");
// ... now repeat this all 16 times lol
// all filled
if (TL and TR and BL and BR)
draw_sprite("cells_15.png");
As you can see, this is so much work that I didn't even want to write them all out for this example!
For 2x2, that's 2^4, 16 options.
If we wanted to draw a 3x3 grid, that would be 2^9, 512 options.
4x4? 2^16, you'd have to write 65,536 if statements.
Surely there must be a better way. Maybe a way that only takes a couple lines no matter how big the grid.
Bitwise tricks!!
Let's talk about how binary numbers are represented.
Each position is a power of two, laid out something like this:
0000 <- Binary positions
8421 <- Value of each position
Based on that, just add up the values of the activated positions.
The trick now is to somehow encode the four plip statuses into this format!
As we know from my chart above, each position in the binary could be compared to each of the plips. This next part is where I might lose some of you, but I'm not quite sure (while writing this at 5 am) how to bridge the gap any smoother.
If we want to convert a 0/1 false/true relationship into the four places, we can do something like this:
And that's it! No matter how big our grid gets, using those two lines of code, we can retrieve the right graphic, instead of needing exponentially more if statements for each size upgrade.
There is a more in-depth way to explain why to multiply those numbers and how to shift bits, but I'm trying to find a place between overly technical and not technical enough with this post. If any of it isn't clear or you have any questions and would like to learn more, feel free to ask! And if you liked this write-up, let me know and I'll see if I can do more in the future for interesting topics.
As this project is open source on my github, you can find the code in question right here! Just a warning, though, since this is early in turbulent development, the linked code may be rewritten or moved somewhere else sometime soon! (Also, in my implementation I reversed the order of the bits because it was a little prettier to me, but the explained method is the standard order.)
That's an interesting approach, but there's an even smorter way, if you're interested. As you've already mentioned, on a 2x2 grind (let's call it a tile, hint hint) there are only 4 combinations of active pips, 1-4. So, how do we make those 4 combinations into 16? Transformation. 0 - no transformation, 1 - mirror image, 2 - upside down, 3 - upside down mirror image. For instance, we want to light up three pips, and we want the top-right pip to be the one that's not lit, all we need to do is use the graphic no.3 with transformation no.3.
Alternatively, you could keep the mirror state and the flip state separate. You also have the option to rotate - 0, 90, 180 or 270 degress (rotating 360 degrees is the same as 0, so no need for fifth state). This is a bit heavier on the machine though, whereas flipping an image on the X or Y axis is trivial.
This is ancient wisdom, from times when VRAM was counted in kilobytes.
Forgive the crude drawing - for demonstration only.
EDIT: You can reduce this even further if you treat your entire screen size as a tile map with its dimensions divided by 2 and introduce a 2-pip state (2x2 tiles, 0-4 pips lit). So, if you have an array of 16x16 pips, you can treat it as an array of 8x8 tiles that are 2x2. This will reduce the amount of draw calls you need to do in half. You don't need to concern yourself with each and every row or column - you can only deal with every other row and column since your tiles are 2x2 (row 0, 3, 5, column 0, 3, 5 etcetera).
That's an interesting approach, but there's an even smorter way, if you're interested. As you've already mentioned, on a 4x4 grind (let's call it a tile, hint hint) there are only 4 combinations of active pips, 1-4. So, how do we make those 4 combinations into 16? Transformation. 0 - no rotation, 1 - mirror image, 2 - mirror image+upside down, 3 - upside down. For instance, we want to light up three pips, and we want the top-right pip to be the one that's not lit, all we need to do is use the graphic no.3 with transformation no.3.
Alternatively, you could keep the mirror state and the flip state separate. You also have the option to rotate - 0, 90, 180 or 240 degress (rotating 360 degrees is the same as 0, so no need for fifth state). This is a bit heavier on the machine though, whereas flipping an image on the X or Y axis is trivial.
This is ancient wisdom, from times when VRAM was counted in kilobytes.
Clever clever! In this case, the graphics are not quiiite flippable because they’re slightly heavier stroked to one side (not necessary but slightly prettier lol), but I love the thought. I’ve always been a big fan of finding tricky smort optimization since my first few years of programming was done on TI-83 calculators! I was gonna have a section on memory vs processing trade offs but figured I’d run into that more dramatically later on and can review it then. Thanks for writing in!
Clever clever! In this case, the graphics are not quiiite flippable because they’re slightly heavier stroked to one side (not necessary but slightly prettier lol), but I love the thought. I’ve always been a big fan of finding tricky smort optimization since my first few years of programming was done on TI-83 calculators! I was gonna have a section on memory vs processing trade offs but figured I’d run into that more dramatically later on and can review it then. Thanks for writing in!
Oh, I can see that now that I zoomed in a bit! Nice touch, makes it look more authentic. Still, I do suggest introducing a 5th pip state and dividing your map in half instead of bothering with individual rows and columns. What you draw and how your game logic interprets that are two separate things, and if you can slash your drawing overhead, great. You seem to be quite limited in terms of possible draw calls, and that's an extremely common solution - been used on things as old as the NES all the way to the good ol' DS.
Oh, I can see that now that I zoomed in a bit! Nice touch, makes it look more authentic. Still, I do suggest introducing a 5th pip state and dividing your map in half instead of bothering with individual rows and columns. What you draw and how your game logic interprets that are two separate things, and if you can slash your drawing overhead, great. You seem to be quite limited in terms of possible draw calls, and that's an extremely common solution - been used on things as old as the NES all the way to the good ol' DS.
I feel ya. If I'm understanding you correctly, the reason I'm in-between here and there is so that I can still dynamically resize the grid. Wanna make it so you can make the shape whatever you want! (-As well as classic mode.) But yeah, all the logic and drawing are completely separated so the game will happily change pip statuses millions of times per frame and then just draw them all at once at the end.
I feel passion, charm, and nostalgia for Brick Game 9999-in-1 systems; I feel myself healed by fantasying myself playing at one of them once more. I much appreciate this project of yours, Chrscool8!
I feel ya. If I'm understanding you correctly, the reason I'm in-between here and there is so that I can still dynamically resize the grid. Wanna make it so you can make the shape whatever you want! (-As well as classic mode.) But yeah, all the logic and drawing are completely separated so the game will happily change pip statuses millions of times per frame and then just draw them all at once at the end.
This do anything for ya?
Edit:
Vroom Vroom. I'm having fun making the games size agnostic. It's fun to see the classic game in a new way.
Very nicely done indeed, nostalgia incarnate. Sizing dynamically is a big advantage, if you're not anywhere near hitting your draw calls limit then there's no reason to cannibalise your engine, but it's always food for thought if you ever get there. I'm definitely liking what I'm seeing, very well done. I kind of wish I had kept some of those doo-dads from the past, but I'm afraid the drawer moths ate all my "bricks", so it's nice to see someone dedicated to making an accurate recreation.
I feel passion, charm, and nostalgia for Brick Game 9999-in-1 systems; I feel myself healed by fantasying myself playing at one of them once more. I much appreciate this project of yours, Chrscool8!
Very nicely done indeed, nostalgia incarnate. Sizing dynamically is a big advantage, if you're not anywhere near hitting your draw calls limit then there's no reason to cannibalise your engine, but it's always food for thought if you ever get there. I'm definitely liking what I'm seeing, very well done. I kind of wish I had kept some of those doo-dads from the past, but I'm afraid the drawer moths ate all my "bricks", so it's nice to see someone dedicated to making an accurate recreation.
@Chrscool8 if i remember well , i have a suggestion ;
i wonder if in the original game , when you hold the desired directions buttons or by pressing A , the snake go very fast. ( even in the first second play).
i was remembering playing tetris/snake/race car ,like this.
thank you again for this nostalgic HB , kids are happy playing it
Seemingly out of nowhere a PC port for Pokemon Platinum has surfaced online, bundled alongside the source code for those interested in building and developing it for...
With very little in the way of announcement, Valve has today increased the price of the Steam Deck but some fairly considerable margins. Both of the available models...
Nintendo's expected Summer showcase is here, offering up plenty of new announcements and exciting reveals. Let's see what they have in store in the latest Nintendo...
Continuing with the great news of Pokémon Platinum getting a native unofficial PC port just a few days ago, today, yet another classic title from the franchise has...
The latest in a growing number of native PC ports, Paper Mario ReCut got its first pre-release build earlier this week. Based on the N64 recompilation toolchain, the...
When you talk about 3DS emulation, most people would jump to Citra. As the defacto choice since its first release it's seen tremendous success, and even after its...
A whole hour of PlayStation content is on the way, thanks to the latest State of Play showcase. Headlining the stream will be Marvel's Wolverine, alongside a...
What once seemed like a far off dream, and after many, many community restarts throughout the years, the elusive Mother 1 / EarthBound Beginnings Remake, which is a...
For the first time in 13 years, the Call of Duty series will again return to Nintendo's consoles. Set to launch on the 23rd of October, the latest release, Modern...
Back in April we covered the ROM hacking efforts to add fifth-generation animated sprites to third generation Pokemon games. It remains a thoroughly impressive...
With very little in the way of announcement, Valve has today increased the price of the Steam Deck but some fairly considerable margins. Both of the available models...
Nintendo's expected Summer showcase is here, offering up plenty of new announcements and exciting reveals. Let's see what they have in store in the latest Nintendo...
Seemingly out of nowhere a PC port for Pokemon Platinum has surfaced online, bundled alongside the source code for those interested in building and developing it for...
With rumours circulating about a Nintendo Direct in the coming days and weeks, fans are left speculating and hoping as to what might be included. At the centre of all...
The latest in a growing number of native PC ports, Paper Mario ReCut got its first pre-release build earlier this week. Based on the N64 recompilation toolchain, the...
A whole hour of PlayStation content is on the way, thanks to the latest State of Play showcase. Headlining the stream will be Marvel's Wolverine, alongside a...
After much speculation and rumour, the fabled Nintendo Direct is upon us. Set to go live tomorrow, the 9th of June, at 3pm in the UK, it'll feature 50 minutes of...
For the first time in 13 years, the Call of Duty series will again return to Nintendo's consoles. Set to launch on the 23rd of October, the latest release, Modern...
Following an investigation over misleading commercial practices, today Nintendo has been imposed a fine of 35 million euros related to the controller malfunctions...
Continuing with the great news of Pokémon Platinum getting a native unofficial PC port just a few days ago, today, yet another classic title from the franchise has...