Homebrew WIP Brick Game 9999-in-1 Clone Project

  • Thread starter Thread starter Chrscool8
  • Start date Start date
  • Views Views 18,529
  • Replies Replies 48
  • Likes Likes 23

Chrscool8

Well-Known Member
Member
Joined
Oct 23, 2008
Messages
114
Reaction score
420
Trophies
1
XP
935
Country
United States


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




abHnkfj.jpg


d8dG2iD.jpg


03%20-%20sprites.jpg


Great with Flip Grip!
QhLFsuI.jpg

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!

Download:
> Github <
> HB App Store <​


Thank you for your time, I hope you enjoy it!
 
Last edited by Chrscool8,
This is so cool holy shit
Thanks! :D

Thank you a lot for your tremendous work!.. :bow:

If you need an authentic boot / gameover sounds from the similar brickgame that I have:



And the HQ SFX archive here:


Hope it turns into what you're looking for! Thanks for those resources, will definitely look into them!

Love this project. Especially from a nostalgic perspective. Snake is running great with my Flip Grip so far. Hope for more games. :)

Hello. I'm sorry for my offtop. How to connect joy-cons as on a last photo? What is a bracket?

It's a Flip Grip I guess, here's the link:
https://www.fangamer.com/products/flip-grip

Yay, thanks! Yep, that's the one! Got myself one of those just for this project and it feels great. :D
 
New version records and loads high scores per game.

09%20-%20high%20score.jpg


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. :) )
 
Last edited by Chrscool8,
New version records and loads high scores per game.

09%20-%20high%20score.jpg


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. :) )
Absolutely. Let us hear about it!
 
  • Like
Reactions: Chrscool8
Dev Log Experimental - Too Many Graphics!

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.

cell_unselected.png
/
cell_selected.png


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.)

cells_00.png
...
cells_01.png
...
cells_13.png
...
cells_15.png

( 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
cell_unselected.png


Binary 1 - Decimal 1
cell_selected.png

Binary 00 - Decimal 0
cell_unselected.png
cell_unselected.png


Binary 01 - Decimal 1
cell_unselected.png
cell_selected.png


Binary 10 - Decimal 2
cell_selected.png
cell_unselected.png


Binary 11 - Decimal 3
cell_selected.png
cell_selected.png

Binary 0000 - Decimal 0
00
00
cells_00.png


Binary 0001 - Decimal 1
00
01
cells_08.png


Binary 0010 - Decimal 2
00
10
cells_04.png


Binary 0011 - Decimal 3
00
11
cells_12.png


Binary 0100 - Decimal 4
01
00
cells_02.png


Binary 0101 - Decimal 5
01
01
cells_10.png


Binary 0110 - Decimal 6
01
10
cells_06.png


Binary 0111 - Decimal 7
01
11
cells_14.png


Binary 1000 - Decimal 8
10
00
cells_01.png


Binary 1001 - Decimal 9
10
01
cells_09.png


Binary 1010 - Decimal 10
10
10
cells_05.png


Binary 1011 - Decimal 11
10
11
cells_13.png


Binary 1100 - Decimal 12
11
00
cells_03.png


Binary 1101 - Decimal 13
11
01
cells_11.png


Binary 1110 - Decimal 14
11
10
cells_07.png


Binary 1111 - Decimal 15
11
11
cells_15.png

----------------------------

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.

0000 =
(0*8)+(0*4)+(0*2)+(0*1) =
0 + 0 + 0 + 0 = 0

0101 =
(0*8)+(1*4)+(0*2)+(1*1) =
0 + 4 + 0 + 1 = 5

1111 =
(1*8)+(1*4)+(1*2)+(1*1) =
8 + 4 + 2 + 1 = 15

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:

sprite_num = (TOP_LEFT * 8) + (TOP_RIGHT * 4) + (BOTTOM_LEFT * 2) + (BOTTOM_RIGHT * 1)

and the resulting number will be a number from 0 to 15, corresponding to the correct sprite and it's then as simple as:

draw_sprite("cells_" + string(sprite_num) + ".png");

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.)

Thanks!
 
Last edited by Chrscool8,
Dev Log Experimental - Too Many Graphics!

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.

cell_unselected.png
/
cell_selected.png


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.)

cells_00.png
...
cells_01.png
...
cells_13.png
...
cells_15.png

( 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
cell_unselected.png


Binary 1 - Decimal 1
cell_selected.png

Binary 00 - Decimal 0
cell_unselected.png
cell_unselected.png


Binary 01 - Decimal 1
cell_unselected.png
cell_selected.png


Binary 10 - Decimal 2
cell_selected.png
cell_unselected.png


Binary 11 - Decimal 3
cell_selected.png
cell_selected.png

Binary 0000 - Decimal 0
00
00
cells_00.png


Binary 0001 - Decimal 1
00
01
cells_08.png


Binary 0010 - Decimal 2
00
10
cells_04.png


Binary 0011 - Decimal 3
00
11
cells_12.png


Binary 0100 - Decimal 4
01
00
cells_02.png


Binary 0101 - Decimal 5
01
01
cells_10.png


Binary 0110 - Decimal 6
01
10
cells_06.png


Binary 0111 - Decimal 7
01
11
cells_14.png


Binary 1000 - Decimal 8
10
00
cells_01.png


Binary 1001 - Decimal 9
10
01
cells_09.png


Binary 1010 - Decimal 10
10
10
cells_05.png


Binary 1011 - Decimal 11
10
11
cells_13.png


Binary 1100 - Decimal 12
11
00
cells_03.png


Binary 1101 - Decimal 13
11
01
cells_11.png


Binary 1110 - Decimal 14
11
10
cells_07.png


Binary 1111 - Decimal 15
11
11
cells_15.png

----------------------------

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.

0000 =
(0*8)+(0*4)+(0*2)+(0*1) =
0 + 0 + 0 + 0 = 0

0101 =
(0*8)+(1*4)+(0*2)+(1*1) =
0 + 4 + 0 + 1 = 5

1111 =
(1*8)+(1*4)+(1*2)+(1*1) =
8 + 4 + 2 + 1 = 15

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:

sprite_num = (TOP_LEFT * 8) + (TOP_RIGHT * 4) + (BOTTOM_LEFT * 2) + (BOTTOM_RIGHT * 1)

and the resulting number will be a number from 0 to 15, corresponding to the correct sprite and it's then as simple as:

draw_sprite("cells_" + string(sprite_num) + ".png");

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.)

Thanks!
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. :P

Untitled135_20210406144709.png

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). :P
 
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. :P

View attachment 256504

Forgive the crude drawing - for demonstration only.

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. :D 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. :D 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.

Well hot dang, I'm old enough to remember this. It better feature that racing game or it's 0/10. :D

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. :)
 
Last edited by 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. :lol: 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. :D
 
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. :lol: 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. :D
thanks a lot , you bring us good souvenirs.
my first console ever ;)
danke

Glad you guys are enjoying the project. ^_^ Hope someday it can fully replicate those old memories!

Speaking of which, Racecar is basically fully playable now! (Speed and difficulty controls for all games to come later.)

 
@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 ;)
 
Last edited by mitcha,

Site & Scene News

Popular threads in this forum