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
andy
position of our circle
- Check for button input and increase/decrease
x
andy
accordingly
- Check that the position can’t go off-screen
- Draw a circle at the current
x
andy
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(); }
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
andB
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
andy
position directly
- Instead we change
xMomentum
andyMomentum
- Then we use
xMomentumg
andyMomentum
to change thex
andy
position
- If the user doesn’t press
LEFT_BUTTON
orRIGHT_BUTTON
, thenxMomentum
should decrease towards0.0
- If the user presses
A_BUTTON
we add an upwards force toyMomentum
to make them jump
- And we add
gravity
as a constant downward force that acts onyMomentum
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(); }
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!
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(); }
➡️ Continue in Part III: Sprites