First game - Rabbit Catch

Hey there! While barely qualifying as a game, I believe this gets there

I would really appreciate people pulling apart my code and showing me how to make it more efficient. I’ve been codiing for 2 days now, after all.

Many thanks to @crait for his amazing tutorials

1 Like

Nice work …

Some code feedback? When testing to see if the rabbit and the player are colliding, you are comparing the players coordinates against the rabbits:

    if (lexipx == playerx or lexipx == playerx - 1 or lexipx == playerx + 1 or lexipx == playerx - 2 or lexipx == playerx + 2 or lexipx == playerx - 3 or lexipx == playerx + 3) {
      ycheck = 1;
    }

    if (ycheck == 1) {
      if (lexipy == playery or lexipy == playery - 1 or lexipy == playery + 1 or lexipy == playery - 2 or lexipy == playery + 2 or lexipy == playery - 3 or lexipy == playery + 3) {
        lexipx = rand() % 113;
        lexipy = rand() % 41 + 8;
        ycheck = 0;
        score = score + 1;
        sound.tone(800, 200);
      }
      else ycheck = 0;
    }

You could use the inbuilt collide function:

Rect playerRect = { playerx, playery, 16, 16 };
Rect rabbitRect = { lexipx, lexipy, 16, 16 };

if (arduboy.collide(playerRect, rabbitRect)) {
  
  lexipx = rand() % 113;
  lexipy = rand() % 41 + 8;
  score = score + 1;
   sound.tone(800, 200);

}

This will register a collision when the rectangles are just touching. Your current code says they need to be much more ‘overlapping’ so you could change the code to something like that below that means the player and rabbit have to be pverlapping.

Rect playerRect = { playerx + 4, playery + 4, 8, 8 };
Rect rabbitRect = { lexipx + 4, lexipy + 4, 8, 8 };
...
1 Like

You might also want to re-read Make Your Own Arduboy Game: Part 7 - Make Pong From Scratch! where @crait introduces the gameState and switch concept:

switch( gamestate ) {
	case 0:
		//Title screen
		break;
	case 1:
		//Gameplay screen
		break;
	case 2:
		//Win screen
		break;
	case 3:
		//Game over screen
		break;
}

Within each of those cases, I would call a subroutine that does what you want rather than have it all buried in the loop(). It will make your code much easier to read.

You also have display() and clear() peppered throughout the code. It is normal to clear() once and the start of the loop and display() at then end of the main loop().

I have marked the game up here https://github.com/filmote/RabbitCatch using the switch statement and collide.

1 Like

I second @filmote’s sentiments for pretty much everything, especially about the switch statement, very handy.

Though naturally I think gameState should be an enum class not a regular old number.

// Globals
enum class GameState : uint8_t
{
	TitleScreen,
	Gameplay,
	Win,
	Lose,
};

GameState gameState = GameState::TitleScreen;

// Main loop
switch(gameState)
{
	case GameState::TitleScreen:
		//Title screen
		break;
	case GameState::Gameplay:
		//Gameplay screen
		break;
	case GameState::Win:
		//Win screen
		break;
	case GameState::Lose:
		//Game over screen
		break;	
}

(I’m also not a fan of the built in collision function because although it does the job, I don’t think the Arduboy2 class should be responsible for testing collisions, or is should at least be a static function so you don’t need an instance of Arduboy2 to call it.)

Time for a new question, are any of these ifs supposed to be else if?
Or are some of the == supposed to be >=?

if (counter == 1) {
	arduboy.drawBitmap(0, 0, banner1, 128, 16, WHITE);
}
if (counter == 10) {
	arduboy.drawBitmap(0, 16, banner2, 128, 16, WHITE);
}
if (counter == 20) {
	arduboy.drawBitmap(0, 32, banner3, 128, 16, WHITE);
}
if (counter == 30) {
	arduboy.drawBitmap(0, 48, banner4, 128, 16, WHITE);
	sound.tone(600, 30, 800, 30, 1400, 50);
}
if (counter == 180) {
	scene = 0;
	counter = 2000;
}

(Also, drawBitmap uses WHITE by default, so the last parameter of those calls is superfluous.)

The compiler will probably figure out that these conditions are mutually exclusive, but if that was the intent then it pays to be explicit.

And a small tip: wrap all your printed strings in the F macro, it puts them in progmem instead of RAM.

arduboy.print(F("Score: "));
arduboy.print(F(score));
arduboy.print(F("\n"));
arduboy.print(F("Hiscore: "));
arduboy.print(F(hiscore));
arduboy.print(F("\n"));
arduboy.print(F("Press A to play again"));

A small stylistic tip: only inexperienced programmers use or,
99% of experienced programmers use ||, which does exactly the same thing.
Similarly, most experienced programmers use &&, not and.
or and and are very uncommonly used, as are the other alternative operators.

And lastly, calling clear in setup is always pointless.
The screen buffer always starts already clear.

I think some people expect the screen buffer to be full of random junk, but there’s a C++ rule that says all global variables are zero-initialised at the start of the program, and the screen buffer is a global variable.
(Technically it’s actually “objects with static storage duration” which is more than just global variables, but that’s getting a bit too technical.)

All in all it’s pretty good for 2 days in.
(For some reason I misread that as ‘2 years’ to begin with.)

I thought about mentioning the enum clasee, the || and && and the use of int where uint8_t would have sufficed but its too early (on day two) to bring them up.

For two days of coding, this is a really good effort!

George … you?

1 Like

It might be too soon to mention scoped enums, but I like to get in early before habits develop.
Also if the named values over bare numbers approach appeals, that’s a good thing.

Definitely too soon for int vs uint8_t, that requires understanding binary first.

Substituting && for and and || for or is negligable though.
Personally I think it’s better to never learn the and/or approach,
they’re so rarely used that it’s just learning bad habits.

Indeed. How’d you find that?

I just googled your name as I thought you might be in Australia based on the time you posted the first message. Now I realise you were just up really late!

1 Like

Nah, just living the student life - though a little late. Finished school at 20, didn’t have the money to really go to uni until 25.


Thank you all for the help here - getting my head around it all and typing it up myself today.

1 Like

Feel free to ask questions if there’s anything you’re unsure about or anything you get stuck on.
Sometimes it’s not always evident why certain techniques are preferred.
Also, when it comes to C++, a lot of the reasons for preferring certain styles/techniques over others involve knowing the language’s history - moreso than other languages.

Hey George, how is this progressing?

Sorry for disappearing - life happened quite a lot!

Still need to go through this again, but plan to tomorrow. Bed now. Thank you all for the help once again

2 Likes