I am making a Map Editor using Allegro 5, and it works with tiles. However, the bitmaps in it are extremely buggy, and I don't really know why.
Here's my code:
#include <allegro5\allegro.h>
#include <allegro5\allegro_font.h>
#include <allegro5\allegro_image.h>
#include <allegro5\allegro_primitives.h>
#include <allegro5\allegro_native_dialog.h>
#include <stdio.h>
#include <vector>
//ENUMS DEFINITION SECTION
enum TYPE{LEFT,RIGHT,UP,DOWN};
//CLASS DEFINITION SECTION
class bmplist
{
public:
ALLEGRO_BITMAP *bmp;
TYPE type;
};
class T_center
{
public:
//place
int p;
int y;
//place bound y up, x left, y down, x right
int pbyu;
int pbxl;
int pbyd;
int pbxr;
ALLEGRO_BITMAP *img;
bmplist a;
};
class bmpdata
{
public:
bmplist bmp;
};
class bmphold
{
public:
ALLEGRO_BITMAP *bmp;
};
const int tileamount = 196;
T_center tile[tileamount];
int height = 700;
int width = 700;
ALLEGRO_DISPLAY *display;
ALLEGRO_EVENT ev;
ALLEGRO_EVENT_QUEUE *queue;
ALLEGRO_BITMAP *curr;
ALLEGRO_BITMAP *tilelr;
ALLEGRO_BITMAP *tileud;
ALLEGRO_BITMAP *tileltd;
ALLEGRO_BITMAP *tileltu;
ALLEGRO_BITMAP *tilertd;
ALLEGRO_BITMAP *tilertu;
ALLEGRO_BITMAP *pathup;
ALLEGRO_BITMAP *pathdown;
ALLEGRO_BITMAP *pathleft;
ALLEGRO_BITMAP *pathright;
ALLEGRO_FONT *actual;
bool isClicked;
bmplist bmps[2];
bool MAPMOUNTING = true;
bool MAPCOORDINATING = false;
bmplist arrows[4];
bmplist bmpindex[20];
bmpdata arrowindex[4];
char tilenow[15];
//FUNCTION DECLARATION SECTION
void func_start_system();
void func_start_tiles(int tilesize, T_center *tile, int tilearray, int xsize);
void func_start_variables();
void func_detect_mouse_move(T_center *tile, int tilearray, int bmpsize);
void mapper_init();
//THE TOOL ITSELF
int main()
{
al_init();
al_init_font_addon();
al_init_image_addon();
al_init_primitives_addon();
mapper_init();
}
//FUNCTION DEFINITION SECTION
void func_start_system()
{
al_install_keyboard();
al_install_mouse();
}
void func_start_tiles(int tilesize, T_center *tile, int tilearray, int xsize)
{
int xcount = 0;
int tile_x = tilesize;
int tile_y = tilesize;
int y_count = 1;
for (int i = 0; i < tilearray; i++)
{
if (xcount == 14)
{
xcount = 0;
tile_y = tile[i-1].pbyd + tilesize;
tile_x = tilesize;
}
tile[i].p = tile_x, tile[i].y = tile_y, tile[i].pbxl = tile_x - tilesize, tile[i].pbxr = tile_x + tilesize, tile[i].pbyu = tile_y - tilesize, tile[i].pbyd = tile_y + tilesize;
tile_x = tile[i].pbxr + tilesize;
printf("%i, %i, left: %i\n", tile[i].p, tile[i].y, tile[i].pbxl);
xcount++;
}
}
void func_start_variables()
{
display = al_create_display(width, height);
tilelr = al_load_bitmap("tilelr.png");
tileud = al_load_bitmap("tileud.png");
tileltu = al_load_bitmap("tireltu.png"), tileltd = al_load_bitmap("tireltd.png"), tilertu = al_load_bitmap("tirertu.png"), tilertd = al_load_bitmap("tirertd.png");
pathup = al_load_bitmap("up.png"), pathdown = al_load_bitmap("down.png"), pathleft = al_load_bitmap("left.png"), pathright = al_load_bitmap("right.png");
arrows[1].bmp = al_load_bitmap("up.png"), arrows[1].type = UP, arrows[2].bmp = al_load_bitmap("down.png"), arrows[2].type = DOWN;
arrows[3].bmp = al_load_bitmap("left.png"), arrows[3].type = LEFT, arrows[4].bmp = al_load_bitmap("right.png"), arrows[4].type = RIGHT;
bmpindex[1].bmp = tilelr, bmpindex[2].bmp = tileud, bmpindex[3].bmp = tileltu, bmpindex[4].bmp = tileltd, bmpindex[5].bmp = tilertu, bmpindex[6].bmp = tilertd;
bmpindex[7].bmp = pathup, bmpindex[7].type = UP, bmpindex[8].bmp = pathdown, bmpindex[8].type = DOWN;
bmpindex[9].bmp = pathleft, bmpindex[9].type = LEFT, bmpindex[10].bmp = pathright, bmpindex[10].type = RIGHT;
curr = bmpindex[1].bmp;
actual = al_load_font("app850.ttf", 12, 0);
queue = al_create_event_queue();
al_register_event_source(queue, al_get_mouse_event_source());
al_register_event_source(queue, al_get_keyboard_event_source());
al_register_event_source(queue, al_get_display_event_source(display));
}
void func_detect_mouse_move(T_center *tile, int tilearray, int bmpsize)
{
int index = 1;
int a = true;
while (a)
{
printf("%i", index);
al_clear_to_color(al_map_rgb(0, 0, 0));
for (int i = 0; i < tilearray; i++)
{
if (tile[i].img != NULL)
al_draw_bitmap(tile[i].img, tile[i].pbxl, tile[i].pbyu, 0);
if (tile[i].a.bmp != NULL)
al_draw_bitmap(tile[i].a.bmp, tile[i].pbxl, tile[i].pbyu, 0);
}
al_wait_for_event(queue, &ev);
{
if (ALLEGRO_EVENT_MOUSE_AXES)
{
for (int i = 0; i < tilearray; i++)
if (ev.mouse.x >= tile[i].pbxl && ev.mouse.x < tile[i].pbxr && ev.mouse.y >= tile[i].pbyu && ev.mouse.y < tile[i].pbyd)
{
al_draw_tinted_bitmap(bmpindex[index].bmp, al_map_rgb(200, 200, 200), tile[i].pbxl, tile[i].pbyu, 0);
al_flip_display();
}
}
if (ALLEGRO_EVENT_MOUSE_BUTTON_DOWN)
{
if (ev.mouse.button & 1)
for (int i = 0; i < tilearray; i++)
{
if (ev.mouse.x >= tile[i].pbxl && ev.mouse.x < tile[i].pbxr && ev.mouse.y >= tile[i].pbyu && ev.mouse.y < tile[i].pbyd)
{
if (bmpindex[index].type == NULL)
{
tile[i].img = curr;
al_flip_display();
}
if (bmpindex[index].type != NULL)
{
tile[i].a.bmp = bmpindex[index].bmp;
tile[i].a.type = bmpindex[index].type;
}
}
}
if (ev.mouse.button & 2)
for (int i = 0; i < tilearray; i++)
{
if (ev.mouse.x >= tile[i].pbxl && ev.mouse.x < tile[i].pbxr && ev.mouse.y >= tile[i].pbyu && ev.mouse.y < tile[i].pbyd)
{
tile[i].img = NULL;
al_flip_display();
}
}
if (ALLEGRO_EVENT_KEY_DOWN)
{
if (ev.keyboard.keycode == ALLEGRO_KEY_1)
index = 1;
if (ev.keyboard.keycode == ALLEGRO_KEY_2)
index = 2;
if (ev.keyboard.keycode == ALLEGRO_KEY_3)
index = 3;
if (ev.keyboard.keycode == ALLEGRO_KEY_4)
index = 4;
if (ev.keyboard.keycode == ALLEGRO_KEY_5)
index = 5;
if (ev.keyboard.keycode == ALLEGRO_KEY_6)
index = 6;
if (ev.keyboard.keycode == ALLEGRO_KEY_UP)
index = 7;
if (ev.keyboard.keycode == ALLEGRO_KEY_DOWN)
index = 8;
if (ev.keyboard.keycode == ALLEGRO_KEY_LEFT)
index = 9;
if (ev.keyboard.keycode == ALLEGRO_KEY_RIGHT)
index = 10;
al_flush_event_queue(queue);
}
curr = bmpindex[index].bmp;
}
}
}
}
void mapper_init()
{
func_start_system();
func_start_variables();
func_start_tiles(25, tile, tileamount, tileamount);
al_flip_display();
func_detect_mouse_move(tile, tileamount, 10);
}
The problem with this map editor is that sometimes, just by moving the mouse, the index, which is an integer that selects a bitmap in the bmpindex array, bugs out and a completely random bitmap appears in the mouse's position. For example, I am placing some bitmaps on the screen with a number 2 index, but then all of a sudden the index number becomes 4. Also, whenever I press a key in the keyboard, the actual selected bitmap is drawn in a completely random part of the screen. Why is this bug happening? How can I fix it?
I'd say it's because you have a small typo there. Those conditions, i.e.:
if (ALLEGRO_EVENT_MOUSE_AXES)
if (ALLEGRO_EVENT_MOUSE_BUTTON_DOWN)
if (ALLEGRO_EVENT_KEY_DOWN)
Will always evaluate as TRUE and therefore the statements will always run. This way you'll end up in situations you described that does not seem make sense (at least on the first sight) but that actually are quite logical
Like reading ev.mouse.x even though mouse did not move at all (but that did not matter because your condition if (ALLEGRO_EVENT_MOUSE_AXES) is always true) and contents of ev do not match mouse union part because it could have been the keyboard that caused the event, so the resulting coordinates are nonsensical (because there were none in that part of the memory)
Solution is actually quite simple - you just need to check type of ev for that value in condition, i.e. replace:
if (ALLEGRO_EVENT_MOUSE_AXES)
With:
if (ev.type == ALLEGRO_EVENT_MOUSE_AXES)
And so on.
Related
As a school project, I've made the classic snake game using SDL2 and C++.
I've already implemented the growing, moving features for the Snake but it was required to make the movement based on a grid, but when I implemented the grid feature, the self-collision was always triggering whenever grow one part, so every time I start the game, and eat the first fruit, the snake dies.
I've been trying for a while now, from placing a delay to the adding of the tail and delaying the collision check, but to no avail, it's always colliding with itself even though it is not.
I can't see what is wrong with the self collision, I would gladly appreciate it if someone can point out what's wrong.
Snake.h
#include "GameObject.h"
#include "common.h"
#include "draw.h"
#include "Food.h"
#include "util.h"
#include <vector>
struct Segment {
int x;
int y;
Segment(int posx, int posy) {
x = posx;
y = posy;
}
};
class Snake :
public GameObject
{
public:
~Snake();
void start();
void update();
void draw();
void outOfBoundsCheck();
void move();
void addSegment(int x, int y);
void selfCollisionCheck(bool hasEaten);
void setHasMoved(bool a);
void setIsAlive(bool a);
int getX();
int getY();
int getWidth();
int getHeight();
bool getIsAlive();
bool getHasMoved();
std::vector<Segment*> const& getV() const;
private:
std::vector<Segment*> body;
SDL_Texture* headTexture;
SDL_Texture* bodyTexture;
int x;
int y;
int width;
int height;
int dx;
int dy;
int tempX;
int tempY;
bool isAlive;
bool hasMoved;
};
Snake.cpp
Snake::~Snake()
{
}
void Snake::start()
{
// Load Texture
headTexture = loadTexture("gfx/player.png");
bodyTexture = loadTexture("gfx/body.png");
tempX = 0;
tempY = 0;
x = 0;
y = 0;
dx = 0;
dy = 0;
isAlive = true;
hasMoved = false;
width = 0;
height = 0;
SDL_QueryTexture(headTexture, NULL, NULL, &width, &height);
addSegment(x, y);
}
void Snake::update()
{
std::cout << "Head" << body[0]->x << std::endl;
if (body.size() > 1) {
std::cout << "2nd Segment" << body[1]->x << std::endl;
if (body.size() > 2) {
std::cout << "3nd Segment" << body[2]->x << std::endl;
}
}
move();
outOfBoundsCheck();
}
void Snake::draw()
{
if (!isAlive) return; // Cancel the render if player dies
for (int i = 0; i < body.size(); i++) {
blit(headTexture, body[i]->x, body[i]->y);
}
}
void Snake::outOfBoundsCheck()
{
for (int i = 0; i < body.size(); i++) {
if (body[i]->x > SCREEN_WIDTH) {
body[i]->x = 0;
}
if (body[i]->x < 0) {
body[i]->x = SCREEN_WIDTH;
}
if (body[i]->y > SCREEN_HEIGHT) {
body[i]->y = 0;
}
if (body[i]->y < 0) {
body[i]->y = SCREEN_HEIGHT;
}
}
}
void Snake::move()
{
if (app.keyboard[SDL_SCANCODE_W] && dy != 5) {
dx = 0;
dy = -5;
}
if (app.keyboard[SDL_SCANCODE_A] && dx != 5) {
dx = -5;
dy = 0;
}
if (app.keyboard[SDL_SCANCODE_S] && dy != -5) {
dx = 0;
dy = 5;
}
if (app.keyboard[SDL_SCANCODE_D] && dx != -5) {
dx = 5;
dy = 0;
}
Segment* snakeHead = *(body.begin()); //Grid
tempX += dx;
tempY += dy;
if (tempX % 25 == 0) {
snakeHead->x += tempX;
tempX = 0;
}
if (tempY % 25 == 0) {
snakeHead->y += tempY;
tempY = 0;
}
for (int i = body.size() - 1; i > 0; i--) { //For the other parts to follow
body[i]->x = body[i - 1]->x;
body[i]->y = body[i - 1]->y;
}
}
void Snake::addSegment(int x, int y)
{
Segment* seg = new Segment(x, y );
body.push_back(seg);
}
void Snake::selfCollisionCheck(bool hasEaten) // Fail
{
Segment* head = body[0];
if (hasEaten == false) {
for (int i = 1; i < body.size(); i++) {
if (head->x == body[i]->x && head->y == body[i]->y) {
isAlive = false;
break;
}
}
}
else {
return;
}
}
void Snake::setHasMoved(bool a)
{
hasMoved = a;
}
void Snake::setIsAlive(bool a)
{
isAlive = a;
}
int Snake::getX()
{
return x;
}
int Snake::getY()
{
return y;
}
int Snake::getWidth()
{
return width;
}
int Snake::getHeight()
{
return height;
}
bool Snake::getIsAlive()
{
return isAlive;
}
bool Snake::getHasMoved()
{
return hasMoved;
}
std::vector<Segment*> const& Snake::getV() const
{
// TODO: insert return statement here
return body;
}
GameScene.h
#include "Scene.h"
#include "GameObject.h"
#include "Snake.h"
#include "Food.h"
#include "util.h"
#include "text.h"
#include "SoundManager.h"
class GameScene : public Scene
{
public:
GameScene();
~GameScene();
void start();
void draw();
void update();
void spawnFood();
void collisionLogic();
void selfCollision();
void despawnFood(Food* food);
private:
Snake* snake;
Food* food;
int points;
std::vector<Food*> spawnedFood;
};
GameScene.cpp
#include "GameScene.h"
GameScene::GameScene()
{
// Register and add game objects on constructor
snake = new Snake();
this->addGameObject(snake);
points = 0;
}
GameScene::~GameScene()
{
delete snake;
delete food;
}
void GameScene::start()
{
Scene::start();
// Initialize any scene logic here
initFonts();
spawnFood();
}
void GameScene::draw()
{
Scene::draw();
drawText(110, 20, 255, 255, 255, TEXT_CENTER, "POINTS: %03d", points);
if (snake->getIsAlive() == false) {
drawText(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, 255, 255, 255, TEXT_CENTER, "GAME OVER!");
}
}
void GameScene::update()
{
Scene::update();
if (spawnedFood.size() == 0 && spawnedFood.size() != 1) {
spawnFood();
}
collisionLogic();
selfCollision();
}
void GameScene::spawnFood()
{
int random = rand() % 720;
if (random % 25 != 0) {
random = rand() % 720;
}
else {
Food* food = new Food();
this->addGameObject(food);
food->setPosition(rand() % SCREEN_WIDTH, rand() % SCREEN_HEIGHT);
spawnedFood.push_back(food);
}
}
void GameScene::collisionLogic()
{
Segment* head = snake->getV()[0];
std::vector<Segment*> snakeBody = snake->getV();
for (int i = 0; i < objects.size(); i++) {
Food* food = dynamic_cast<Food*>(objects[i]);
if (food != NULL) {
int collision = checkCollision(
head->x, head->y, snake->getWidth(), snake->getHeight(),
food->getX(), food->getY(), food->getWidth(), food->getHeight()
);
if (collision == 1) {
despawnFood(food);
snake->addSegment(snakeBody[snakeBody.size() - 1]->x, snakeBody[snakeBody.size() - 1]->y); //Adds a part to the snake
points++;
break;
}
}
}
}
void GameScene::selfCollision()
{
std::vector<Segment*> body = snake->getV();
Segment* head = snake->getV()[0];
for (int i = 1; i < snake->getV().size(); i++) {
if (head->x == body[i]->x && head->y == body[i]->y) {
snake->setIsAlive(false);
break;
}
}
}
void GameScene::despawnFood(Food* food)
{
int index = -1;
for (int i = 0; i < spawnedFood.size(); i++) {
if (food == spawnedFood[i]) {
index = i;
break;
}
}
if (index != -1) {
spawnedFood.erase(spawnedFood.begin() + index);
delete food;
}
}
It seems that I had some logical errors when it comes to the grid movement because when I re-coded everything and changed the grid movement into cell based instead of using modulo condition by dividing the screen width and height to the pixel size of my snake and using that as the coordinates for my movement, everything went back to normal and the collision bug disappeared.
Instead of doing this for the grid movement
Old Grid Movement Code
tempX += dx;
tempY += dy;
if (tempX % 25 == 0) {
snakeHead->x += tempX;
tempX = 0;
}
if (tempY % 25 == 0) {
snakeHead->y += tempY;
tempY = 0;
}
I defined this as a permanent value in my defs.h
defs.h
#define CELL_SIZE 25 // Size of the segment
#define CELL_WIDTH SCREEN_WIDTH / CELL_SIZE
#define CELL_HEIGHT SCREEN_HEIGHT / CELL_SIZE
After that, since I'm still going to render the picture with the default resolution, I multiplied CELL_SIZE to the dest variable of my blit function
draw.cpp
void blit(SDL_Texture* texture, int x, int y)
{
SDL_Rect dest;
dest.x = x * CELL_SIZE;
dest.y = y * CELL_SIZE;
SDL_QueryTexture(texture, NULL, NULL, &dest.w, &dest.h);
SDL_RenderCopy(app.renderer, texture, NULL, &dest);
}
This results to the snake and any other thing that I'm going to render to follow a grid and by assigning the x and y values with the CELL_WIDTH and CELL_HEIGHT as substitution to the resolution, I accomplished the grid movement with no conflict with my collision check.
I'm trying to make a simple menu with 3 different options. Play, Options, Exit.
I want them to change colors when you hover over them and when you mouse up on them. I tried using lazy foo's reference for mouse events, but I'm confused on how to do it with different "buttons". It would be one thing if they were saying all the same thing.
Here is my code:
#include "Menu.h"
#include <iostream>
std::string tmpCap;
SDL_Color tmpColor;
SDL_Color colorW = { 255, 255, 255, 255 };
SDL_Color colorG1 = { 65, 65, 65, 255 };
SDL_Color colorG2 = { 85, 85, 85, 255 };
SDL_Color colorG3 = { 125, 125, 125, 255 };
SDL_Texture* currentPlaySet[3];
SDL_Texture* currentOptionsSet[3];
SDL_Texture* currentExitSet[3];
SDL_Texture* currentPlay;
SDL_Texture* currentOptions;
SDL_Texture* currentExit;
SDL_Texture* currentTexboo[3];
enum mMenuNum
{
PLAY_MOUT = 0,
PLAY_MON = 1,
PLAY_MUP = 2,
OPTIONS_MOUT = 3,
OPTIONS_MON = 4,
OPTIONS_MUP = 5,
EXIT_MOUT = 6,
EXIT_MON = 7,
EXIT_MUP = 8,
MENUNUM_TOTAL = 9
};
enum mCheckButton
{
B_OUT = 0,
B_ON = 1,
B_UP = 2,
B_TOTAL = 3
};
int xCenter = 1024 / 2;
int yCenter = 768 / 2;
SDL_Rect rtmp[3];
SDL_Texture* btmp[9];
class Button
{
public:
//Initializes internal variables
Button();
//Sets top left position
void setPosition( int x, int y );
void setSize(int x, int y, int w, int h);
//Handles mouse event
void handleEvent(SDL_Event* e);
//Shows button sprite
void renderButtons();
private:
//Top left position
SDL_Point mPosition;
SDL_Rect mSize;
//Currently used global sprite
mCheckButton mCurrentSprite;
};
void Menu::init()
{
const char* buttonCaptions[3] = {
" Play ", " Options ", " Exit "
};
for (int i = 0; i < 9; i++)
{
if(i < 3)
{
tmpCap = buttonCaptions[0];
}
if(i < 6 && i > 2)
{
tmpCap = buttonCaptions[1];
}
if(i < 9 && i > 5)
{
tmpCap = buttonCaptions[2];
}
if(i == 0 || i == 3 || i == 6)
{
tmpColor = colorG1;
}
if(i == 1 || i == 4 || i == 7)
{
tmpColor = colorG2;
}
if(i == 2 || i == 5 || i == 8)
{
tmpColor = colorG3;
}
btmp[i] = TextureManager::TextTexture("../src/assets/Arial Black.ttf", tmpCap, 40, tmpColor);
//std::cout << tmpCap << " " << i << std::endl;
}
currentPlaySet[0] = btmp[0];
currentOptionsSet[0] = btmp[3];
currentExitSet[0] = btmp[6];
currentPlaySet[1] = btmp[1];
currentOptionsSet[1] = btmp[4];
currentExitSet[1] = btmp[7];
currentPlaySet[2] = btmp[2];
currentOptionsSet[2] = btmp[5];
currentExitSet[2] = btmp[8];
Menu::getSize();
}
Button::Button()
{
currentPlay = btmp[0];
currentOptions = btmp[3];
currentExit = btmp[6];
mCurrentSprite = B_OUT;
}
Button f[9];
void Button::setPosition(int x, int y)
{
mPosition.x = x;
mPosition.y = y;
}
void Button::setSize(int x, int y, int w, int h)
{
mSize.x = x;
mSize.y = y;
mSize.w = w;
mSize.h = h;
}
void Menu::getSize()
{
SDL_QueryTexture(btmp[0], NULL, NULL, &rtmp[0].w, &rtmp[0].h);
int aX = xCenter - (rtmp[0].w / 2);
int aY = (yCenter - (rtmp[0].h / 2)) - 100;
int aW = rtmp[0].w;
int aH = rtmp[0].h;
f[0].setPosition(aX, aY);
f[0].setSize(aX, aY, aW, aH);
SDL_QueryTexture(btmp[3], NULL, NULL, &rtmp[1].w, &rtmp[1].h);
int bX = xCenter - (rtmp[1].w / 2);
int bY = (yCenter - (rtmp[1].h / 2));
int bW = rtmp[1].w;
int bH = rtmp[1].h;
f[1].setPosition(bX, bY);
f[1].setSize(bX, bY, bW, bH);
SDL_QueryTexture(btmp[6], NULL, NULL, &rtmp[2].w, &rtmp[2].h);
int cX = xCenter - (rtmp[2].w / 2);
int cY = (yCenter - (rtmp[2].h / 2)) + 100;
int cW = rtmp[2].w;
int cH = rtmp[2].h;
f[2].setPosition(cX, cY);
f[2].setSize(cX, cY, cW, cH);
}
void Button::handleEvent(SDL_Event* e)
{
if( e->type == SDL_MOUSEMOTION || e->type == SDL_MOUSEBUTTONDOWN || e->type == SDL_MOUSEBUTTONUP )
{
int x, y;
SDL_GetMouseState(&x, &y);
//Check if mouse is in button
bool inside = true;
//Mouse is left of the button
if( x < mPosition.x )
{
inside = false;
}
//Mouse is right of the button
else if( x > mPosition.x + mSize.w )
{
inside = false;
}
//Mouse above the button
else if( y < mPosition.y )
{
inside = false;
}
//Mouse below the button
else if( y > mPosition.y + mSize.h )
{
inside = false;
}
//Mouse is outside button
if( !inside )
{
std::cout << "Outside " << " " << mCurrentSprite << std::endl;
//currentPlay = currentPlaySet[mCurrentSprite];
//currentOptions = currentOptionsSet[mCurrentSprite];
//currentExit = currentExitSet[mCurrentSprite];
mCurrentSprite = B_OUT;
}
//Mouse is inside button
else
{
switch( Game::event.type )
{
case SDL_MOUSEMOTION:
mCurrentSprite = B_ON;
//currentPlay = btmp[(mCurrentSprite/3)+1];
//currentOptions = btmp[(mCurrentSprite/3)+1];
//currentExit = btmp[(mCurrentSprite/3)+1];
//currentPlay = currentPlaySet[mCurrentSprite];
//currentOptions = currentOptionsSet[mCurrentSprite];
//currentExit = currentExitSet[mCurrentSprite];
std::cout << "In " << mCurrentSprite << std::endl;
break;
case SDL_MOUSEBUTTONDOWN:
//currentPlay = currentPlaySet[mCurrentSprite];
//currentOptions = currentOptionsSet[mCurrentSprite];
//currentExit = currentExitSet[mCurrentSprite];
//mCurrentSprite = B_DOWN;
//currentTex = mTex3;
break;
case SDL_MOUSEBUTTONUP:
//currentPlay = currentPlaySet[mCurrentSprite];
//currentOptions = currentOptionsSet[mCurrentSprite];
//currentExit = currentExitSet[mCurrentSprite];
mCurrentSprite = B_UP;
//currentTex = mTex4;
break;
}
}
}
}
void Button::renderButtons()
{
while( SDL_PollEvent( &Game::event ) != 0 )
{
for( int i = 0; i < 3; ++i )
{
f[i].handleEvent(&Game::event);
//isMouseOverText();
}
}
SDL_RenderCopy(Game::mRenderer, btmp[mCurrentSprite], nullptr, &mSize);
//SDL_RenderCopy(Game::mRenderer, currentOptionsSet[mCurrentSprite], nullptr, &mSize);
//SDL_RenderCopy(Game::mRenderer, currentExitSet[mCurrentSprite], nullptr, &mSize);
}
void Menu::render()
{
for( int i = 0; i < 3; ++i )
{
f[i].renderButtons();
}
}
I was able to figure it out using an old SDL tutorial code from here.
Is it viable to delete and create these textures on interact?
I've updated my code to avoid deleting textures every time at the bottom. Is there a better way to do this? It seems clean to me but I'm concerned about resource usage.
Any insight is appreciated.
My code:
const int NUMMENU = 2;
const char* labels[NUMMENU] = {"Continue","Exit"};
SDL_Texture* menus[NUMMENU];
bool selected[NUMMENU] = {0,0};
SDL_Color color[2] = {{0,0,0,255},{255,0,0,255}};
SDL_Rect pos[NUMMENU];
int x,y;
const char* font = "../assets/Arial Black.ttf";
void MainMenu::init()
{
menus[0] = TextureManager::CreateFontTexture(font, labels[0], 40, color[0]);
menus[1] = TextureManager::CreateFontTexture(font, labels[1], 40, color[0]);
SDL_QueryTexture(menus[0], NULL, NULL, &pos[0].w, &pos[0].h);
SDL_QueryTexture(menus[1], NULL, NULL, &pos[1].w, &pos[1].h);
pos[0].x = (1024/2) - (pos[0].w / 2);
pos[0].y = (768/2) - (pos[0].h / 2) - 100;
pos[1].x = (1024/2) - (pos[1].w / 2);
pos[1].y = (768/2) - (pos[1].h / 2);
}
void MainMenu::render()
{
SDL_RenderCopy(App::appRenderer, menus[0], NULL, &pos[0]);
SDL_RenderCopy(App::appRenderer, menus[1], NULL, &pos[1]);
if( App::event.type == SDL_MOUSEMOTION || App::event.type == SDL_MOUSEBUTTONDOWN || App::event.type == SDL_MOUSEBUTTONUP )
{
switch(App::event.type)
{
case SDL_MOUSEMOTION:
x = App::event.motion.x;
y = App::event.motion.y;
for(int i = 0; i < NUMMENU; i += 1) {
if(x>=pos[i].x && x<=pos[i].x+pos[i].w && y>=pos[i].y && y<=pos[i].y+pos[i].h)
{
if(!selected[i])
{
selected[i] = 1;
SDL_DestroyTexture(menus[i]);
menus[i] = TextureManager::CreateFontTexture(font, labels[i], 40, color[1]);
}
}
else
{
if(selected[i])
{
selected[i] = 0;
SDL_DestroyTexture(menus[i]);
menus[i] = TextureManager::CreateFontTexture(font, labels[i], 40, color[0]);
}
}
}
break;
case SDL_MOUSEBUTTONDOWN:
x = App::event.button.x;
y = App::event.button.y;
for(int i = 0; i < NUMMENU; i += 1) {
if(x>=pos[i].x && x<=pos[i].x+pos[i].w && y>=pos[i].y && y<=pos[i].y+pos[i].h)
{
SDL_DestroyTexture(menus[0]);
SDL_DestroyTexture(menus[1]);
}
}
break;
}
}
}
UPDATED code without deleting textures every time:
const int NUMMENU = 3;
const char* labels[NUMMENU] = {"New Game", "Options","Exit"};
SDL_Texture* menus[NUMMENU];
SDL_Texture* selectedMenus[NUMMENU];
SDL_Texture* toRender[NUMMENU];
bool selected[NUMMENU] = {0,0};
SDL_Color color[2] = {{0,0,0,255},{125,125,125,255}};
SDL_Rect pos[NUMMENU];
int x,y;
const char* font = "../assets/Arial Black.ttf";
int aOne = 0;
void MainMenu::init()
{
for(int i = 0; i < NUMMENU; i++)
{
menus[i] = TextureManager::CreateFontTexture(font, labels[i], 40, color[0]);
toRender[i] = menus[i];
selectedMenus[i] = TextureManager::CreateFontTexture(font, labels[i], 40, color[1]);
SDL_QueryTexture(menus[i], NULL, NULL, &pos[i].w, &pos[i].h);
}
pos[0].x = (1024/2) - (pos[0].w / 2);
pos[0].y = (768/2) - (pos[0].h / 2) - 100;
pos[1].x = (1024/2) - (pos[1].w / 2);
pos[1].y = (768/2) - (pos[1].h / 2);
pos[2].x = (1024/2) - (pos[2].w / 2);
pos[2].y = (768/2) - (pos[2].h / 2) + 100;
}
void MainMenu::inputD()
{
aOne++;
std::cout << aOne << std::endl;
switch(App::event.type)
{
case SDL_MOUSEMOTION:
x = App::event.motion.x;
y = App::event.motion.y;
for(int i = 0; i < NUMMENU; i += 1)
{
if(x>=pos[i].x && x<=pos[i].x+pos[i].w && y>=pos[i].y && y<=pos[i].y+pos[i].h)
{
if(!selected[i])
{
selected[i] = 1;
toRender[i] = selectedMenus[i];
}
}
else
{
if(selected[i])
{
selected[i] = 0;
toRender[i] = menus[i];
}
}
}
break;
case SDL_MOUSEBUTTONDOWN:
x = App::event.button.x;
y = App::event.button.y;
for(int i = 0; i < NUMMENU; i += 1)
{
if(x>=pos[i].x && x<=pos[i].x+pos[i].w && y>=pos[i].y && y<=pos[i].y+pos[i].h)
{
std::cout << labels[i] << " button clicked" << std::endl;
}
}
break;
}
}
void MainMenu::render()
{
for(int i = 0; i < NUMMENU; i++)
SDL_RenderCopy(App::appRenderer, toRender[i], NULL, &pos[i]);
}
I'm wondering if anyone can point me in the right direction for this error.
I am learning 2D game programming using Allegro 5 installed in Visual Studio 2010.
I have been following a tutorial series and everything has been fine until the last lesson.
The final code will build successfully. The executable will load but will crash out when I hit the space bar to fire a bullet (it's a side shooter game). I get the following error:
"Assertion failed: spl, file allegro-git\addons\audio\kgm_sample.c line 321"
It obviously has something to do with the bullet sample sound linked in with the key press.
If I comment out the line: al_play_sample(shot, 1, 0, 1, ALLEGRO_PLAYMODE_ONCE, 0); in the FireBullet() function, then the game will play fine (without the bullet sound).
I have searched everywhere and cannot find a solution.
Here is the complete code for the game:
#include <allegro5\allegro.h>
#include <allegro5\allegro_primitives.h>
#include <allegro5\allegro_font.h>
#include <allegro5\allegro_ttf.h>
#include <allegro5\allegro_image.h>
#include <allegro5\allegro_audio.h>
#include <allegro5\allegro_acodec.h>
#include "objects.h"
//GLOBALS==============================
const int WIDTH = 800;
const int HEIGHT = 400;
const int NUM_BULLETS = 5;
const int NUM_COMETS = 10;
const int NUM_EXPLOSIONS = 5;
enum STATE{TITLE, PLAYING, LOST};
enum KEYS{UP, DOWN, LEFT, RIGHT, SPACE};
bool keys[5] = {false, false, false, false, false};
SpaceShip ship;
Bullet bullets[NUM_BULLETS];
Comet comets[NUM_COMETS];
Explosion explosions[NUM_EXPLOSIONS];
ALLEGRO_SAMPLE *shot = NULL;
ALLEGRO_SAMPLE *boom = NULL;
ALLEGRO_SAMPLE *song = NULL;
ALLEGRO_SAMPLE_INSTANCE *songInstance = NULL;
//prototypes
void InitShip(SpaceShip &ship, ALLEGRO_BITMAP *image);
void ResetShipAnimation(SpaceShip &ship, int position);
void DrawShip(SpaceShip &ship);
void MoveShipUp(SpaceShip &ship);
void MoveShipDown(SpaceShip &ship);
void MoveShipLeft(SpaceShip &ship);
void MoveShipRight(SpaceShip &ship);
void InitBullet(Bullet bullet[], int size);
void DrawBullet(Bullet bullet[], int size);
void FireBullet(Bullet bullet[], int size, SpaceShip &ship);
void UpdateBullet(Bullet bullet[], int size);
void CollideBullet(Bullet bullet[], int bSize, Comet comets[], int cSize, SpaceShip &ship, Explosion explosions[], int eSize);
void InitComet(Comet comets[], int size, ALLEGRO_BITMAP *image);
void DrawComet(Comet comets[], int size);
void StartComet(Comet comets[], int size);
void UpdateComet(Comet comets[], int size);
void CollideComet(Comet comets[], int cSize, SpaceShip &ship, Explosion explosions[], int eSize);
void InitExplosions(Explosion explosions[], int size, ALLEGRO_BITMAP *image);
void DrawExplosions(Explosion explosions[], int size);
void StartExplosions(Explosion explosions[], int size, int x, int y);
void UpdateExplosions(Explosion explosions[], int size);
void InitBackground(Background &back, float x, float y, float velX, float velY, int width, int height, int dirX, int dirY, ALLEGRO_BITMAP *image);
void UpdateBackground(Background &back);
void DrawBackground(Background &back);
void ChangeState(int &state, int newState);
int main(void)
{
//primitive variable
bool done = false;
bool redraw = true;
const int FPS = 60;
int state = -1;
//object variables
Background BG;
Background MG;
Background FG;
//Allegro variables
ALLEGRO_DISPLAY *display = NULL;
ALLEGRO_EVENT_QUEUE *event_queue = NULL;
ALLEGRO_TIMER *timer = NULL;
ALLEGRO_FONT *font18 = NULL;
ALLEGRO_BITMAP *shipImage;
ALLEGRO_BITMAP *cometImage;
ALLEGRO_BITMAP *expImage;
ALLEGRO_BITMAP *title = NULL;
ALLEGRO_BITMAP *lost = NULL;
ALLEGRO_BITMAP *bgImage = NULL;
ALLEGRO_BITMAP *mgImage = NULL;
ALLEGRO_BITMAP *fgImage = NULL;
//Initialization Functions
if(!al_init()) //initialize Allegro
return -1;
display = al_create_display(WIDTH, HEIGHT); //create our display object
if(!display) //test display object
return -1;
al_init_primitives_addon();
al_install_keyboard();
al_init_font_addon();
al_init_ttf_addon();
al_init_image_addon();
al_install_audio();
al_init_acodec_addon();
event_queue = al_create_event_queue();
timer = al_create_timer(1.0 / FPS);
shipImage = al_load_bitmap("spaceship_by_arboris.png");
al_convert_mask_to_alpha(shipImage, al_map_rgb(255, 0, 255));
cometImage = al_load_bitmap("asteroids.png");
expImage = al_load_bitmap("explosion.png");
title = al_load_bitmap("Shooter_Title.png");
lost = al_load_bitmap("Shooter_Lose.png");
bgImage = al_load_bitmap("starBG.png");
mgImage = al_load_bitmap("starMG.png");
fgImage = al_load_bitmap("starFG.png");
al_reserve_samples(10);
shot = al_load_sample("shot.ogg");
boom = al_load_sample("explosion.wav");
song = al_load_sample("war.wav");
songInstance = al_create_sample_instance(song);
al_set_sample_instance_playmode(songInstance, ALLEGRO_PLAYMODE_LOOP);
al_attach_sample_instance_to_mixer(songInstance, al_get_default_mixer());
srand(time(NULL));
ChangeState(state, TITLE);
InitShip(ship, shipImage);
InitBullet(bullets, NUM_BULLETS);
InitComet(comets, NUM_COMETS, cometImage);
InitExplosions(explosions, NUM_EXPLOSIONS, expImage);
InitBackground(BG, 0, 0, 1, 0, 800, 400, -1, 1, bgImage);
InitBackground(MG, 0, 0, 2, 0, 2400, 400, -1, 1, mgImage);
InitBackground(FG, 0, 0, 4, 0, 2400, 400, -1, 1, fgImage);
font18 = al_load_font("arial.ttf", 18, 0);
al_register_event_source(event_queue, al_get_keyboard_event_source());
al_register_event_source(event_queue, al_get_timer_event_source(timer));
al_register_event_source(event_queue, al_get_display_event_source(display));
al_start_timer(timer);
while(!done)
{
ALLEGRO_EVENT ev;
al_wait_for_event(event_queue, &ev);
if(ev.type == ALLEGRO_EVENT_TIMER)
{
redraw = true;
if(keys[UP])
MoveShipUp(ship);
else if(keys[DOWN])
MoveShipDown(ship);
else
ResetShipAnimation(ship, 1);
if(keys[LEFT])
MoveShipLeft(ship);
else if(keys[RIGHT])
MoveShipRight(ship);
else
ResetShipAnimation(ship, 2);
if(state == TITLE)
{}
else if(state == PLAYING)
{
UpdateBackground(BG);
UpdateBackground(MG);
UpdateBackground(FG);
UpdateExplosions(explosions, NUM_EXPLOSIONS);
UpdateBullet(bullets, NUM_BULLETS);
StartComet(comets, NUM_COMETS);
UpdateComet(comets, NUM_COMETS);
CollideBullet(bullets, NUM_BULLETS, comets, NUM_COMETS, ship, explosions, NUM_EXPLOSIONS);
CollideComet(comets, NUM_COMETS, ship, explosions, NUM_EXPLOSIONS);
if(ship.lives <= 0)
ChangeState(state, LOST);
}
else if(state == LOST)
{}
}
else if(ev.type == ALLEGRO_EVENT_DISPLAY_CLOSE)
{
done = true;
}
else if(ev.type == ALLEGRO_EVENT_KEY_DOWN)
{
switch(ev.keyboard.keycode)
{
case ALLEGRO_KEY_ESCAPE:
done = true;
break;
case ALLEGRO_KEY_UP:
keys[UP] = true;
break;
case ALLEGRO_KEY_DOWN:
keys[DOWN] = true;
break;
case ALLEGRO_KEY_LEFT:
keys[LEFT] = true;
break;
case ALLEGRO_KEY_RIGHT:
keys[RIGHT] = true;
break;
case ALLEGRO_KEY_SPACE:
keys[SPACE] = true;
if(state == TITLE)
ChangeState(state, PLAYING);
else if(state ==PLAYING)
FireBullet(bullets, NUM_BULLETS, ship);
else if(state == LOST)
ChangeState(state, PLAYING);
break;
}
}
else if(ev.type == ALLEGRO_EVENT_KEY_UP)
{
switch(ev.keyboard.keycode)
{
case ALLEGRO_KEY_ESCAPE:
done = true;
break;
case ALLEGRO_KEY_UP:
keys[UP] = false;
break;
case ALLEGRO_KEY_DOWN:
keys[DOWN] = false;
break;
case ALLEGRO_KEY_LEFT:
keys[LEFT] = false;
break;
case ALLEGRO_KEY_RIGHT:
keys[RIGHT] = false;
break;
case ALLEGRO_KEY_SPACE:
keys[SPACE] = false;
break;
}
}
if(redraw && al_is_event_queue_empty(event_queue))
{
redraw = false;
if(state == TITLE)
{
al_draw_bitmap(title, 0, 0, 0);
}
else if(state == PLAYING)
{
DrawBackground(BG);
DrawBackground(MG);
DrawBackground(FG);
DrawShip(ship);
DrawBullet(bullets, NUM_BULLETS);
DrawComet(comets, NUM_COMETS);
DrawExplosions(explosions, NUM_EXPLOSIONS);
al_draw_textf(font18, al_map_rgb(255, 0, 255), 5, 5, 0, "Player has %i lives left. Player has destroyed %i objects", ship.lives, ship.score);
}
else if(state == LOST)
{
al_draw_bitmap(lost, 0, 0, 0);
al_draw_textf(font18, al_map_rgb(0, 255, 255), WIDTH - 10, 20, ALLEGRO_ALIGN_RIGHT, "Final Score: %i", ship.score);
}
al_flip_display();
al_clear_to_color(al_map_rgb(0,0,0));
}
}
al_destroy_sample(shot);
al_destroy_sample(boom);
al_destroy_sample(song);
al_destroy_sample_instance(songInstance);
al_destroy_bitmap(bgImage);
al_destroy_bitmap(mgImage);
al_destroy_bitmap(fgImage);
al_destroy_bitmap(title);
al_destroy_bitmap(lost);
al_destroy_bitmap(expImage);
al_destroy_bitmap(cometImage);
al_destroy_bitmap(shipImage);
al_destroy_event_queue(event_queue);
al_destroy_timer(timer);
al_destroy_font(font18);
al_destroy_display(display); //destroy our display object
return 0;
}
void InitShip(SpaceShip &ship, ALLEGRO_BITMAP *image = NULL) {
ship.x = 20;
ship.y = HEIGHT / 2;
ship.ID = PLAYER;
ship.lives = 3;
ship.speed = 6;
ship.boundx = 10;
ship.boundy = 12;
ship.score = 0;
ship.maxFrame = 3;
ship.curFrame = 0;
ship.frameCount = 0;
ship.frameDelay = 50;
ship.frameWidth = 44;
ship.frameHeight = 41;
ship.animationColumns = 3;
ship.animationDirection = 1;
ship.animationRow = 1;
if(image != NULL)
ship.image = image;
}
void ResetShipAnimation(SpaceShip &ship, int position)
{
if(position == 1)
ship.animationRow = 1;
else
ship.curFrame = 0;
}
void DrawShip(SpaceShip &ship)
{
int fx =(ship.curFrame % ship.animationColumns) * ship.frameWidth;
int fy = ship.animationRow * ship.frameHeight;
al_draw_bitmap_region(ship.image, fx, fy, ship.frameWidth,
ship.frameHeight, ship.x - ship.frameWidth / 2, ship.y - ship.frameHeight / 2, 0);
/*al_draw_filled_rectangle(ship.x - ship.boundx, ship.y - ship.boundy, ship.x + ship.boundx,
ship.y + ship.boundy, al_map_rgba(255, 0, 255, 100));*/
}
void MoveShipUp(SpaceShip &ship)
{
ship.animationRow = 0;
ship.y -= ship.speed;
if(ship.y < 0)
ship.y = 0;
}
void MoveShipDown(SpaceShip &ship)
{
ship.animationRow = 2;
ship.y += ship.speed;
if(ship.y > HEIGHT)
ship.y = HEIGHT;
}
void MoveShipLeft(SpaceShip &ship)
{
ship.curFrame = 2;
ship.x -= ship.speed;
if(ship.x < 0)
ship.x = 0;
}
void MoveShipRight(SpaceShip &ship)
{
ship.curFrame = 1;
ship.x += ship.speed;
if(ship.x > 300)
ship.x = 300;
}
void InitBullet(Bullet bullet[], int size)
{
for(int i = 0; i < size; i++)
{
bullet[i].ID = BULLET;
bullet[i].speed = 10;
bullet[i].live = false;
}
}
void DrawBullet(Bullet bullet[], int size)
{
for( int i = 0; i < size; i++)
{
if(bullet[i].live)
al_draw_filled_circle(bullet[i].x, bullet[i].y, 2, al_map_rgb(255, 255, 255));
}
}
void FireBullet(Bullet bullet[], int size, SpaceShip &ship)
{
for( int i = 0; i < size; i++)
{
if(!bullet[i].live)
{
bullet[i].x = ship.x + 17;
bullet[i].y = ship.y;
bullet[i].live = true;
al_play_sample(shot, 1, 0, 1, ALLEGRO_PLAYMODE_ONCE, 0);
break;
}
}
}
void UpdateBullet(Bullet bullet[], int size)
{
for(int i = 0; i < size; i++)
{
if(bullet[i].live)
{
bullet[i].x += bullet[i].speed;
if(bullet[i].x > WIDTH)
bullet[i].live = false;
}
}
}
void CollideBullet(Bullet bullet[], int bSize, Comet comets[], int cSize, SpaceShip &ship, Explosion explosions[], int eSize)
{
for(int i = 0; i < bSize; i++)
{
if(bullet[i].live)
{
for(int j =0; j < cSize; j++)
{
if(comets[j].live)
{
if(bullet[i].x > (comets[j].x - comets[j].boundx) &&
bullet[i].x < (comets[j].x + comets[j].boundx) &&
bullet[i].y > (comets[j].y - comets[j].boundy) &&
bullet[i].y < (comets[j].y + comets[j].boundy))
{
bullet[i].live = false;
comets[j].live = false;
ship.score++;
StartExplosions(explosions, eSize, bullet[i].x, bullet[i].y);
al_play_sample(boom, 1, 0, 1, ALLEGRO_PLAYMODE_ONCE, 0);
}
}
}
}
}
}
void InitComet(Comet comets[], int size, ALLEGRO_BITMAP *image = NULL)
{
for(int i = 0; i < size; i++)
{
comets[i].ID = ENEMY;
comets[i].live = false;
comets[i].speed = 5;
comets[i].boundx = 35;
comets[i].boundy = 35;
comets[i].maxFrame = 143;
comets[i].curFrame = 0;
comets[i].frameCount = 0;
comets[i].frameDelay = 2;
comets[i].frameWidth = 96;
comets[i].frameHeight = 96;
comets[i].animationColumns = 21;
if(rand() % 2)
comets[i].animationDirection = 1;
else
comets[i].animationDirection = -1;
if(image != NULL)
comets[i].image = image;
}
}
void DrawComet(Comet comets[], int size)
{
for(int i = 0; i < size; i++)
{
if(comets[i].live)
{
int fx = (comets[i].curFrame % comets[i].animationColumns) * comets[i].frameWidth;
int fy = (comets[i].curFrame / comets[i].animationColumns) * comets[i].frameHeight;
al_draw_bitmap_region(comets[i].image, fx, fy, comets[i].frameWidth,
comets[i].frameHeight, comets[i].x - comets[i].frameWidth / 2, comets[i].y - comets[i].frameHeight / 2, 0);
/*al_draw_filled_rectangle(comets[i].x - comets[i].boundx, comets[i].y - comets[i].boundy, comets[i].x + comets[i].boundx,
comets[i].y + comets[i].boundy, al_map_rgba(255, 0, 255, 100));*/
}
}
}
void StartComet(Comet comets[], int size)
{
for(int i = 0; i < size; i++)
{
if(!comets[i].live)
{
if(rand() % 500 == 0)
{
comets[i].live = true;
comets[i].x = WIDTH;
comets[i].y = 30 + rand() % (HEIGHT - 60);
break;
}
}
}
}
void UpdateComet(Comet comets[], int size)
{
for(int i = 0; i < size; i++)
{
if(comets[i].live)
{
if(++comets[i].frameCount >= comets[i].frameDelay)
{
comets[i].curFrame += comets[i].animationDirection;
if(comets[i].curFrame >= comets[i].maxFrame)
comets[i].curFrame = 0;
else if( comets[i].curFrame <= 0)
comets[i].curFrame = comets[i].maxFrame - 1;
comets[i].frameCount = 0;
}
comets[i].x -= comets[i].speed;
}
}
}
void CollideComet(Comet comets[], int cSize, SpaceShip &ship, Explosion explosions[], int eSize)
{
for(int i = 0; i < cSize; i++)
{
if(comets[i].live)
{
if(comets[i].x - comets[i].boundx < ship.x + ship.boundx &&
comets[i].x + comets[i].boundx > ship.x - ship.boundx &&
comets[i].y - comets[i].boundy < ship.y + ship.boundy &&
comets[i].y + comets[i].boundy > ship.y - ship.boundy)
{
ship.lives--;
comets[i].live = false;
StartExplosions(explosions, eSize, ship.x, ship.y);
al_play_sample(boom, 1, 0, 1, ALLEGRO_PLAYMODE_ONCE, 0);
}
else if(comets[i].x < 0)
{
comets[i].live = false;
ship.lives--;
}
}
}
}
void InitExplosions(Explosion explosions[], int size, ALLEGRO_BITMAP *image = NULL)
{
for(int i = 0; i < size; i++)
{
explosions[i].live = false;
explosions[i].maxFrame = 31;
explosions[i].curFrame = 0;
explosions[i].frameCount = 0;
explosions[i].frameDelay = 1;
explosions[i].frameWidth = 128;
explosions[i].frameHeight = 128;
explosions[i].animationColumns = 8;
explosions[i].animationDirection = 1;
if(image != NULL)
explosions[i].image = image;
}
}
void DrawExplosions(Explosion explosions[], int size)
{
for(int i = 0; i < size; i++)
{
if(explosions[i].live)
{
int fx = (explosions[i].curFrame % explosions[i].animationColumns) * explosions[i].frameWidth;
int fy = (explosions[i].curFrame / explosions[i].animationColumns) * explosions[i].frameHeight;
al_draw_bitmap_region(explosions[i].image, fx, fy, explosions[i].frameWidth,
explosions[i].frameHeight, explosions[i].x - explosions[i].frameWidth / 2, explosions[i].y - explosions[i].frameHeight / 2, 0);
}
}
}
void StartExplosions(Explosion explosions[], int size, int x, int y)
{
for(int i = 0; i < size; i++)
{
if(!explosions[i].live)
{
explosions[i].live = true;
explosions[i].x = x;
explosions[i].y = y;
break;
}
}
}
void UpdateExplosions(Explosion explosions[], int size)
{
for(int i = 0; i < size; i++)
{
if(explosions[i].live)
{
if(++explosions[i].frameCount >= explosions[i].frameDelay)
{
explosions[i].curFrame += explosions[i].animationDirection;
if(explosions[i].curFrame >= explosions[i].maxFrame)
{
explosions[i].curFrame = 0;
explosions[i].live = false;
}
explosions[i].frameCount = 0;
}
}
}
}
void InitBackground(Background &back, float x, float y, float velx, float vely, int width, int height, int dirX, int dirY, ALLEGRO_BITMAP *image)
{
back.x = x;
back.y = y;
back.velX = velx;
back.velY = vely;
back.width = width;
back.height = height;
back.dirX = dirX;
back.dirY = dirY;
back.image = image;
}
void UpdateBackground(Background &back)
{
back.x += back.velX * back.dirX;
if(back.x + back.width <= 0)
back.x = 0;
}
void DrawBackground(Background &back)
{
al_draw_bitmap(back.image, back.x, back.y, 0);
if(back.x + back.width < WIDTH)
al_draw_bitmap(back.image, back.x + back.width, back.y, 0);
}
void ChangeState(int &state, int newState)
{
if(state == TITLE)
{}
else if(state == PLAYING)
{
al_stop_sample_instance(songInstance);
}
else if(state == LOST)
{}
state = newState;
if(state == TITLE)
{}
else if(state == PLAYING)
{
InitShip(ship);
InitBullet(bullets, NUM_BULLETS);
InitComet(comets, NUM_COMETS);
InitExplosions(explosions, NUM_EXPLOSIONS);
al_play_sample_instance(songInstance);
}
else if(state == LOST)
{}
}
Check that a pointer you are using is not NULL when you try to load the file. If it stays NULL then that exception is always thrown because the system will try to read from memory address 0x0 which frankly does not exist
I am making a TBS game, at least im trying to.
So i've started off by following a tutorial on lazyfoo.net (lesson 29) on how to make a tiled environment and making my own changes along the way.
Every time i try to compile it it gives me two main errors:
No. 1: 255 error: expected primary-expression before ',' token
No. 2: 267 error: expected ';' before 'myUnit'
Here is the source code, i am fairly certain the that the problem is not with the images or the map.
CODE:
#include "SDL/SDL_image.h"
#include "SDL/SDL.h"
#include <string>
#include <fstream>
const int SCREEN_WIDTH = 600;
const int SCREEN_HEIGHT = 600;
const int SCREEN_BPP = 32;
const int FPS = 30;
const int UNIT_WIDTH = 50;
const int UNIT_HEIGHT = 50;
const int LEVEL_WIDTH = 600;
const int LEVEL_HEIGHT = 600;
const int TILE_WIDTH = 60;
const int TILE_HEIGHT = 60;
const int TOTAL_TILES = 100;
const int TILE_SPRITES = 3;
const int TILE_GRASS = 0;
const int TILE_WATER = 1;
const int TILE_MOUNTAIN = 2;
SDL_Surface *screen = NULL;
SDL_Surface *Unit = NULL;
SDL_Surface *tileMap = NULL;
SDL_Rect clips[ TILE_SPRITES ];
SDL_Event occur;
class Tile
{
private:
SDL_Rect box;
int type;
public:
Tile(int x, int y, int tileType);
void show();
int get_type();
SDL_Rect get_box();
};
class Unit
{
private:
SDL_Rect Box;
bool movement;
public:
Unit();
void handle_input();
void move( Tile *tiles[]);
void show();
};
SDL_Surface *load_image(std::string filename)
{
SDL_Surface* loadedImage = NULL;
SDL_Surface* optimizedImage = NULL;
loadedImage = IMG_Load(filename.c_str());
if(loadedImage != NULL)
{
optimizedImage = SDL_DisplayFormat( loadedImage );
SDL_FreeSurface( loadedImage );
if(optimizedImage != NULL)
{
SDL_SetColorKey(optimizedImage, SDL_SRCCOLORKEY, SDL_MapRGB(optimizedImage->format, 0,0xff,0xff));
}
}
return optimizedImage;
}
void apply_surface(int x, int y, SDL_Surface* source, SDL_Surface* destination, SDL_Rect* clip = NULL)
{
SDL_Rect offset;
offset.x = x;
offset.y = y;
SDL_BlitSurface(source, clip, destination, &offset);
}
bool init()
{
if(SDL_Init(SDL_INIT_EVERYTHING) == -1)
{
return false;
}
screen = SDL_SetVideoMode(SCREEN_WIDTH,SCREEN_HEIGHT,SCREEN_BPP,SDL_SWSURFACE);
if(screen == NULL)
{
return false;
}
SDL_WM_SetCaption("Strategy Game", NULL);
return true;
}
bool load_files()
{
Unit = load_image("infantry_red.png");
if(Unit == NULL)
{
return false;
}
tileMap = load_image("tilemap.png");
if( tileMap == NULL)
{
return false;
}
return 0;
}
void clean_up(Tile *tiles[])
{
SDL_FreeSurface(Unit);
SDL_FreeSurface(tileMap);
for(int t = 0;t < TOTAL_TILES; t++)
{
delete tiles[ t ];
}
SDL_Quit();
}
void clip_tiles()
{
clips[TILE_GRASS].x = 0;
clips[TILE_GRASS].y = 0;
clips[TILE_GRASS].w = TILE_WIDTH;
clips[TILE_GRASS].h = TILE_HEIGHT;
clips[TILE_WATER].x = 60;
clips[TILE_WATER].y = 0;
clips[TILE_WATER].w = TILE_WIDTH;
clips[TILE_WATER].h = TILE_HEIGHT;
clips[TILE_MOUNTAIN].x = 120;
clips[TILE_MOUNTAIN].y = 0;
clips[TILE_MOUNTAIN].w = TILE_WIDTH;
clips[TILE_MOUNTAIN].h = TILE_HEIGHT;
}
bool set_tiles( Tile *tiles[])
{
int x = 0, y = 0;
std::ifstream map("strategy_game.map");
if(map == NULL)
{
return false;
}
for(int t = 0; y < TOTAL_TILES; t++)
{
int tileType = -1;
map >> tileType;
if(map.fail() == true)
{
map.close();
return false;
}
if( (tileType >= 0) && (tileType < TILE_SPRITES))
{
tiles[t] = new Tile(x,y,tileType);
}
else
{
map.close();
return false;
}
x += TILE_WIDTH;
if(x >= LEVEL_WIDTH)
{
x = 0;
y += TILE_HEIGHT;
}
}
map.close();
return true;
}
Tile::Tile(int x, int y, int tileType)
{
box.x = x;
box.y = y;
box.w = TILE_WIDTH;
box.h = TILE_HEIGHT;
type = tileType;
}
void Tile::show()
{
apply_surface(box.x, box.y, tileMap, screen, &clips[type]);
}
int Tile::get_type()
{
return type;
}
SDL_Rect Tile::get_box()
{
return box;
}
Unit::Unit()
{
Box.x = 0;
Box.y = 0;
Box.w = UNIT_WIDTH;
Box.h = UNIT_HEIGHT;
}
SDL_Rect rect;
int mouseX,mouseY;
void Unit::handle_input()
{
if(occur.type == SDL_MOUSEBUTTONDOWN)
{
mouseX = occur.button.x;
mouseY = occur.button.y;
}
}
void Unit::move(Tile *tiles[])
{
Box.x += mouseX;
if( Box.x < 0 || Box.x + UNIT_WIDTH > LEVEL_WIDTH )
{
Box.x -= mouseX;
}
Box.y -= mouseY;
if( Box.y < 0 || Box.y + UNIT_HEIGHT > LEVEL_HEIGHT)
{
Box.y -= mouseY;
}
}
void Unit::show()
{
int BoxX;
int BoxY;
Box.x = BoxX;
Box.y = BoxY;
SDL_Rect unitOffset;
unitOffset.x = BoxX;
unitOffset.y = BoxY;
SDL_BlitSurface(Unit,NULL,screen,unitOffset);
}
Uint32 start;
int main(int argc, char* args[])
{
bool quit = false;
Unit myUnit;
Tile *tiles[TOTAL_TILES];
if(init() == false)
{
return 1;
}
if(load_files() == false)
{
return 1;
}
clip_tiles();
if( set_tiles(tiles) == false)
{
return 1;
}
while(quit == false)
{
start = SDL_GetTicks();
while(SDL_PollEvent(&occur));
{
myUnit.handle_input();
switch(occur.type)
{
case SDL_QUIT:
quit = false;
break;
}
}
myUnit.move(tiles);
for(int t = 0;t<TOTAL_TILES;t++)
{
tiles[t]->show();
}
myUnit.show();
//render
SDL_FillRect(screen,&screen->clip_rect,0);
SDL_Flip(screen);
if(1000/FPS > SDL_GetTicks() - start ){
SDL_Delay(1000/FPS - (SDL_GetTicks()-start));
}
}
clean_up( tiles );
return 0;
}
Remove semi-colon from while(SDL_PollEvent(&occur))
Change: SDL_Surface *Unit = NULL; to `SDL_Surface *unitSurf = NULL;'
Change:
Unit = load_image("infantry_red.png");
if(Unit == NULL)
{
return false;
}
to
unitSurf = load_image("infantry_red.png");
if(unitSurf == NULL)
{
return false;
}
Change: SDL_BlitSurface(Unit,NULL,screen,unitOffset); to SDL_BlitSurface(unitSurf,NULL,screen,unitOffset);
Remove the semicolon after while(SDL_PollEvent(&occur))
I've checked everywhere in my code and I could not find the reason why my program freezes every time I load it. The problem started happening after I included a new AI header, "schoolboy.h". I checked to make sure that attempting to blit an image wasn't the problem, and it wasn't. So, after some testing, I figured that the problem lied in "void schoolboy_action()" within "schoolboy.h". Here is the entire project's code. I spent an hour looking at that section and could not find a solution. I know that the is really long, and that there are some holes in it that I plan on filling, but please bear with me.
Main.cpp
#include "schoolboy.h"
#include "include_file.h"
int main(int argc,char* argv[])
{
SDL_Init(SDL_INIT_EVERYTHING);
variables();
while (quit == 1)
{
while (MARS.alive == 1)
{
SDL_WM_SetCaption("Mobile Anihilation Robot System", NULL);
applysurface(0,0,background,screen);
MARS_action();
MARS_bullet_action(1);
gunshot_explosion_action(1);
if (create_schoolboy_variable == 1)
{create_schoolboy(); create_schoolboy_variable = 0;}
else if (create_schoolboy_variable < 1)
{create_schoolboy_variable = rand() % 1;};
schoolboy_action(0,0,0);
applysurface(MARS.x-25, MARS.y-25, MARS_image, screen);
SDL_Flip(screen);
SDL_Delay(1);
};
while (MARS.alive == 0)
{
SDL_WM_SetCaption("Mobil Anihilation Robot System", NULL);
};
};
SDL_FreeSurface(screen);
SDL_Quit();
return 0;
};
include_file.h
#ifndef INCLUDE_FILE_H_INCLUDED
#define INCLUDE_FILE_H_INCLUDED
#include <map>
#include <cstdlib>
#include <SDL/SDL.h>
#include <SDL/SDL_image.h>
/*structs*/
struct instance_struct
{
int gunshot_explosion;
int schoolboy_instance;
};
struct MARS_struct
{
int x;
int y;
int alive;
int bullet;
};
struct MARS_bullet_struct
{
int x;
int y;
int direction;
int exist;
int id;
bool operator<(const MARS_bullet_struct & n)const{return this->id<n.id;}
};
struct schoolboy_struct
{
int x;
int y;
int direction;
int id;
int exist;
int walk_delay;
int shoot_delay;
int walked;
SDL_Rect clip[3];
bool operator<(const schoolboy_struct&n)const{return this->id<n.id;}
};
struct gunshot_explosion_struct
{
int x;
int y;
int id;
int life;
int exist;
bool operator<(const gunshot_explosion_struct&n)const{return this->id<n.id;}
};
/*declaring structs*/
MARS_struct MARS;
instance_struct instance_body;
std::map<int, MARS_bullet_struct>MARS_bullet;
std::map<int, schoolboy_struct>schoolboy;
std::map<int, gunshot_explosion_struct>gunshot_explosion;
/*applysurface*/
void applysurface(int x, int y, SDL_Surface *source, SDL_Surface *destination, SDL_Rect* clip = NULL)
{
SDL_Rect offset;
offset.x = x;
offset.y = y;
SDL_BlitSurface(source,clip,destination,&offset);
};
/*declaring global variables*/
int quit;
int create_schoolboy_variable;
SDL_Event event;
SDL_Rect clip[3];
SDL_Surface *screen = NULL;
SDL_Surface *background = NULL;
SDL_Surface *gunshot_explosion_image = NULL;
SDL_Surface *MARS_image = NULL;
SDL_Surface *MARS_bullet_image = NULL;
SDL_Surface *schoolboy_image = NULL;
/*giving variables values*/
void variables()
{
quit = 1;
MARS.alive = 1;
MARS.x = 256;
MARS.y = 256;
create_schoolboy_variable = 0;
clip[0].x = 0;
clip[0].y = 0;
clip[0].w = 50;
clip[0].h = 50;
clip[1].x = 50;
clip[1].y = 50;
clip[1].w = 100;
clip[1].h = 100;
clip[2].x = 100;
clip[2].y = 100;
clip[2].w = 150;
clip[2].h = 150;
clip[3].x = 150;
clip[3].y = 150;
clip[3].w = 200;
clip[3].h = 200;
screen = SDL_SetVideoMode(512,512,32,SDL_SWSURFACE);
background = IMG_Load("images/background.png");
gunshot_explosion_image = IMG_Load("images/gunshot_explosion.png");
MARS_image = IMG_Load("images/MARS.png");
MARS_bullet_image = IMG_Load("images/MARS_bullet.png");
schoolboy_image = IMG_Load("images/schoolboy.png");
};
void gunshot_explosion_action(int instance)
{
while (instance <= instance_body.gunshot_explosion)
{
if (gunshot_explosion[instance].exist == 0)
{
gunshot_explosion[instance].life = gunshot_explosion[instance].life + 1;
if (gunshot_explosion[instance].life > 7)
{gunshot_explosion[instance].exist = 1;};
applysurface(gunshot_explosion[instance].x-6,gunshot_explosion[instance].y-6,gunshot_explosion_image,screen);
};
instance = instance + 1;
};
};
#endif // INCLUDE_FILE_H_INCLUDED
MARS.h
#ifndef MARS_H_INCLUDED
#define MARS_H_INCLUDED
#include "include_file.h"
/*character functions*/
void MARS_action()
{
if (SDL_PollEvent(&event))
{
if (event.type == SDL_QUIT)
{
MARS.alive = 2;
quit = 0;
};
if (event.type == SDL_KEYDOWN)
{
switch(event.key.keysym.sym)
{
case SDLK_a:
if(MARS.x > 0){MARS.x = MARS.x - 16;}; break;
case SDLK_d:
if(MARS.x < 512){MARS.x = MARS.x + 16;}; break;
case SDLK_w:
if(MARS.y > 0){MARS.y = MARS.y - 16;}; break;
case SDLK_s:
if(MARS.y < 512){MARS.y = MARS.y + 16;}; break;
case SDLK_LEFT:
MARS.bullet = MARS.bullet + 1;
MARS_bullet[MARS.bullet].direction = 2;
MARS_bullet[MARS.bullet].x = MARS.x-25;
MARS_bullet[MARS.bullet].y = MARS.y;
instance_body.gunshot_explosion = instance_body.gunshot_explosion+1;
gunshot_explosion[instance_body.gunshot_explosion].x = MARS.x-25;
gunshot_explosion[instance_body.gunshot_explosion].y = MARS.y;
break;
case SDLK_RIGHT:
MARS.bullet = MARS.bullet + 1;
MARS_bullet[MARS.bullet].direction = 0;
MARS_bullet[MARS.bullet].x = MARS.x+25;
MARS_bullet[MARS.bullet].y = MARS.y;
instance_body.gunshot_explosion = instance_body.gunshot_explosion+1;
gunshot_explosion[instance_body.gunshot_explosion].x = MARS.x+25;
gunshot_explosion[instance_body.gunshot_explosion].y = MARS.y;
break;
case SDLK_UP:
MARS.bullet = MARS.bullet + 1;
MARS_bullet[MARS.bullet].direction = 1;
MARS_bullet[MARS.bullet].x = MARS.x;
MARS_bullet[MARS.bullet].y = MARS.y-25;
instance_body.gunshot_explosion = instance_body.gunshot_explosion+1;
gunshot_explosion[instance_body.gunshot_explosion].x = MARS.x;
gunshot_explosion[instance_body.gunshot_explosion].y = MARS.y-25;
break;
case SDLK_DOWN:
MARS.bullet = MARS.bullet + 1;
MARS_bullet[MARS.bullet].direction = 3;
MARS_bullet[MARS.bullet].x = MARS.x;
MARS_bullet[MARS.bullet].y = MARS.y+25;
instance_body.gunshot_explosion = instance_body.gunshot_explosion+1;
gunshot_explosion[instance_body.gunshot_explosion].x = MARS.x;
gunshot_explosion[instance_body.gunshot_explosion].y = MARS.y+25;
break;
case SDLK_ESCAPE: quit = 0; MARS.alive = 2; break;
};
};
};
};
void MARS_bullet_action(int instance)
{
while (instance <= MARS.bullet)
{
if (MARS_bullet[instance].exist == 0)
{
if (MARS_bullet[instance].direction == 0)
{MARS_bullet[instance].x = MARS_bullet[instance].x + 5;};
if (MARS_bullet[instance].direction == 1)
{MARS_bullet[instance].y = MARS_bullet[instance].y - 5;};
if (MARS_bullet[instance].direction == 2)
{MARS_bullet[instance].x = MARS_bullet[instance].x - 5;};
if (MARS_bullet[instance].direction == 3)
{MARS_bullet[instance].y = MARS_bullet[instance].y + 5;};
if (MARS_bullet[instance].x < 0 or MARS_bullet[instance].x > 512 or MARS_bullet[instance].y < 0 or MARS_bullet[instance].y > 512)
{MARS_bullet[instance].exist = 1;};
applysurface(MARS_bullet[instance].x-5, MARS_bullet[instance].y-5, MARS_bullet_image, screen);
};
instance = instance + 1;
};
};
#endif // MARS_H_INCLUDED
schoolboy.h
#ifndef SCHOOLBOY_H_INCLUDED
#define SCHOOLBOY_H_INCLUDED
#include "include_file.h"
void create_schoolboy(int positionx = 0, int positiony = 0)
{
instance_body.schoolboy_instance = instance_body.schoolboy_instance + 1;
positionx = rand() % 1;
positiony = rand() % 1;
if (positionx == 0 and positiony == 0)
{
schoolboy[instance_body.schoolboy_instance].x = 0;
schoolboy[instance_body.schoolboy_instance].y = 0;
};
if (positionx == 1 and positiony == 0)
{
schoolboy[instance_body.schoolboy_instance].x = 512;
schoolboy[instance_body.schoolboy_instance].y = 0;
};
if (positionx == 0 and positiony == 1)
{
schoolboy[instance_body.schoolboy_instance].x = 0;
schoolboy[instance_body.schoolboy_instance].y = 512;
};
if (positionx == 1 and positiony == 1)
{
schoolboy[instance_body.schoolboy_instance].x = 512;
schoolboy[instance_body.schoolboy_instance].y = 512;
};
};
void schoolboy_action(int instance, int bullet, int first_direction)
{
while (instance <= instance_body.schoolboy_instance)
{
first_direction = rand() % 1;
if (schoolboy[instance].exist == 0)
{
while (bullet <= MARS.bullet)
{
if (schoolboy[instance].x-12 >= MARS_bullet[bullet].x and MARS_bullet[bullet].x <= schoolboy[instance].x+12 and schoolboy[instance].y-12 >= MARS_bullet[bullet].y and MARS_bullet[bullet].y <= schoolboy[instance].y+12)
{
schoolboy[instance].exist = 1;
};
bullet = bullet + 1;
};
if (schoolboy[instance].walk_delay == 0)
{
if (first_direction == 0)
{
schoolboy[instance].walked = 0;
if (MARS.x > schoolboy[instance].x)
{
schoolboy[instance].x = schoolboy[instance].x + 16;
applysurface(schoolboy[instance].x-25, schoolboy[instance].y-25, schoolboy_image, screen, &clip[0]);
schoolboy[instance].walked = 1;
}
else if (MARS.x < schoolboy[instance].x)
{
schoolboy[instance].x = schoolboy[instance].x - 16;
applysurface(schoolboy[instance].x-25, schoolboy[instance].y-25, schoolboy_image, screen, &clip[2]);
schoolboy[instance].walked = 1;
};
if (schoolboy[instance].walked = 0)
{
if (MARS.y > schoolboy[instance].y)
{
schoolboy[instance].y = schoolboy[instance].y+16;
applysurface(schoolboy[instance].x-25, schoolboy[instance].y-25, schoolboy_image, screen, &clip[3]);
schoolboy[instance].walked = 1;
}
else if (MARS.y < schoolboy[instance].y)
{
schoolboy[instance].y = schoolboy[instance].y+16;
applysurface(schoolboy[instance].x-25, schoolboy[instance].y-25, schoolboy_image, screen, &clip[1]);
schoolboy[instance].walked = 1;
};
};
};
if (first_direction == 1)
{
schoolboy[instance].walked = 0;
if (MARS.y > schoolboy[instance].y)
{
schoolboy[instance].y = schoolboy[instance].y+16;
applysurface(schoolboy[instance].x-25, schoolboy[instance].y-25, schoolboy_image, screen, &clip[3]);
schoolboy[instance].walked = 1;
}
else if (MARS.y < schoolboy[instance].y)
{
schoolboy[instance].y = schoolboy[instance].y+16;
applysurface(schoolboy[instance].x-25, schoolboy[instance].y-25, schoolboy_image, screen, &clip[1]);
schoolboy[instance].walked = 1;
};
if (schoolboy[instance].walked = 0)
{
if (MARS.x > schoolboy[instance].x)
{
schoolboy[instance].x = schoolboy[instance].x + 16;
applysurface(schoolboy[instance].x-25, schoolboy[instance].y-25, schoolboy_image, screen, &clip[0]);
schoolboy[instance].walked = 1;
}
else if (MARS.x < schoolboy[instance].x)
{
schoolboy[instance].x = schoolboy[instance].x - 16;
applysurface(schoolboy[instance].x-25, schoolboy[instance].y-25, schoolboy_image, screen, &clip[2]);
schoolboy[instance].walked = 1;
};
};
};
schoolboy[instance].walk_delay = schoolboy[instance].walk_delay + 1;
}
else {schoolboy[instance].walk_delay = schoolboy[instance].walk_delay + 1;};
if (schoolboy[instance].walk_delay == 10){schoolboy[instance].walk_delay = 0;};
};
};
instance = instance + 1;
};
#endif // SCHOOLBOY_H_INCLUDED
All of my files are involved with each other, especially schoolboy.h, which is why I put them down. If you find the answer, can you explain why that's so. Any help appreciated!
Your instance = instance + 1 line at the end of schoolboy.h looks like it's outside the while loop that relies on it being incremented to eventually end.