Help with game development


(JohnnydCoder) #1

Hello Arduboy Community!

I was following the tutorials on this website and tried to make my own game using the skills found there. I was trying to make my sprite go through 3 different images: a standing image, a running image, and another running image. The code starts with the standing image. When the Right button is pressed, I want it to change to the first running image. It is supposed to file through the first and second running images, and go and stay at the standing image when the right button is not pressed anymore. When I run it, however, it only stays on the standing image.:confused:

Am I not going through the sprites correctly? Or did I misunderstand the tutorials and should read through them again?

Here is the code:

#include "images.h"

#define PLAYER_WIDTH    10
#define PLAYER_HEIGHT   16
#define PLAYER_X_OFFSET   WIDTH / 2 - PLAYER_WIDTH / 2
#define PLAYER_Y_OFFSET    HEIGHT / 2 - PLAYER_HEIGHT / 2

enum Stance {
  Standing,
  Running1,
  Running2
};

const byte *player_images[] { player_standing, player_running1, player_running2 };

struct Player {
  int x;
  int y;
  Stance stance;
  bool sliding;
  const byte *image;
};

Player player = {PLAYER_X_OFFSET, PLAYER_Y_OFFSET, 0, false, player_standing};

void drawplayer() {
player.image = player_images[player.stance];
arduboy.fillRect(PLAYER_X_OFFSET, PLAYER_Y_OFFSET, PLAYER_WIDTH, PLAYER_HEIGHT, BLACK);  
Sprites::drawExternalMask(player.x, player.y, player.image, 0);
if (arduboy.pressed(RIGHT_BUTTON)) {
    
  switch(player.stance) {
    case Stance::Standing:
    player.stance = Stance::Running1;
    break;

    case Stance::Running1:
    player.stance = Stance::Running2;
    break;

    case Stance::Running2:
    player.stance = Stance::Running1;
  }
  
  if (arduboy.notPressed(RIGHT_BUTTON) && player.stance == Stance::Running1 || player.stance == Stance::Running2) {
      player.stance = Stance::Standing;
    }
  }
}
}
}

And here is the image data for the sprites:

const byte PROGMEM player_standing[] = {
  //Standing 
10, 16,
   
0x78, 0x84, 0x02, 0x29, 0x41, 0x41, 0x29, 0x02, 0x84, 0x78, 0x00, 0x3c, 0x85, 0xfe, 0x1e, 0x1e,
0xfe, 0x85, 0x3c, 0x00
};

const byte PROGMEM player_running1[] = {
  ////Running1
10, 16,
   
0x78, 0x84, 0x02, 0x01, 0x01, 0x21, 0x49, 0x42, 0x84, 0x78, 0x00, 0x18, 0x85, 0x7e, 0x1e, 0x1e,
0xfe, 0x85, 0x08, 0x04, 
};

const byte PROGMEM player_running2[] = {
  //Running2 
10, 16,
   
0x78, 0x84, 0x02, 0x01, 0x01, 0x21, 0x49, 0x42, 0x84, 0x78, 0x00, 0x18, 0x85, 0x7e, 0xfe, 0xfe,
0x9e, 0x05, 0x08, 0x04, 
};

Note: This code is part of a bigger game so let me know if you want to see more code.

Thanks a lot!:grinning:


(Simon) #2

if (arduboy.notPressed(RIGHT_BUTTON) && player.stance == Stance::Running1 || player.stance == Stance::Running2) { player.stance = Stance::Standing; }

Should this be:

if (arduboy.notPressed(RIGHT_BUTTON) && ( player.stance == Stance::Running1 || player.stance == Stance::Running2 ) ) { player.stance = Stance::Standing; }

With extra brackets (in bold)?


(JohnnydCoder) #3

Thanks @filmote !

I actually have another problem as well…

When I try to upload the game, an error pops up saying “redefinition of 'const byte player_standing []”

Is this because I am not doing something right with my images.h file in which my bitmaps and sprites are placed?

Here is the entire file:

const byte PROGMEM player_standing[] = {
  //Standing 
10, 16,
   
0x78, 0x84, 0x02, 0x29, 0x41, 0x41, 0x29, 0x02, 0x84, 0x78, 0x00, 0x3c, 0x85, 0xfe, 0x1e, 0x1e,
0xfe, 0x85, 0x3c, 0x00
};

const byte PROGMEM player_running1[] = {
  ////Running1
10, 16,
   
0x78, 0x84, 0x02, 0x01, 0x01, 0x21, 0x49, 0x42, 0x84, 0x78, 0x00, 0x18, 0x85, 0x7e, 0x1e, 0x1e,
0xfe, 0x85, 0x08, 0x04, 
};

const byte PROGMEM player_running2[] = {
  //Running2 
10, 16,
   
0x78, 0x84, 0x02, 0x01, 0x01, 0x21, 0x49, 0x42, 0x84, 0x78, 0x00, 0x18, 0x85, 0x7e, 0xfe, 0xfe,
0x9e, 0x05, 0x08, 0x04, 
};

const byte PROGMEM ball[] = {
  //Ball

  0x3c, 0x42, 0x89, 0x8d, 0x81, 0x81, 0x42, 0x3c, 
};


const unsigned char tiles[9][32] PROGMEM  = {
  //Grass of the field
  { 0x00, 0x40, 0x02, 0x00, 0x20, 0x00, 0x00, 0x08, 0x40, 0x00, 0x01, 0x10, 0x00, 0x02, 0x80, 0x00, 0x10, 0x02, 0x00, 0x80, 0x08, 0x01, 0x00, 0x20, 0x04, 0x00, 0x00, 0x41, 0x00, 0x00, 0x08, 0x00 },
  //Left line of the field
  { 0xff, 0xff, 0xff, 0x00, 0x22, 0x00, 0x00, 0x08, 0x40, 0x00, 0x00, 0x12, 0x00, 0x00, 0x20, 0x02, 0xff, 0xff, 0xff, 0x00, 0x00, 0x11, 0x00, 0x40, 0x04, 0x00, 0x00, 0x41, 0x00, 0x08, 0x00, 0x40 },
  //Top line of the field
  { 0x27, 0x07, 0x07, 0x07, 0x27, 0x07, 0x07, 0x0f, 0x47, 0x07, 0x07, 0x07, 0x47, 0x07, 0x0f, 0x07, 0x00, 0x22, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x04, 0x00, 0x00, 0x41, 0x00, 0x08, 0x00, 0x00 },
  //Right line of the field
  { 0x20, 0x04, 0x00, 0x00, 0x22, 0x00, 0x00, 0x08, 0x40, 0x00, 0x04, 0x10, 0x00, 0xff, 0xff, 0xff, 0x00, 0x22, 0x00, 0x00, 0x00, 0x11, 0x00, 0x40, 0x04, 0x00, 0x00, 0x41, 0x00, 0xff, 0xff, 0xff },
  //Bottom line of the field
  { 0x02, 0x40, 0x00, 0x00, 0x22, 0x00, 0x00, 0x08, 0x40, 0x00, 0x00, 0x12, 0x00, 0x00, 0x20, 0x02, 0xe0, 0xe0, 0xe0, 0xe4, 0xe0, 0xe1, 0xe0, 0xe0, 0xe4, 0xe0, 0xe0, 0xe1, 0xe0, 0xe8, 0xe0, 0xe0 },
  //Top/left corner of the field
  { 0xff, 0xff, 0xff, 0x07, 0x27, 0x07, 0x07, 0x0f, 0x47, 0x07, 0x07, 0x17, 0x07, 0x07, 0x87, 0x07, 0xff, 0xff, 0xff, 0x00, 0x08, 0x01, 0x00, 0x20, 0x04, 0x00, 0x00, 0x41, 0x00, 0x00, 0x08, 0x00 },
  //Top/right corner of the field
  { 0x07, 0x47, 0x07, 0x07, 0x27, 0x07, 0x07, 0x0f, 0x47, 0x07, 0x07, 0x17, 0x07, 0xff, 0xff, 0xff, 0x10, 0x02, 0x00, 0x80, 0x08, 0x01, 0x00, 0x20, 0x04, 0x00, 0x00, 0x41, 0x00, 0xff, 0xff, 0xff },
  //Bottom/left corner of the field
  { 0xff, 0xff, 0xff, 0x00, 0x20, 0x00, 0x00, 0x09, 0x40, 0x00, 0x00, 0x12, 0x00, 0x00, 0x40, 0x04, 0xff, 0xff, 0xff, 0xe0, 0xe0, 0xe1, 0xe0, 0xe0, 0xe4, 0xe0, 0xe0, 0xe1, 0xe0, 0xe0, 0xe4, 0xe0 },
  //Bottom/right cornder of the field
  { 0x02, 0x40, 0x00, 0x00, 0x22, 0x00, 0x00, 0x08, 0x40, 0x00, 0x00, 0x12, 0x00, 0xff, 0xff, 0xff, 0xe0, 0xe0, 0xe0, 0xe4, 0xe0, 0xe1, 0xe0, 0xe0, 0xe4, 0xe0, 0xe0, 0xe1, 0xe0, 0xff, 0xff, 0xff },
};

As you might see, I am making a soccer/football game.:grinning:

I have seen people add something like #ifndef to begin #endif to end their files. Is that what I’m missing?


(Pharap) #4

Try putting #pragma once at the top.

If that doesn’t work then you’ll have to separate into a .h file and a .cpp file, which I can show you how to do.


While I’m at it, I’ll tell you a little trick.
If you pack your images like this:

#pragma once

const char playerImages[] PROGMEM =
{
	// Width, Height
	10, 16,
	   
	// Standing
	0x78, 0x84, 0x02, 0x29, 0x41, 0x41, 0x29, 0x02, 0x84, 0x78, 0x00, 0x3c, 0x85, 0xfe, 0x1e, 0x1e,
	0xfe, 0x85, 0x3c, 0x00,

	// Running1   
	0x78, 0x84, 0x02, 0x01, 0x01, 0x21, 0x49, 0x42, 0x84, 0x78, 0x00, 0x18, 0x85, 0x7e, 0x1e, 0x1e,
	0xfe, 0x85, 0x08, 0x04,

	// Running2   
	0x78, 0x84, 0x02, 0x01, 0x01, 0x21, 0x49, 0x42, 0x84, 0x78, 0x00, 0x18, 0x85, 0x7e, 0xfe, 0xfe,
	0x9e, 0x05, 0x08, 0x04, 
};

Then you don’t need your player_images array or const byte * image in player, you can just do:

Sprites::drawExternalMask(player.x, player.y, playerImages, player.stance);

Which is how quite a lot of us would do it if writing a game.


(JohnnydCoder) #5

The #pragma once works!

Thanks for all the help! :grinning:

Just one last question for future reference:
What exactly does #pragma do?


(Pharap) #6

The short version is that it stops the file being processed multiple times, which prevents ‘duplicate definition’ type errors.

There’s a bit more about it here.
Don’t worry about the “non-standard” bit, I’ve yet to encounter a C++ compiler that doesn’t support it, and you’ll probably only be compiling for Arduboy anyway.


(JohnnydCoder) #7

Great!

Now I can move on with my game!


(Simon) #8

I suspect @Johnnydb is doing this because that is how I did it in the Dino tutorial. In this tutorial, the dimensions of the dinosaur change as he runs or ducks.

If the graphics are all of the same dimensions (as these appear to be) then the approach you have given is much more desirable.


(JohnnydCoder) #9

Thanks for your help @filmote and @Pharap!

I got my player running all over my screen now! :smile:


(JohnnydCoder) #10

I’ve got my player moving, but now I would like a ball to move with a player when the player makes contact with the ball (like a soccer player who dribbles a ball) .

I have been able to move the map using @crait’s recent tutorials.

If it’s too much trouble, I can adapt the game.

Thanks!


(Stephane C) #11

Well someone once told me that in order to find how to code something, you should first write it in english(or whatever is your language) and then you can try to ‘transate it’ in C++.

So basically what you want is:

Make the ball follow the player when he makes contact with the ball.

That mean that if the player(object) collide with the ball(another object), you want the ball location to be in relation to the player coordinate.

So basically if player rect() collide with ball rect() then ball x and y coordinate must equal player x + player width and player y + player height / 2 (depending on where in relation to the player you want the ball).

And then i guess the next step would be to translate that into proper C++

I hope you get the idea, and i am by no mean good at this.

While it’s all good to follow tutorials, understanding what’s happening is even more important.

Don’t be afraid to try things yourself and try to do it your way, and then, after you have something working you can always ask people here how they would do it and ask why. Some things people told me here were eyes opening to me.


(JohnnydCoder) #12

I see what you are saying.

I can figure out ways to put the ball when the player makes contact with it. Now I want to put it on a specific place on a map, not a specific place on the screen. I think it’s too advanced for me to figure out. :smile:

Is there any way to do that?:confused:


(Pharap) #13

I’d suggest starting with a smaller problem, like how collision detection works.


(JohnnydCoder) #14

I’ve been trying to use the arduboy2.collide function, because my two sprites are basically rectangular. :smile:

I uploaded the code to my arduboy, but it said there was a collision without the objects touching.

Here’s the code I used:

#define BALL_SIZE    8

int ballx{PLAYER_X_OFFSET + PLAYER_HEIGHT};
int bally{PLAYER_Y_OFFSET + PLAYER_HEIGHT / 2};

Rect ballRect = {
  ballx, bally, BALL_SIZE, BALL_SIZE
};

#define PLAYER_WIDTH    10
#define PLAYER_HEIGHT  16
#define PLAYER_X_OFFSET   WIDTH / 2 - PLAYER_WIDTH / 2
#define PLAYER_Y_OFFSET    HEIGHT / 2 - PLAYER_HEIGHT / 2

Rect playerRect = {
  PLAYER_X_OFFSET, PLAYER_Y_OFFSET, PLAYER_WIDTH, PLAYER_HEIGHT
};


bool collision = false;

void contact () {
if (arduboy.collide(ballRect, playerRect))  {
  collision = true;
}
  
if (collision = true) {
  arduboy.setCursor(0, 0);
  arduboy.print("Collision");
}
}

Am I using collide wrong, or should I try a different way?:confused:

Thanks!


(Scott) #15

Should be:

if (collision == true) {

or just:

if (collision) {


(JohnnydCoder) #16

Wow…

I can’t believe I missed that! :laughing:


(JohnnydCoder) #17

Ok, so I made the collision work and I got the player moving with the ball.


void contact () {
if (arduboy.collide(ballRect, playerRect))  {
  collision = true;
}
  
if (collision == true) {
  arduboy.setCursor(0, 0);
  arduboy.print("Collision");
  player.hasBall = true;
}
}


void drawplayer() {
player.image = playerImages[player.stance];
arduboy.fillRect(PLAYER_X_OFFSET, PLAYER_Y_OFFSET, PLAYER_WIDTH, PLAYER_HEIGHT, BLACK);  
Sprites::drawExternalMask(player.x, player.y, playerImages, playerImages, player.stance, player.stance);
}

void playerinput() {
  if(arduboy.pressed(UP_BUTTON)) {
    if(mapy < PLAYER_Y_OFFSET + 12) {
      mapy += 1;
    }

    if (arduboy.pressed(UP_BUTTON) && (player.hasBall = true)) {
      ballx = PLAYER_X_OFFSET + 6;
      bally = PLAYER_Y_OFFSET + 6;
    }
    else {
      ballx = PLAYER_X_OFFSET + PLAYER_WIDTH + 40;
      bally = PLAYER_Y_OFFSET + PLAYER_HEIGHT / 2;
    }
      if (arduboy.everyXFrames(7)) {  
  switch(player.stance) {
    case Stance::Standing:
    case Stance::RunningR1:
    case Stance::RunningL1:
    case Stance::RunningF1:
    player.stance = Stance::RunningB1;
    break;

    case Stance::RunningB1:
    player.stance = Stance::RunningB2;
    break;
    
    case Stance::RunningB2:
    case Stance::RunningF2:
    case Stance::RunningL2:
    case Stance::RunningR2:
    player.stance = Stance::RunningB1;
    break;
  }
  }
   if (arduboy.notPressed(UP_BUTTON) && ( player.stance == Stance::RunningB1 || player.stance == Stance::RunningB2 ) ) {
    player.stance = Stance::Standing; 
    }
  }
  
  
  if(arduboy.pressed(DOWN_BUTTON)) {
    if(PLAYER_Y_OFFSET + PLAYER_HEIGHT < mapy + TILE_SIZE * WORLD_HEIGHT) {
      mapy -= 1;
    }

    if (arduboy.pressed(DOWN_BUTTON) && (player.hasBall = true)) {
      ballx = PLAYER_X_OFFSET + 1;
      bally = PLAYER_Y_OFFSET + PLAYER_HEIGHT;
    }
    else {
      ballx = PLAYER_X_OFFSET + PLAYER_WIDTH + 40;
      bally = PLAYER_Y_OFFSET + PLAYER_HEIGHT / 2;
    }
    
     if (arduboy.everyXFrames(7)) {  
 switch(player.stance) {
    case Stance::Standing:
    case Stance::RunningB1:
    case Stance::RunningR1:
    case Stance::RunningL1:
    player.stance = Stance::RunningF1;
    break;

    case Stance::RunningF1:
    player.stance = Stance::RunningF2;
    break;
    
    case Stance::RunningF2:
    case Stance::RunningL2:
    case Stance::RunningR2:
    case Stance::RunningB2:
    player.stance = Stance::RunningF1;
    break;
  }
  }
   if (arduboy.notPressed(DOWN_BUTTON) && ( player.stance == Stance::RunningF1 || player.stance == Stance::RunningF2 ) ) {
    player.stance = Stance::Standing; 
    }
  }
  
  if(arduboy.pressed(LEFT_BUTTON)) {
    if(mapx < PLAYER_X_OFFSET) {
      mapx += 1;
    }

    if (arduboy.pressed(LEFT_BUTTON) && (player.hasBall = true)) {
      ballx = PLAYER_X_OFFSET - PLAYER_WIDTH + 2;
      bally = PLAYER_Y_OFFSET + PLAYER_HEIGHT / 2;
    }
   else {
      ballx = PLAYER_X_OFFSET + PLAYER_WIDTH + 40;
      bally = PLAYER_Y_OFFSET + PLAYER_HEIGHT / 2;
    }
    
   if (arduboy.everyXFrames(7)) {  
  switch(player.stance) {
    case Stance::Standing:
    case Stance::RunningR1:
    case Stance::RunningB1:
    case Stance::RunningF1:
    player.stance = Stance::RunningL1;
    break;

    case Stance::RunningL1:
    player.stance = Stance::RunningL2;
    break;
    
    case Stance::RunningF2:
    case Stance::RunningL2:
    case Stance::RunningR2:
    case Stance::RunningB2:
    player.stance = Stance::RunningL1;
    break;
  }
  }
   if (arduboy.notPressed(LEFT_BUTTON) && ( player.stance == Stance::RunningL1 || player.stance == Stance::RunningL2 ) ) {
    player.stance = Stance::Standing; 
    }
  }

  if(arduboy.pressed(RIGHT_BUTTON)) {
    if(PLAYER_X_OFFSET + PLAYER_WIDTH < mapx + TILE_SIZE * WORLD_WIDTH) {
      mapx -= 1;
    }

    if (arduboy.pressed(RIGHT_BUTTON) && (player.hasBall = true)) {
      ballx = PLAYER_X_OFFSET + PLAYER_WIDTH;
      bally = PLAYER_Y_OFFSET + PLAYER_HEIGHT / 2;
    }
    else {
      ballx = PLAYER_X_OFFSET + PLAYER_WIDTH + 40;
      bally = PLAYER_Y_OFFSET + PLAYER_HEIGHT / 2;
    }
    
  if (arduboy.everyXFrames(7)) {  
  switch(player.stance) {
    case Stance::Standing:
    case Stance::RunningL1:
    case Stance::RunningB1:
    case Stance::RunningF1:
    player.stance = Stance::RunningR1;
    break;

    case Stance::RunningR1:
    player.stance = Stance::RunningR2;
    break;
    
    case Stance::RunningF2:
    case Stance::RunningL2:
    case Stance::RunningR2:
    case Stance::RunningB2:
    player.stance = Stance::RunningR1;
    break;
  }
  }
   if (arduboy.notPressed(RIGHT_BUTTON) && ( player.stance == Stance::RunningR1 || player.stance == Stance::RunningR2 ) ) {
    player.stance = Stance::Standing; 
    }
  }

if (arduboy.justPressed(B_BUTTON) && (arduboy.pressed(DOWN_BUTTON))) {
  player.stance = Stance::Standing;
}
  
}



Then I tried placing the ball away from the player and when I moved my player the ball snapped back to the place it is supposed to go when the player has the ball.:slightly_frowning_face:


(Simon) #18

Think you did it again. == is the comparison and = is the assignment.

Also, could …

void contact () {
if (arduboy.collide(ballRect, playerRect))  {
  collision = true;
}
  
if (collision == true) {
  arduboy.setCursor(0, 0);
  arduboy.print("Collision");
  player.hasBall = true;
}
}

Be simplified to:

void contact () {
  if (arduboy.collide(ballRect, playerRect))  {
    arduboy.setCursor(0, 0);
    arduboy.print("Collision");
    player.hasBall = true;
  }
}

(JohnnydCoder) #19

As a wise programmer once said…

I got the collision working!
Now I have this problem that I want to work on now:

My goal is that the player can run into it, and take the ball.:grinning:


(Scott) #20

As with the alternative I gave in my previous reply, many (most?) people find code more readable when == true is left off when testing a boolean, provided the variables are named in a way that it makes sense, as yours have. This also helps eliminate the error of using = instead of ==.

if (collision)

instead of

if (collision == true)

and:

if (!collision)

instead of

if (collision == false)