Help with my first game (platformer)

I’m relatively new to c++ and I’ve been reading through @crait 's amazing tutorials. Though I would like to create a 2d platformer so I started off my code.

#include <Arduboy2.h>
Arduboy2 arduboy;

int playerwidth = 8;
int playerheight = 8;
int playery = 34;
int playerx = 10;

const unsigned char PROGMEM player[] =
{
// width, height,
16, 16,
0xfe, 0x01, 0x3d, 0x25, 0x25, 0x3d, 0x01, 0x01, 0xc1, 0x01, 0x3d, 0x25, 0x25, 0x3d, 0x01, 0xfe, 
0x7f, 0x80, 0x9c, 0xbc, 0xb0, 0xb0, 0xb2, 0xb2, 0xb3, 0xb0, 0xb0, 0xb0, 0xbc, 0x9c, 0x80, 0x7f, 
};

void setup() {
  arduboy.begin();
  arduboy.setFrameRate(75);
  arduboy.clear();

}

void loop() {
  if (!arduboy.nextFrame()) {
    return;
  }
  arduboy.clear();
  arduboy.pollButtons();
  Sprites::drawOverwrite (playerx, playery, player, 0);
  Rect (playerx, playery, playerwidth, playerheight);
  if (arduboy.pressed(RIGHT_BUTTON)) {
    playerx = playerx + 1;
  }
  if (arduboy.pressed(LEFT_BUTTON)) {
    playerx = playerx - 1;
  }
  if (arduboy.justPressed(A_BUTTON)) {
    playery = playery - 20;
  }
  if (arduboy.justReleased(A_BUTTON)) {
    playery = playery + 20;
  }
  arduboy.display();
  
}

Currently, I’m using an 8x8 tile from @crait 's tutorials for convenience but I plan on changing it later. Now one thing I thought was to make it so that when the A button is pressed, the player y decreases by 20, then when the A button is released, I increase it by 20 again. There is an issue though, you can hold A and the player will stay in the air. Also, when the A button is released, the player snaps back instantly, so is there any way I could make a delay so that it is more like a normal platformer? Thanks. :slightly_smiling_face:

I assume you want the player to jump and then fall to earth after a second or so?

There are a number of ways:

Option 1

With your existing code, you could simply add a counter at the top of the code like so:

#include <Arduboy2.h>
Arduboy2 arduboy;

int playerwidth = 8;
int playerheight = 8;
int playery = 34;
int playerx = 10;
int playerCount = 0;

Then when you press the A button, you can use the new variable to manage how long the player is in the sky for.

void loop() {
  ...
  if (arduboy.justPressed(A_BUTTON) && playerCount == 0) {
    playery = playery - 20;
    playerCount = 30;
  }
 
  // Not needed anymore .. 
  // if (arduboy.justReleased(A_BUTTON)) {
  //   playery = playery + 20;
  // }


  // Should the player fall to earth?

  if (playerCount > 0) {
    playerCount--;
    if (playerCount == 0) {
      playery = playery + 20;
    }
  }
 
  arduboy.display();
  
}

Option 2

Having the player spring from one level to another is not really nice.

There is a second tutorial that goes on to describe how to make the ‘Chrome Dino’ game here > Make Your Own Sideways Scroller: Part 4 - Moving and Rendering Steve

I have pointed you to part 4 as it describes how to make the dinosaur jump which is what you are looking to do.

1 Like

The reason the player stays in the air is because you only add the 20 back when justReleased(A_BUTTON) is true, and that only happens when the player releases the A button.

That’s because you add the 20 back on instantly.
You aren’t doing it gradually over several frames, you’re doing it all at once in a single frame.

As @filmote mentions, you can count frames.
Here’s an alternative way based on frame counting that should decrease the player’s height by 1 pixel per frame:

#include <Arduboy2.h>

Arduboy2 arduboy;

int16_t playerWidth = 8;
int16_t playerHeight = 8;
int16_t playerY = 34;
int16_t playerX = 10;
uint8_t playerJumpOffset = 0;

constexpr unsigned char PROGMEM playerSprite[]
{
    // Width, Height,
    16, 16,

    // Frame 0
    0xFE, 0x01, 0x3D, 0x25, 0x25, 0x3D, 0x01, 0x01, 0xC1, 0x01, 0x3D, 0x25, 0x25, 0x3D, 0x01, 0xFE,
    0x7F, 0x80, 0x9C, 0xBC, 0xB0, 0xB0, 0xB2, 0xB2, 0xB3, 0xB0, 0xB0, 0xB0, 0xBC, 0x9C, 0x80, 0x7F,
};

void setup()
{
  arduboy.begin();
}

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

  arduboy.clear();

  arduboy.pollButtons();

  if(arduboy.pressed(RIGHT_BUTTON))
  {
    playerX += 1;
  }

  if(arduboy.pressed(LEFT_BUTTON))
  {
    playerX -= 1;
  }

  if(arduboy.justPressed(A_BUTTON) && (playerJumpOffset == 0))
  {
    playerY -= 20;
    playerJumpOffset = 20;
  }
  
  // If the player jump offset is greater than 0
  if(playerJumpOffset > 0)
  {
      // Decrease player jump offset by 1
      playerJumpOffset -= 1;

      // Move down player by 1
      playerY += 1;
  }

  Sprites::drawOverwrite(playerX, playerY, playerSprite, 0);

  arduboy.display();
}

Note that:

  • playerX -= 1; is equivalent to playerX = playerX - 1;.
  • playerX += 1; is equivalent to playerX = playerX + 1;
  • uint8_t variables store values between 0 and 255 inclusive
  • int16_t variables store values between -32768 and 32767 inclusive
  • On Arduboy, int and int16_t are the same thing

(If that’s too fast, there are some tricks you can do to slow it down.)


A more robust approach would be to introduce some notion of being ‘on the ground’ or ‘on a solid surface’ and a notion of ‘gravity’, after which you’d apply the ‘gravity’ (i.e. a downward force) every frame until the player’s character is standing on a solid surface. Then jumping would be a matter of applying an upward force to the player’s character, which would decay over a few frames due to the effect of the gravity.

For this you’d need to give the player’s character a velocity (in addition to the position they already have), and apply the velocity to the player’s position each frame, and you’d have to use either floating points (float) or fixed points to account for sub-pixel amounts of movement. You’d also need working collisions before you could have a notion of ‘on the ground’ or ‘on a solid surface’.

Basically, you’d be looking at a very basic physics simulation. But that might be a bit advanced for you at the moment.

(If you decide to have a go at going down that route though, you may want to look at my basic physics demo Physix and its source code, my 2D Platformer Demo, and its source code.)


Just so you know, this line is creating a Rect and then immediately discarding it. If you need the rectangle for something, you need to store it in a variable.

Here’s a few alternative options for you to choose from:

// Modern style "uniform initialisation syntax" (C++11 onwards)
Rect playerRect { playerX, playerY, playerWidth, playerHeight };
// Older (pre-C++11) style
Rect playerRect = Rect(playerX, playerY, playerWidth, playerHeight);
// Older style with modern (C++11 onwards) type inference
auto playerRect = Rect(playerX, playerY, playerWidth, playerHeight);
1 Like

Why not just use a normal “int”?

Firstly, because int varies in size between platform.

On Arduboy it’s 16 bits, but on desktop it would almost certainly be 32 bits.
(Theoretically there’s no upper bound, but I’ve never known a system to use anything larger than a 64 bit int.)

It’s generally better to be specific about which size of integer you intend to be using for the sake of portability.

Secondly, if you use a smaller type when you can then you’ll save memory, which is important on the Arduboy where memory is scarce. If you can use uint8_t or int8_t instead of uint16_t or int16_t then that’s a byte of memory saved (or more, depending on the circumstances).

Thirdly, it expresses your intent better. (I.e. “I intend that this variable will not exceed the upper bounds of a uint8_t. If it does then that’s a bug that I need to fix”.)

1 Like

I am fascinated with this response :slight_smile:

To lead with " int varies in size between platform" in response to a person who is obviously learning to program on his first platform (the Arduboy) is a strange one. I am pretty sure there are about two people on this forum that actually port their games to other platforms and those people are aware of the issues. By the time @Ard_Flamingo has finished writing the best Arduboy platformer and looks to port it, he will have covered enough ground that he understands these concerns.

The second one makes perfect sense and if @Ard_Flamingo is going to continue building out this game, he will certainly run into memory constraints.

Sorry, don’t mean to sound rude to your (perfectly valid and factual) response. I think what fascinates me is the emphasis.

1 Like

That’s the order they came to mind in, not necessarily the order of relevence.

The fact int varies in size nearly always comes to mind first because it’s probably the most notable thing about C++'s int. Most languages only have fixed size integer types, they don’t have types that vary by platform. (E.g. Java and C# both have a 32-bit int, which will always be 32-bit on any platform.)

Besides which, any Arduboy game can end up running on another platform even if it’s not the author doing the porting. You can say “that’s the porter’s problem”, but personally I don’t think 4 characters is enough of a hardship to justify giving porters extra work. (Unless the goal is to make it harder for porters, in which case just write a few functions in assembly, that’ll really give them trouble.)