Arduboy Course - Part II: Movement

date
Jun 4, 2024
type
KnowledgeBase
year
slug
arduboy-basics-2
status
Published
tags
Arduino
C++
Microcontroller
summary
Let’s make things move and work our way towards something we could call a “game”!
Previous Chapter: Part I: BASICS
Ok, with all the basics out of of the way, let’s make something!

Movement

Let’s make a circle we can move via the up/down/left/right buttons!
  • Create variables for the x and y position of our circle
  • Check for button input and increase/decrease x and y accordingly
  • Check that the position can’t go off-screen
  • Draw a circle at the current x and y position!
Here’s the result:
#include <Arduino.h> #include <Arduboy2.h> Arduboy2 arduboy; int x = 64; int y = 32; int radius = 6; void setup() { arduboy.begin(); } void loop() { if (!(arduboy.nextFrame())) return; arduboy.clear(); arduboy.pollButtons(); arduboy.drawRect(0, 0, arduboy.width(), arduboy.height(), WHITE); // Draw a white frame around the screen // Change the position // Up to go up if(arduboy.pressed(UP_BUTTON)) { y -= 1; } // Left to go left if(arduboy.pressed(LEFT_BUTTON)) { x -= 1; } // Right to go right if(arduboy.pressed(RIGHT_BUTTON)) { x += 1; } // Down to go down if(arduboy.pressed(DOWN_BUTTON)) { y += 1; } // Keep the position on the screen if(x < 0 + radius) { x = radius; } if(x > arduboy.width() - radius) { x = arduboy.width() - radius; } if(y < 0 + radius) { y = 0 + radius; } if(y > arduboy.height() - radius) { y = arduboy.height() - radius; } // Draw a circle at the current position arduboy.fillCircle(x, y, radius, WHITE); arduboy.display(); }
notion imagenotion image
Challenges
  • Can you change it so the circle moves faster?
  • Can you make it so the circle’s radius can be changed with the A and B buttons?

Movement via Momentum + Gravity

The previous example changed the position directly. But now we want to change the program so that movement is based on momentum!
  • When we press a button we no longer change the x and y position directly
  • Instead we change xMomentum and yMomentum
  • Then we use xMomentumg and yMomentum to change the x and y position
  • If the user doesn’t press LEFT_BUTTON or RIGHT_BUTTON, then xMomentum should decrease towards 0.0
  • If the user presses A_BUTTON we add an upwards force to yMomentum to make them jump
  • And we add gravity as a constant downward force that acts on yMomentum and pushes the player towards the ground
#include <Arduino.h> #include <Arduboy2.h> Arduboy2 arduboy; float x = 64.0; float y = 32.0; int radius = 5; float gravity = 0.1; float xMomentum = 0.0; float yMomentum = 0.0; void setup() { arduboy.begin(); } void loop() { if (!(arduboy.nextFrame())) return; arduboy.clear(); arduboy.pollButtons(); arduboy.drawRect(0, 0, arduboy.width(), arduboy.height(), WHITE); // Left / Right momentum if(arduboy.pressed(LEFT_BUTTON)) { xMomentum = -1.0; } else if(arduboy.pressed(RIGHT_BUTTON)) { xMomentum = 1.0; } else { // Slow down if no input xMomentum = xMomentum * 0.95; } // Jump if(arduboy.justPressed(A_BUTTON)) { yMomentum = -2.0; } // Add Gravitational force (but cap at a 4) if(yMomentum < 4) yMomentum += gravity; // Update the position based on momentum x += xMomentum; y += yMomentum; // Check if the circle is out of bounds // Left Edge if(x < 0 + radius) { x = radius; } // Right Edge if(x > arduboy.width() - radius) { x = arduboy.width() - radius; } // Top Edge if(y < 0 + radius) { y = 0 + radius; } // Bottom Edge if(y > arduboy.height() - radius - 2) { y = arduboy.height() - radius - 2; } // Draw a circle at the current position arduboy.fillCircle(x, y, radius, WHITE); // then we finally we tell the arduboy to display everything we wrote into the buffer this frame arduboy.display(); }
notion imagenotion image
Challenges
  • Can you make it so the player can only jump when in contact with the floor?

🎓 Making it Frame-Rate-Independent

Arduboy tries its best to keep running at a stable frame-rate. So unless you’re in danger of dipping below the set framerate, you can ignore all this for simplicity’s sake. But be aware that in other game engines you usually run at variable frame-rates and very much have to account for that!
Arduboy tries its best to keep running at a stable frame-rate. So unless you’re in danger of dipping below the set framerate, you can ignore all this for simplicity’s sake. But be aware that in other game engines you usually run at variable frame-rates and very much have to account for that!
The previous example will behave different at different frame-rates. You can try this out by changing the setup function to this:
void setup() { arduboy.begin(); arduboy.setFrameRate(20); }
A low frame-rate like 20 brings it to a crawl, a high frame-rate like 120 puts it on steroids. But ideally we’d like it to behave the same no matter the frame-rate ends up being!
To achieve this we need to
  • Calculate the deltaTime - the actual time that passed since the previous frame
  • Set all our speed values in pixels per second and then multiply them by the deltaTime we calculated
#include <Arduino.h> #include <Arduboy2.h> Arduboy2 arduboy; float x = 64.0; float y = 32.0; int radius = 5; float gravity = 240.0; float xMomentum = 0.0; float yMomentum = 0.0; float speed = 60.0; void setup() { arduboy.begin(); } long lastFrameMs = 0; float deltaTime; void loop() { if (!(arduboy.nextFrame())) return; // calculate the time since the last frame long ms = millis() - lastFrameMs; // in ms deltaTime = ms * 0.001; // convert to seconds lastFrameMs = millis(); // remember the current ms for use during the next frame arduboy.clear(); arduboy.pollButtons(); arduboy.drawRect(0, 0, arduboy.width(), arduboy.height(), WHITE); // Left / Right momentum if(arduboy.pressed(LEFT_BUTTON)) { xMomentum = -speed; } else if(arduboy.pressed(RIGHT_BUTTON)) { xMomentum = speed; } else { // Slow down if no input xMomentum = xMomentum * pow(0.05, deltaTime); } // Jump if(arduboy.justPressed(A_BUTTON)) { yMomentum = -speed * 2.0; } // Add Gravitational force if(yMomentum < 100.0) yMomentum += gravity * deltaTime; // Update the position based on momentum x += xMomentum * deltaTime; y += yMomentum * deltaTime; // Check if the circle is out of bounds // Left Edge if(x < 0 + radius) { x = radius; } // Right Edge if(x > arduboy.width() - radius) { x = arduboy.width() - radius; } // Top Edge if(y < 0 + radius) { y = 0 + radius; } // Bottom Edge if(y > arduboy.height() - radius - 2) { y = arduboy.height() - radius - 2; yMomentum = 0.0; } // Draw a circle at the current position arduboy.fillCircle(x, y, radius, WHITE); // Display current momentum arduboy.setCursor(4, 4); arduboy.print(xMomentum); arduboy.setCursor(4, 12); arduboy.print(yMomentum); // Display current position arduboy.setCursor(arduboy.width() - 20, 4); arduboy.print((int)x); arduboy.setCursor(arduboy.width() - 20, 12); arduboy.print((int)y); // then we finally we tell the arduboy to display everything we wrote into the buffer this frame arduboy.display(); }
notion imagenotion image
 
➡️ Continue in Part III: Sprites

Leave a comment