Help with game development


(Holmes) #21

Wow! I am impressed with your code, for a beginner! Great job breaking things up into understandable functions. :slight_smile: It really helps people understand what is going on so they can help pinpoint where any bugs may be and contribute their ideas.


(JohnnydCoder) #22

Thanks @crait!

Thanks to your and @filmote’s tutorials, I’ve been able to code on the arduboy.:grinning:

I can’t wait for the next one!:wink:

I’ve also started learning the C++ language using a book!:closed_book:


(Pharap) #23

I recommend the tutorial on this website: http://www.learncpp.com/

Also, might I refer you to my resource collection?

Lots of useful links for C++, gamedev and general programming.


(JohnnydCoder) #24

Thanks for the resources @Pharap!

I’ll be sure to use them! :grinning:


(JohnnydCoder) #25

I was wondering what kind of AI my computerized opponent should use. Here are some options:

  • Make the opponent somehow go toward the player on a string-like pattern.
  • The opponent follows the same Y pattern as the player, but has a farther X pattern.
  • The opponent goes on a randomly generated pattern each game.
  • Other ideas?

0 voters

Please tell why you chose the option and how you would program it.

Thanks!


(Pharap) #26

It depends entirely on the type of game and how important the AI is.

In side-scrolling shooters you can get away with making ships fly in a pre-configured pattern.
In roguelikes you can make monsters wonder around aimlessly until they spot the player.

(I voted ‘other ideas’.)

In some games where more advanced AI is important, you need a proper state machine.


(JohnnydCoder) #27

So, the overall vote was for the opponent to attack the player like being on a string. I personally like that AI for the game as the best as well. :smile:

The problem is… I don’t know how I could program that. Could everyone offer their ways of programing that specific AI?

That would be great. :wink:


(Pharap) #28

Somewhere in the first 7 videos.
Best watch all 7 to be safe, they’re very educational:

(In case it’s not showing up properly: https://www.youtube.com/playlist?list=PLW3Zl3wyJwWOpdhYedlD-yCB7WQoHf-My.)

Here’s the first video:

(More useful things like this can be found on my resource collection.)


(JohnnydCoder) #29

Is this the video?

https://www.youtube.com/watch?list=PLW3Zl3wyJwWOpdhYedlD-yCB7WQoHf-My&time_continue=6&v=Rcbjmt35PDo


(Pharap) #30

That’s part of it.
The actual part focusing on enemies moving towards the player is this video:

(Again, in case it doesn’t show up: https://www.youtube.com/watch?v=WNaxtPTMqSo&index=2&list=PLW3Zl3wyJwWOpdhYedlD-yCB7WQoHf-My.)

But you should try to watch all 7 because they’re all about vectors and vectors are really useful.
Vectors are the first step to mastering movement.


(JohnnydCoder) #31

Thanks!

I can’t wait to start making the AI!


(JohnnydCoder) #32

I keep on getting an error that says, " ‘ballRect’ was not declared in this scope".:confused:

Here’s the code:

#pragma once
#include "player.h"

#define OPP_WIDTH    10
#define OPP_HEIGHT  16
#define OPP_X_OFFSET   WIDTH / 2 + OPP_WIDTH * 2
#define OPP_Y_OFFSET    HEIGHT / 2 - OPP_HEIGHT / 2

Rect oppRect = {
  OPP_X_OFFSET, OPP_Y_OFFSET, OPP_WIDTH, OPP_HEIGHT
};

enum oppStance {
  oppStanding,
  oppRunningR1,
  oppRunningR2,
  oppRunningL1,
  oppRunningL2,
  oppRunningF1,
  oppRunningF2,
  oppRunningB1,
  oppRunningB2,
};

struct oppVector {
  int x, y;
};

struct Opponent {
  int x;
  int y;
  oppStance stance;
  bool hasBall;
  char image;
};

Opponent opp = {OPP_X_OFFSET, OPP_Y_OFFSET, oppStance::oppStanding, false, opponentImages};

void oppAttack() {
  if (opp.hasBall == false) {
    
  oppVector oppV;

  oppV.x = (WIDTH / 2 - OPP_WIDTH / 2) - opp.x;
  oppV.y = (HEIGHT / 2 - OPP_HEIGHT / 2) - opp.y;

  opp.x = oppV.x;
  opp.y = oppV.y;

  if (arduboy.collide(ballRect, oppRect)) {
    opp.hasBall = true;
  }
  }
}


void drawopponent() {
opp.image = opponentImages[opp.stance];
arduboy.fillRect(OPP_X_OFFSET, OPP_Y_OFFSET, OPP_WIDTH, OPP_HEIGHT, BLACK);  
Sprites::drawExternalMask(opp.x, opp.y, opponentImages, opponentImages, opp.stance, opp.stance);
}

I include player.h that has ballRect in it, but it still doesn’t compile.

I appreciate your help.

FYI: This is the code for my AI!:grinning:


(Pharap) #33

Nobody will know unless we see player.h too.


(JohnnydCoder) #34

Sorry about that! :smile:

#pragma once

#include "images.h"
#include "opponent.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
#define BALL_RADIUS    4
#define BALL_SIZE    8

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

int playerScore {0};

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

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



enum Stance {
  Standing,
  RunningR1,
  RunningR2,
  RunningL1,
  RunningL2,
  RunningF1,
  RunningF2,
  RunningB1,
  RunningB2,
};

struct Player {
  int x;
  int y;
  Stance stance;
  bool hasBall;
  char image;
};

Player player = {PLAYER_X_OFFSET, PLAYER_Y_OFFSET, Stance::Standing, false, playerImages};


void drawball() {
  arduboy.fillCircle(ballx + 4, bally + 4, BALL_RADIUS, BLACK);
  arduboy.drawBitmap(ballx, bally, ball, BALL_SIZE, BALL_SIZE, WHITE);
}

void contact () {
  if (arduboy.collide(ballRect, playerRect))  {
    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)) {
      ballx = PLAYER_X_OFFSET + 6;
      bally = PLAYER_Y_OFFSET + 6;
    }
      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)) {
      ballx = PLAYER_X_OFFSET + 1;
      bally = PLAYER_Y_OFFSET + PLAYER_HEIGHT;
    }
    
     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)) {
      ballx = PLAYER_X_OFFSET - PLAYER_WIDTH + 2;
      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)) {
      ballx = PLAYER_X_OFFSET + PLAYER_WIDTH;
      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;
}
}

void scoreGoal() {

  arduboy.setCursor(120, 0);
  arduboy.print(playerScore);

  if (arduboy.justPressed(A_BUTTON) && (player.hasBall)) {
    ballx += 30;
    player.hasBall = false;
  }
  
  if (ballx + BALL_SIZE >= mapx + TILE_SIZE * WORLD_WIDTH) {
    playerScore += 1; 
    ballx = PLAYER_X_OFFSET + PLAYER_WIDTH - 2;
    bally = PLAYER_Y_OFFSET + PLAYER_HEIGHT / 2;
    mapx = 0;
    mapy = 0;
  }
}

void resetGame() {
  ballx = PLAYER_X_OFFSET + PLAYER_WIDTH - 2;
  bally = PLAYER_Y_OFFSET + PLAYER_HEIGHT / 2;
  playerScore = 0;
  mapx = 0;
  mapy = 0;
}

As you can see, I’ve added a few new things to the code.:slightly_smiling_face:


(Pharap) #35

Are you 100% sure the error came from your first file and not another file?
And are you sure that was the only error?

It’s hard to get a full picture of what’s going on from just this.


(JohnnydCoder) #36

Here’s the full error message:

In file included from sketch/player.h:8:0,
                 from /Users/davidbauer/Documents/Arduino/Soccer_Game/Soccer_Game.ino:15:
sketch/opponent.h: In function 'void oppAttack()':
opponent.h:50: error: 'ballRect' was not declared in this scope
   if (arduboy.collide(ballRect, oppRect)) {
                       ^
exit status 1
'ballRect' was not declared in this scope

It’s the only error I see.:confused:


(Simon) #37

You might need to post the entire project up here or on GitHub.


(JohnnydCoder) #38

All right.
Here is the entire code.

Main file:

#include <Arduboy2.h>
Arduboy2 arduboy;

#include "maps.h"
#include "player.h"
#include "opponent.h"

#define GAME_INTRO  0
#define GAME_TITLE  1
#define GAME_PLAY 2
#define GAME_OVER 3
#define GAME_HIGH 4
int gamestate = GAME_TITLE;

void titlescreen() {
  arduboy.setCursor(0, 0);
  arduboy.print("Title Screen\n");
  if(arduboy.justPressed(A_BUTTON)) {
    gamestate = GAME_PLAY;
  }
}

void gameplay() {
  arduboy.setCursor(0, 0);
  arduboy.print("Gameplay\n");

 contact(); 
 playerinput();
 drawworld();
 drawball(); 
 drawplayer();
 scoreGoal();
 drawopponent();

 if (player.hasBall) {
  oppAttack();
}


  if (playerScore == 5) {
    gamestate = GAME_HIGH;
  }
}

void gameover() {
  arduboy.setCursor(0, 0);
  arduboy.print("Game Over Screen\n");
  if(arduboy.justPressed(A_BUTTON)) {
    gamestate = GAME_HIGH;
  }
}

void highscore() {
  arduboy.setCursor(0, 0);
  arduboy.print("High Score Screen\n");

  if (gamestate == GAME_HIGH) {
    resetGame();
  }
  if(arduboy.justPressed(A_BUTTON)) {
    gamestate = GAME_TITLE;
  }
}

void gameloop() {
  switch(gamestate) {
    case GAME_TITLE:
      titlescreen();
      break;

    case GAME_PLAY:
      gameplay();
      break;

    case GAME_OVER:
      gameover();
      break;

    case GAME_HIGH:
      highscore();
      break;
  }
}



void setup() {
  arduboy.begin();
  arduboy.setFrameRate(45);
  arduboy.display();

  arduboy.initRandomSeed();
  
  arduboy.clear();
}

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

  arduboy.pollButtons();

  arduboy.clear();

  gameloop();

  arduboy.display();
}

Images file:


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,
  // RunningR1   
  0x78, 0x84, 0x02, 0x01, 0x01, 0x21, 0x49, 0x42, 0x84, 0x78, 0x00, 0x18, 0x85, 0x7e, 0x1e, 0x1e,
  0xfe, 0x85, 0x08, 0x04,
  // RunningR2   
  0x78, 0x84, 0x02, 0x01, 0x01, 0x21, 0x49, 0x42, 0x84, 0x78, 0x00, 0x00, 0x8d, 0x7e, 0xfe, 0xfe,
  0x9e, 0x0d, 0x10, 0x00, 
  // RunningL1
  0x78, 0x84, 0x42, 0x49, 0x21, 0x01, 0x01, 0x02, 0x84, 0x78, 0x04, 0x08, 0x85, 0xfe, 0x1e, 0x1e,
  0x7e, 0x85, 0x18, 0x00, 
  //RunningL2
  0x78, 0x84, 0x42, 0x49, 0x21, 0x01, 0x01, 0x02, 0x84, 0x78, 0x00, 0x10, 0x0d, 0x9e, 0xfe, 0xfe,
  0x7e, 0x8d, 0x00, 0x00, 
  //RunningF1
  0x78, 0x84, 0x02, 0x29, 0x41, 0x41, 0x29, 0x02, 0x84, 0x78, 0x00, 0x06, 0x05, 0xfe, 0x1e, 0x5e,
  0x3e, 0x05, 0x18, 0x00, 
  //RunningF2
  0x78, 0x84, 0x02, 0x29, 0x41, 0x41, 0x29, 0x02, 0x84, 0x78, 0x00, 0x18, 0x05, 0x3e, 0x5e, 0x1e,
  0xfe, 0x05, 0x06, 0x00,
  //RunningB1
  0x78, 0x84, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x84, 0x78, 0x00, 0x06, 0x05, 0xfe, 0x1e, 0x5e,
  0x3e, 0x05, 0x18, 0x00, 
  //RunningB2
  0x78, 0x84, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x84, 0x78, 0x00, 0x18, 0x05, 0x3e, 0x5e, 0x1e,
  0xfe, 0x05, 0x06, 0x00, 

};

const byte PROGMEM ball[] = {
  //Ball
  0x3c, 0x42, 0x89, 0x8d, 0x81, 0x81, 0x42, 0x3c, 
};



const char opponentImages[] PROGMEM =
{
  // Width, Height
  10, 16,
     
  // Standing
  0x78, 0xfc, 0xfe, 0xb7, 0x7f, 0x7f, 0xb7, 0xfe, 0xfc, 0x78, 0x00, 0x3c, 0x85, 0xff, 0x1f, 0x1f,
  0xff, 0x85, 0x3c, 0x00, 
  // RunningR1   
  0x78, 0xfc, 0xfe, 0xff, 0xff, 0xbf, 0x77, 0x7e, 0xfc, 0x78, 0x00, 0x18, 0x85, 0xff, 0x1f, 0x1f,
  0xff, 0x85, 0x08, 0x04, 
  // RunningR2   
  0x78, 0xfc, 0xfe, 0xff, 0xff, 0xbf, 0x77, 0x7e, 0xfc, 0x78, 0x00, 0x00, 0x99, 0x7f, 0xff, 0xff,
  0xff, 0x99, 0x00, 0x00, 
  // RunningL1
  0x78, 0xfc, 0x7e, 0x6f, 0xbf, 0xff, 0xff, 0xfe, 0xfc, 0x78, 0x04, 0x08, 0x85, 0xff, 0x1f, 0x1f,
  0x7f, 0x85, 0x18, 0x00, 
  //RunningL2
  0x78, 0xfc, 0x7e, 0x6f, 0xbf, 0xff, 0xff, 0xfe, 0xfc, 0x78, 0x00, 0x00, 0x8d, 0xff, 0xff, 0xff,
  0x7f, 0x8d, 0x00, 0x00, 
  //RunningF1
  0x78, 0xfc, 0xfe, 0xb7, 0x7f, 0x7f, 0xb7, 0xfe, 0xfc, 0x78, 0x00, 0x18, 0x05, 0xff, 0x1f, 0x5f,
  0x3f, 0x05, 0x06, 0x00, 
  //RunningF2
  0x78, 0xfc, 0xfe, 0xb7, 0x7f, 0x7f, 0xb7, 0xfe, 0xfc, 0x78, 0x00, 0x06, 0x05, 0x3f, 0x5f, 0x1f,
  0xff, 0x05, 0x18, 0x00, 
  //RunningB1
  0x78, 0xfc, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfc, 0x78, 0x00, 0x18, 0x05, 0xff, 0x1f, 0x5f,
  0x3f, 0x05, 0x06, 0x00, 
  //RunningB2
  0x78, 0xfc, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfc, 0x78, 0x00, 0x06, 0x05, 0x3f, 0x5f, 0x1f,
  0xff, 0x05, 0x18, 0x00, 
};
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 },
};

World file:


int mapx = 0;
int mapy = 0;

#define WORLD_HEIGHT    8
#define WORLD_WIDTH    14
#define GRASS    0
#define LEFT_SIDE_FIELD    1
#define T_FIELD    2
#define RIGHT_SIDE_OF_FIELD    3
#define B_FIELD    4
#define TL_CORNER    5
#define TR_CORNER    6
#define BL_CORNER    7
#define BR_CORNER    8

int world [WORLD_HEIGHT] [WORLD_WIDTH] = {
{TL_CORNER, T_FIELD, T_FIELD, T_FIELD, T_FIELD, T_FIELD, T_FIELD, T_FIELD, T_FIELD, T_FIELD, T_FIELD, T_FIELD, T_FIELD, TR_CORNER},
{LEFT_SIDE_FIELD, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, RIGHT_SIDE_OF_FIELD},
{LEFT_SIDE_FIELD, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, RIGHT_SIDE_OF_FIELD},
{LEFT_SIDE_FIELD, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, RIGHT_SIDE_OF_FIELD},
{LEFT_SIDE_FIELD, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, RIGHT_SIDE_OF_FIELD},
{LEFT_SIDE_FIELD, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, RIGHT_SIDE_OF_FIELD},
{LEFT_SIDE_FIELD, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, GRASS, RIGHT_SIDE_OF_FIELD},
{BL_CORNER, B_FIELD,  B_FIELD, B_FIELD, B_FIELD, B_FIELD,  B_FIELD, B_FIELD, B_FIELD, B_FIELD,  B_FIELD, B_FIELD, B_FIELD, BR_CORNER}
};

#define TILE_SIZE      16
void drawworld() {
  const int tileswide = WIDTH / TILE_SIZE + 1;
  const int tilestall = HEIGHT / TILE_SIZE + 1;

  for(int y = 0; y < tilestall; y++) {
    for(int x = 0; x < tileswide; x++) {
      const int tilex = x - mapx / TILE_SIZE;
      const int tiley = y - mapy / TILE_SIZE;
      if(tilex >= 0 && tiley >= 0 && tilex < WORLD_WIDTH && tiley < WORLD_HEIGHT) {
        arduboy.drawBitmap(x * TILE_SIZE + mapx % TILE_SIZE, y * TILE_SIZE + mapy % TILE_SIZE, tiles[world[tiley][tilex]], TILE_SIZE, TILE_SIZE, WHITE);
      }   
    }
  }
}

AI file:

#include "player.h"

#define OPP_WIDTH    10
#define OPP_HEIGHT  16
#define OPP_X_OFFSET   WIDTH / 2 + OPP_WIDTH * 2
#define OPP_Y_OFFSET    HEIGHT / 2 - OPP_HEIGHT / 2

Rect oppRect = {
  OPP_X_OFFSET, OPP_Y_OFFSET, OPP_WIDTH, OPP_HEIGHT
};

enum OppStance {
  oppStanding,
  oppRunningR1,
  oppRunningR2,
  oppRunningL1,
  oppRunningL2,
  oppRunningF1,
  oppRunningF2,
  oppRunningB1,
  oppRunningB2,
};

struct oppVector {
  int x, y;
};

struct Opponent {
  int x;
  int y;
  OppStance stance;
  bool hasBall;
  char image;
};

Opponent opp = {OPP_X_OFFSET, OPP_Y_OFFSET, OppStance::oppStanding, false, opponentImages};

void oppAttack() {
  if (opp.hasBall == false) {
    
  oppVector oppV;

  oppV.x = (WIDTH / 2 - OPP_WIDTH / 2) - opp.x;
  oppV.y = (HEIGHT / 2 - OPP_HEIGHT / 2) - opp.y;

  opp.x = oppV.x;
  opp.y = oppV.y;

  if (arduboy.collide(ballRect, oppRect)) {
    opp.hasBall = true;
  }
  }
}


void drawopponent() {
opp.image = opponentImages[opp.stance];
arduboy.fillRect(OPP_X_OFFSET, OPP_Y_OFFSET, OPP_WIDTH, OPP_HEIGHT, BLACK);  
Sprites::drawExternalMask(opp.x, opp.y, opponentImages, opponentImages, opp.stance, opp.stance);
}

And the player file:


#include "images.h"
#include "opponent.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
#define BALL_RADIUS    4
#define BALL_SIZE    8

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

int playerScore {0};

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

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



enum Stance {
  Standing,
  RunningR1,
  RunningR2,
  RunningL1,
  RunningL2,
  RunningF1,
  RunningF2,
  RunningB1,
  RunningB2,
};

struct Player {
  int x;
  int y;
  Stance stance;
  bool hasBall;
  char image;
};

Player player = {PLAYER_X_OFFSET, PLAYER_Y_OFFSET, Stance::Standing, false, playerImages};


void drawball() {
  arduboy.fillCircle(ballx + 4, bally + 4, BALL_RADIUS, BLACK);
  arduboy.drawBitmap(ballx, bally, ball, BALL_SIZE, BALL_SIZE, WHITE);
}

void contact () {
  if (arduboy.collide(ballRect, playerRect))  {
    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)) {
      ballx = PLAYER_X_OFFSET + 6;
      bally = PLAYER_Y_OFFSET + 6;
    }
      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)) {
      ballx = PLAYER_X_OFFSET + 1;
      bally = PLAYER_Y_OFFSET + PLAYER_HEIGHT;
    }
    
     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)) {
      ballx = PLAYER_X_OFFSET - PLAYER_WIDTH + 2;
      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)) {
      ballx = PLAYER_X_OFFSET + PLAYER_WIDTH;
      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;
}
}

void scoreGoal() {

  arduboy.setCursor(120, 0);
  arduboy.print(playerScore);

  if (arduboy.justPressed(A_BUTTON) && (player.hasBall)) {
    ballx += 30;
    player.hasBall = false;
  }
  
  if (ballx + BALL_SIZE >= mapx + TILE_SIZE * WORLD_WIDTH) {
    playerScore += 1; 
    ballx = PLAYER_X_OFFSET + PLAYER_WIDTH - 2;
    bally = PLAYER_Y_OFFSET + PLAYER_HEIGHT / 2;
    mapx = 0;
    mapy = 0;
  }
}

void resetGame() {
  ballx = PLAYER_X_OFFSET + PLAYER_WIDTH - 2;
  bally = PLAYER_Y_OFFSET + PLAYER_HEIGHT / 2;
  playerScore = 0;
  mapx = 0;
  mapy = 0;
}

I hope that’s enough.:grinning:


(Simon) #39

Are these the only files so far? Is there one for the opponent.h … the game doesn’t compile properly as opponent refers to player and player refers to opponent (assuming that what you called ‘AI’ is actually called opponent.h).

Just to check …

Images file: images.h
World file: maps.h
AI file:: opponent.h
Player file: player.h


(JohnnydCoder) #40

Yes, you are correct.

Correct! :slight_smile:

I’ll fix that immediately. :smile: