Space Lasers - My First Arduboy Game

Space_Lasers.ino.leonardo.hex (40.6 KB)

Hi all! So have been into Arduino for 8 months now, and decided to get an Arduboy. I made this simple game and would like some feedback from you guys. The project can be downloaded here. Enjoy! (P.S. I might add new features like high scores but when I do I will update GitHub.)

9 Likes

This is awesome for only only being into it for a short time! The game runs a little fast, I might slow it down a little but maybe that is just how it plays on the emulator?

Also it seems like you might want to add a debounce to the pause button? I kept pressing pause and it would pause and upause very fast getting me stuck in the pause screen!

Thanks for the feedback! It definitely isn’t that fast in real life, and I definitely will add a debounce to the pause button. Thanks!

Actually, after revisiting my game and playing it on my actual Arduboy, it doesn’t seem to have a debounce problem. It may have to do with the fact that I have so many variables being checked and modified each time the loop is run. I noticed that when I added more commands (like drawing the background bitmap) to my loop, the loop was slower(ships moved way slower). It may affect the frequency at which the button is being checked, thus reducing the debounce issue. If you have an Arduboy, maybe you might want to download my game onto that and try it for yourself. It may be a good idea to add debounce anyway just so that it can be played on the emulator.

You should be controlling the speed by running at a constant frame rate. You should be using nextFrame() at the beginning of loop().

void loop() {
  if (!arduboy.nextFrame()) {
    return;
  }

The default rate is 60FPS. If you want something different add setFrameRate() or setFrameDuration() in setup().

If you want to make sure that your loop is executing within a frame’s duration, temporarily change nextFrame to nextFrameDEV. The USB TX LED will then light if you can’t keep up to the rate you’ve set.


For things like a pause button, you can use justPressed(), which will only detect a single press of a button, regardless of how long it’s held pressed. It gives you a natural debounce. For justPressed() to work, you need a call to pollButtons() in your loop. This can go right after the nextFrame() test.

void loop() {
  if (!arduboy.nextFrame()) {
    return;
  }

  arduboy.pollButtons();
1 Like

@MLXXXp is right about the lack of framerate regulation.
I suspected this would be the case because I remember another game doing the exact same thing fairly recently.

Not bad for a first attempt.

For future reference, you’re not supposed to .zip the source code before uploading it to GitHub.
GitHub automatically packs the code into a .zip file when you download it, so people will end up downloading a .zip within a .zip.
Also if you upload it as-is, you can read the source directly from GitHub without having to download it.

1 Like

Wow, thank you guys for the feedback. I will be sure to edit the game and post the code on GitHub the right way :wink:

OK so I uploaded the newest version to GitHub. I found the best frame rate for the game and fixed the issue with the pause button.

2 Likes

Would it be possible for the stars to move left to right?

Some code I stole from another game (on another platform).

Call initStarField() from your setup.
Call updateStarField() from your game’s main loop.
Render the stars before you draw your players / enemies.

#DEFINE STAR_COUNT 50
int16_t stars[STAR_COUNT];

// ----------------------------------------------------------------------------
//  Update star field .. 
//
void BaseState::updateStarField() {

    for (uint8_t x=0; x < STAR_COUNT; x++) {
        
        this->stars[x]++;
        if (this->stars[x] < 0) this->stars[x] = rand() % 50 + 128; // a random distance past 128
        
    }

}

// ----------------------------------------------------------------------------
//  Update star field .. 
//
void BaseState::initStarField() {

    // Populate star field ..

    for (uint8_t x = 0; x < STAR_COUNT; x++) {
        
        this->stars[x] = rand() % 180;
        
    }

}

    

    // Render star field ..

    for (uint8_t x = 0; x < STAR_COUNT; x++) {
        
        arduboy.drawPixel(x * 5, this->stars[x]);

    }

Actually that’s not a bad idea. I could make another identical bitmap of the stars and have both of them move left until the one on the left reaches the left edge of the screen. Then it could go to the right side off of the screen and the cycle can just repeat. If space ships are not going insanely fast, then the stars won’t move that much because they are probably light years away, though.

Strange, I was sure the Arduboy already had a game that did this.
I certainly remember writing something like this at one point to demonstrate the idea.

(Edit: I think I found what I was thinking of, and it actually generated a front-facing field of stars rather than horizontally scrolling stars, so not quite the same.)

Either way this seems to be mixing up the x and the y.
If you want to move the stars left and right then the loop counter should be the y coordinate and the array should be holding the x values rather than the other way around.

That would work too.

Mmm … maybe my code was a bit inappropriate as it is for a game with a starfield that falls from top to bottom (not left to right).

The idea with an array and random star placement is that it is exactly that - random. Otherwise you get that ‘Flintstones’ effect where the same scenery keeps going past. Anyhow, the code can be easily adapted.

I wonder if anyone would actually notice though.

You don’t need the first
arduboy.nextFrame();
statement on line 65 immediately after loop(). It’s not doing anything.


I would move the arduboy.pollButtons() statement on line 390 to the top of loop(), immediately after the nextFrame() test, as I previously suggested. It makes it easier to see that you’re using it.

void loop() {
  if (!arduboy.nextFrame()) {
    return;
  }

  arduboy.pollButtons(); 
    
  switch (gamestate){

You should have a break; statement and the end of each of your case blocks. Otherwise, the execution will “fall through” from the end of one case to the start of the next. It’s not causing a problem now but it may bite you in the future.

1 Like

So I have edited the game so that the stars are different, and they scroll by (thanks for the idea, filmote). I also did what MLXXXp suggested. I am going to post it in GitHub and post the emulator again but running the updated code.

Space_Lasers.ino.leonardo.hex (40.8 KB)

You didn’t add a break; at the end of case 0, which is the important one (unless you actually want it to “fall through”).

https://www.tutorialspoint.com/cplusplus/cpp_switch_statement.htm

1 Like

Well when I had the break at the end of case 0 the game started over whenever I pressed the button to start, so I just took it out.

While we’re handing out advice, you don’t actually need your whilee variable.

Instead of:

whilee = 1;

while (whilee == 1) {
	if (arduboy.pressed(UP_BUTTON)) {
		arduboy.audio.on();
		whilee = 0;
	}
	if (arduboy.pressed(DOWN_BUTTON)) {
		arduboy.audio.off();
		whilee = 0;
	}
}

You can do:

while (true) {
	if (arduboy.pressed(UP_BUTTON)) {
		arduboy.audio.on();
		break;
	}
	if (arduboy.pressed(DOWN_BUTTON)) {
		arduboy.audio.off();
		break;
	}
}

(There is actually a better way to structure your program than using delays and whiles, but it would mean quite a big architectural change, so you might want to wait until you’re more comfortable with C++ before attempting that.)

That sounds like you accidentally put it before gamestate = 1;.

One thing that might make it easier to manage your game states (and prevent those sorts of mistakes) is to have the code for each game state in a separate function, that way it’s easier to see when/where the code for a state begins and ends.

1 Like

Nice game. If you increase the number of enemies and maybe make them smaller it would add quite some action. Maybe also make the enemies appear in waves so the player can get some rest between epic shooting battles.