Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Closed 9 years ago.
Improve this question
So I'm nearly done writing my first tic tac toe in C++ and SDL, ad I've run into a slight problem. I use a state machine to switch from title screen to shape choice screen, to player one or two to winning screen and back to the choice screen etc etc. On the choice screen I have two SDL_Rect arrays that act as buttons and the buttons are the X and O sprites I use from my sprite sheet. When the mouse hovers over them, they change colors. Everything is fine and dandy until the game resets to the choice screen after a win lose or tie. When it goes back to the choice screen and the mouse hovers over the button, it does not show the highlighted or mouse hovered sprite it clipped before. I pin pointed this problem to the initialization of my "set_grid_regions()" function. But this array doesn't even interact with the choice screen class in any way. How can this be affecting my hover sprites?
Just so anyone can see, here is the whole of the program:
#include "SDL.h"
#include "SDL_ttf.h"
#include "SDL_image.h"
#include "SDL_mixer.h"
#include <string>
#include <iostream>
//constants
const int SCREEN_HEIGHT = 300;
const int SCREEN_WIDTH = 300;
const int SCREEN_BPP = 32;
const int GRID_WIDTH = 96;
const int GRID_HEIGHT = 96;
//game states
enum States
{
S_NULL,
INTRO,
CHOICE,
START_O,
START_X,
PLAYER_ONE,
PLAYER_TWO,
O_win,
X_win,
Tie,
EXIT,
};
// CLASSES //
class GameState
{
public:
virtual void events() = 0;
virtual void logic() = 0;
virtual void render() = 0;
virtual ~GameState(){};
};
class intro : public GameState
{
private:
//hover variable
bool button_hover = NULL;
//rects and surfaces
SDL_Rect button;
SDL_Surface *title_message = NULL;
public:
void events();
void logic();
void render();
intro();
~intro();
};
class choice : public GameState
{
private:
bool O_hover = NULL;
bool X_hover = NULL;
SDL_Rect shape_O_button;
SDL_Rect shape_X_button;
SDL_Surface *choice_text = NULL;
public:
void events();
void logic();
void render();
choice();
~choice();
};
class playerOne : public GameState
{
public:
void events();
void logic();
void render();
playerOne();
~playerOne();
};
class playerTwo : public GameState
{
public:
void events();
void logic();
void render();
playerTwo();
~playerTwo();
};
class win : public GameState
{
private:
int winner = NULL;
SDL_Surface *Tie = NULL;
SDL_Surface *X_win = NULL;
SDL_Surface *O_win = NULL;
public:
void events();
void logic();
void render();
win(int winner);
~win();
};
class Exit : public GameState
{
public:
void events();
void logic();
void render();
Exit();
~Exit();
};
// GLOBALS //
GameState *currentState = NULL;
int stateID = S_NULL;
int nextState = S_NULL;
//event
SDL_Event event;
//surfaces
SDL_Surface *screen = NULL;
SDL_Surface *sprites = NULL;
//ttf
TTF_Font *font = NULL;
SDL_Color color = { 0, 0, 0 };
SDL_Color win_Color = { 0, 100, 0 };
//arrays
int grid_array[9];
//rects
SDL_Rect sprite_clip[10];
SDL_Rect grid_region[9];
int number_elements = sizeof(grid_region) / sizeof(grid_region[0]);
//bools
bool shape = NULL;
bool invalid = NULL;
bool winner = NULL;
//ints
int highlight = NULL;
int shape_winner = NULL;
// FUCNTIONS //
//load image
SDL_Surface *load_image(std::string filename)
{
//loaded image
SDL_Surface* loadedImage = NULL;
//optimized surface
SDL_Surface* optimizedImage = NULL;
//load image
loadedImage = IMG_Load(filename.c_str());
//if image loaded
if (loadedImage != NULL)
{
//Create optimized image
optimizedImage = SDL_DisplayFormat(loadedImage);
//free old image
SDL_FreeSurface(loadedImage);
//if optimized
if (optimizedImage != NULL)
{
//map color key
Uint32 colorkey = SDL_MapRGB(optimizedImage->format, 255, 255, 0);
//set all pixles of color 0,0,0 to be transparent
SDL_SetColorKey(optimizedImage, SDL_SRCCOLORKEY, colorkey);
}
}
return optimizedImage;
}
//apply image
void apply_surface(int x, int y, SDL_Surface* source, SDL_Surface* destination, SDL_Rect* clip = NULL)
{
//temp rect
SDL_Rect offset;
//offsets
offset.x = x;
offset.y = y;
//blit
SDL_BlitSurface(source, clip, destination, &offset);
}
//initiate SDL etc
bool init()
{
//initialize all SDL subsystems
if (SDL_Init(SDL_INIT_EVERYTHING) == -1)
{
return false;
}
//set up screen
screen = SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, SDL_SWSURFACE);
if (screen == NULL)
{
return false;
}
//check screen
if (screen == NULL)
{
return false;
}
//init TTF
if (TTF_Init() == -1)
{
return false;
}
//set window caption
SDL_WM_SetCaption("Tic-Tac-Toe", NULL);
//if evetything worked
return true;
}
//load files
bool load_files()
{
//sprite sheet
sprites = load_image("Sprites.png");
if (sprites == NULL)
{
return false;
}
font = TTF_OpenFont("font.ttf", 45);
if (font == NULL)
{
return false;
}
return true;
}
//quit
void clean_up()
{
//delete game state
delete currentState;
//free image
SDL_FreeSurface(sprites);
//quit ttf
TTF_CloseFont(font);
TTF_Quit();
//quit SDL
SDL_Quit();
}
void set_clip_regions()
{
//init clip array
//O
sprite_clip[0].x = 300;
sprite_clip[0].y = 0;
sprite_clip[0].w = 80;
sprite_clip[0].h = 82;
//X
sprite_clip[1].x = 300;
sprite_clip[1].y = 176;
sprite_clip[1].w = 80;
sprite_clip[1].h = 82;
//grid
sprite_clip[2].x = 0;
sprite_clip[2].y = 0;
sprite_clip[2].w = 300;
sprite_clip[2].h = 300;
//highlight
sprite_clip[3].x = 300;
sprite_clip[3].y = 82;
sprite_clip[3].w = 94;
sprite_clip[3].h = 94;
//left to right line
sprite_clip[4].x = 398;
sprite_clip[4].y = 188;
sprite_clip[4].w = 234;
sprite_clip[4].h = 12;
//diag up
sprite_clip[5].x = 393;
sprite_clip[5].y = 0;
sprite_clip[5].w = 188;
sprite_clip[5].h = 186;
//up down line
sprite_clip[6].x = 591;
sprite_clip[6].y = 0;
sprite_clip[6].w = 11;
sprite_clip[6].h = 208;
//Diag down line
sprite_clip[7].x = 0;
sprite_clip[7].y = 300;
sprite_clip[7].w = 188;
sprite_clip[7].h = 186;
//start button
sprite_clip[8].x = 202;
sprite_clip[8].y = 300;
sprite_clip[8].w = 94;
sprite_clip[8].h = 32;
//intro and choice background
sprite_clip[9].x = 300;
sprite_clip[9].y = 300;
sprite_clip[9].w = 300;
sprite_clip[9].h = 300;
//start hover
sprite_clip[10].x = 202;
sprite_clip[10].y = 332;
sprite_clip[10].w = 94;
sprite_clip[10].h = 32;
//X hover
sprite_clip[11].x = 202;
sprite_clip[11].y = 446;
sprite_clip[11].w = 80;
sprite_clip[11].h = 82;
//o hover
sprite_clip[12].x = 202;
sprite_clip[12].y = 364;
sprite_clip[12].w = 80;
sprite_clip[12].h = 82;
}
void set_grid_regions()
{
//set regions for images to be applied to
grid_region[0].x = 3;
grid_region[0].y = 3;
grid_region[1].x = 103;
grid_region[1].y = 3;
grid_region[2].x = 203;
grid_region[2].y = 3;
grid_region[3].x = 3;
grid_region[3].y = 103;
grid_region[4].x = 103;
grid_region[4].y = 103;
grid_region[5].x = 203;
grid_region[5].y = 103;
grid_region[6].x = 3;
grid_region[6].y = 203;
grid_region[7].x = 103;
grid_region[7].y = 203;
grid_region[8].x = 203;
grid_region[8].y = 203;
}
void init_grid()
{
//from left to right top to bottom
grid_array[0] = 0;
grid_array[1] = 0;
grid_array[2] = 0;
grid_array[3] = 0;
grid_array[4] = 0;
grid_array[5] = 0;
grid_array[6] = 0;
grid_array[7] = 0;
grid_array[8] = 0;
}
// STATE MACHINE FUNCTIONS //
void set_next_state(int newState)
{
if (nextState != EXIT)
{
nextState = newState;
}
}
void change_state()
{
if (nextState != S_NULL)
{
//change state
switch (nextState)
{
case CHOICE:
currentState = new choice();
break;
case PLAYER_ONE:
currentState = new playerOne();
break;
case PLAYER_TWO:
currentState = new playerTwo();
break;
case O_win:
currentState = new win(0);
break;
case X_win:
currentState = new win(1);
break;
case Tie:
currentState = new win(2);
break;
case EXIT:
currentState = new Exit();
break;
}
//change state
stateID = nextState;
//null nextState
nextState = S_NULL;
}
}
// CLASS DEFINITIONS //
intro::intro()
{
//button
button_hover = false;
//title
title_message = TTF_RenderText_Solid(font, "TIC TAC TOE", color);
//button bounds
button.x = 102;
button.y = 180;
button.w = 94;
button.h = 32;
}
intro::~intro()
{
SDL_FreeSurface(title_message);
}
void intro::events()
{
int x, y;
//mouse events
while (SDL_PollEvent(&event))
{
if (event.type == SDL_MOUSEMOTION)
{
x = event.motion.x;
y = event.motion.y;
if ((x > button.x) && (x < button.x + button.w) && (y > button.y) && (y < button.y + button.h))
{
button_hover = true;
}
else
{
button_hover = false;
}
}
if (event.type == SDL_MOUSEBUTTONDOWN)
{
if (event.button.button == SDL_BUTTON_LEFT)
{
x = event.motion.x;
y = event.motion.y;
if ((x > button.x) && (x < button.x + button.w) && (y > button.y) && (y < button.y + button.h))
{
set_next_state(CHOICE);
}
}
}
if (event.type == SDL_QUIT)
{
set_next_state(EXIT);
}
}
}
void intro::logic()
{
}
void intro::render()
{
apply_surface(0, 0, sprites, screen, &sprite_clip[9]);
apply_surface((SCREEN_WIDTH - title_message->w) / 2, 100, title_message, screen);
if (button_hover == true)
{
apply_surface(102, 180, sprites, screen, &sprite_clip[10]);
}
else
{
apply_surface(102, 180, sprites, screen, &sprite_clip[8]);
}
}
choice::choice()
{
O_hover = false;
X_hover = false;
shape_O_button.x = 0;
shape_O_button.y = 130;
shape_O_button.w = 80;
shape_O_button.h = 82;
shape_X_button.x = 220;
shape_X_button.y = 130;
shape_X_button.w = 80;
shape_X_button.h = 82;
font = TTF_OpenFont("font.ttf", 34);
choice_text = TTF_RenderText_Solid(font, "CHOOSE YOUR SHAPE", color);
}
choice::~choice()
{
SDL_FreeSurface(choice_text);
O_hover = NULL;
X_hover = NULL;
}
void choice::events()
{
int x, y;
//mouse events
while (SDL_PollEvent(&event))
{
if (event.type == SDL_MOUSEMOTION)
{
x = event.motion.x;
y = event.motion.y;
if ((x > shape_O_button.x) && (x < shape_O_button.x + shape_O_button.w) && (y > shape_O_button.y) && (y < shape_O_button.y + shape_O_button.h))
{
O_hover = true;
}
else
{
O_hover = false;
}
if ((x > shape_X_button.x) && (x < shape_X_button.x + shape_X_button.w) && (y > shape_X_button.y) && (y < shape_X_button.y + shape_X_button.h))
{
X_hover = true;
}
else
{
X_hover = false;
}
}
if (event.type == SDL_MOUSEBUTTONDOWN)
{
if (event.button.button == SDL_BUTTON_LEFT)
{
x = event.motion.x;
y = event.motion.y;
if ((x > shape_O_button.x) && (x < shape_O_button.x + shape_O_button.w) && (y > shape_O_button.y) && (y < shape_O_button.y + shape_O_button.h))
{
shape = false;
init_grid();
set_next_state(PLAYER_ONE);
}
if ((x > shape_X_button.x) && (x < shape_X_button.x + shape_X_button.w) && (y > shape_X_button.y) && (y < shape_X_button.y + shape_X_button.h))
{
shape = true;
init_grid();
set_next_state(PLAYER_TWO);
}
}
}
if (event.type == SDL_QUIT)
{
set_next_state(EXIT);
}
}
}
void choice::logic()
{
}
void choice::render()
{
apply_surface( 0, 0, sprites, screen, &sprite_clip[9]);
if (O_hover == false)
{
apply_surface(shape_O_button.x, shape_O_button.y, sprites, screen, &sprite_clip[0]);
}
else
{
apply_surface(shape_O_button.x, shape_O_button.y, sprites, screen, &sprite_clip[12]);
}
if (X_hover == false)
{
apply_surface(shape_X_button.x, shape_X_button.y, sprites, screen, &sprite_clip[1]);
}
else
{
apply_surface(shape_X_button.x, shape_X_button.y, sprites, screen, &sprite_clip[11]);
}
apply_surface((SCREEN_WIDTH - choice_text->w) / 2, 60, choice_text, screen);
}
//plyer O
playerOne::playerOne()
{
set_grid_regions();
}
playerOne::~playerOne()
{
}
void playerOne::events()
{
//mouse offsets
int x = 0, y = 0;
//if mouse moves
while (SDL_PollEvent(&event))
{
if (event.type == SDL_MOUSEMOTION)
{
//get the mouse co-ords
x = event.motion.x;
y = event.motion.y;
for (int grid = 0; grid < number_elements; grid++)
{
if ((x > grid_region[grid].x) && (x < grid_region[grid].x + GRID_WIDTH) && (y > grid_region[grid].y) && (y < grid_region[grid].y + GRID_HEIGHT))
{
//set highlight region
highlight = grid;
}
}
}
//when the player clicks on a grid_region
if (event.type == SDL_MOUSEBUTTONDOWN)
{
//mouse co-ordinates
x = event.motion.x;
y = event.motion.y;
if (event.button.button == SDL_BUTTON_LEFT)
{
//iterate
for (int grid = 0; grid < number_elements; grid++)
{
//if in region box
if ((x > grid_region[grid].x) && (x < grid_region[grid].x + GRID_WIDTH) && (y > grid_region[grid].y) && (y < grid_region[grid].y + GRID_HEIGHT))
{
//check region
//if O turn
if ((grid_array[grid] == 0) && (shape == 0))
{
//fill region
grid_array[grid] = 1;
shape = (!shape);
}
else if (grid_array[grid] != 0)
{
//raise "error"
invalid = true;
}
//if X turn
else if ((grid_array[grid] == 0) && (shape == 1))
{
if ((grid_array[grid] == 0))
{
//fill region
grid_array[grid] = 2;
shape = (!shape);
}
else if (grid_array[grid] != 0)
{
//raise "error"
invalid = true;
}
}
}
}
}
}
if (event.type == SDL_QUIT)
{
set_next_state(EXIT);
}
}
}
void playerOne::logic()
{
//check O win
for (int win = 0; win <= 6; win += 3)
{
if ((grid_array[win] == 1) && (grid_array[win + 1] == 1) && (grid_array[win + 2] == 1))
{
winner = 0;
set_next_state(O_win);
}
}
for (int win = 0; win < 3; win++)
{
if ((grid_array[win] == 1) && (grid_array[win + 3] == 1) && (grid_array[win + 6] == 1))
{
winner = 0;
set_next_state(O_win);
}
}
if ((grid_array[0] == 1) && (grid_array[4] == 1) && (grid_array[8] == 1))
{
winner = 0;
set_next_state(O_win);
}
if ((grid_array[2] == 1) && (grid_array[4] == 1) && (grid_array[6] == 1))
{
winner = 0;
set_next_state(O_win);
}
//check X's
for (int win = 0; win <= 6; win += 3)
{
if ((grid_array[win] == 2) && (grid_array[win + 1] == 2) && (grid_array[win + 2] == 2))
{
winner = 1;
set_next_state(X_win);
}
}
for (int win = 0; win < 3; win++)
{
if ((grid_array[win] == 2) && (grid_array[win + 3] == 2) && (grid_array[win + 6] == 2))
{
winner = 1;
set_next_state(X_win);
}
}
if ((grid_array[0] == 2) && (grid_array[4] == 2) && (grid_array[8] == 2))
{
winner = 1;
set_next_state(X_win);
}
if ((grid_array[2] == 2) && (grid_array[4] == 2) && (grid_array[6] == 2))
{
winner = 1;
set_next_state(X_win);
}
//check TIE
if ((grid_array[0] != 0) && (grid_array[1] != 0) && (grid_array[2] != 0) && (grid_array[3] != 0) && (grid_array[4] != 0) && (grid_array[5] != 0) && (grid_array[6] != 0) && (grid_array[7] != 0) && (grid_array[8] != 0) && (winner == NULL))
{
set_next_state(Tie);
}
}
void playerOne::render()
{
//logic
//rendering
//background
SDL_FillRect(screen, &screen->clip_rect, SDL_MapRGB(screen->format, 0xFF, 0xFF, 0xFF));
//grid
apply_surface(0, 0, sprites, screen, &sprite_clip[2]);
//highlight
if (highlight != -1)
{
apply_surface(grid_region[highlight].x, grid_region[highlight].y, sprites, screen, &sprite_clip[3]);
}
//APPLY PLAYER SHAPE
for (int grid = 0; grid < number_elements; grid++)
{
//O's
if ((grid_array[grid] == 1))
{
apply_surface(grid_region[grid].x + 7, grid_region[grid].y + 6, sprites, screen, &sprite_clip[0]);
}
else if ((grid_array[grid] == 2))
{
//X's
apply_surface(grid_region[grid].x + 7, grid_region[grid].y + 6, sprites, screen, &sprite_clip[1]);
}
}
}
playerTwo::playerTwo()
{
set_grid_regions();
}
playerTwo::~playerTwo()
{
}
void playerTwo::events()
{
//mouse offsets
int x = 0, y = 0;
//if mouse moves
while (SDL_PollEvent(&event))
{
if (event.type == SDL_MOUSEMOTION)
{
//get the mouse co-ords
x = event.motion.x;
y = event.motion.y;
for (int grid = 0; grid < number_elements; grid++)
{
if ((x > grid_region[grid].x) && (x < grid_region[grid].x + GRID_WIDTH) && (y > grid_region[grid].y) && (y < grid_region[grid].y + GRID_HEIGHT))
{
//set highlight region
highlight = grid;
}
}
}
//when the player clicks on a grid_region
if (event.type == SDL_MOUSEBUTTONDOWN)
{
//mouse co-ordinates
x = event.motion.x;
y = event.motion.y;
if (event.button.button == SDL_BUTTON_LEFT)
{
//iterate
for (int grid = 0; grid < number_elements; grid++)
{
//if in region box
if ((x > grid_region[grid].x) && (x < grid_region[grid].x + GRID_WIDTH) && (y > grid_region[grid].y) && (y < grid_region[grid].y + GRID_HEIGHT))
{
//check region
//if O turn
if ((grid_array[grid] == 0) && (shape == 0))
{
//fill region
grid_array[grid] = 1;
shape = (!shape);
}
else if (grid_array[grid] != 0)
{
//raise "error"
invalid = true;
}
//if X turn
else if ((grid_array[grid] == 0) && (shape == 1))
{
if ((grid_array[grid] == 0))
{
//fill region
grid_array[grid] = 2;
shape = (!shape);
}
else if (grid_array[grid] != 0)
{
//raise "error"
invalid = true;
}
}
}
}
}
}
if (event.type == SDL_QUIT)
{
set_next_state(EXIT);
}
}
}
void playerTwo::logic()
{
//check O win
for (int win = 0; win <= 6; win += 3)
{
if ((grid_array[win] == 1) && (grid_array[win + 1] == 1) && (grid_array[win + 2] == 1))
{
winner = 0;
set_next_state(O_win);
}
}
for (int win = 0; win < 3; win++)
{
if ((grid_array[win] == 1) && (grid_array[win + 3] == 1) && (grid_array[win + 6] == 1))
{
winner = 0;
set_next_state(O_win);
}
}
if ((grid_array[0] == 1) && (grid_array[4] == 1) && (grid_array[8] == 1))
{
winner = 0;
set_next_state(O_win);
}
if ((grid_array[2] == 1) && (grid_array[4] == 1) && (grid_array[6] == 1))
{
winner = 0;
set_next_state(O_win);
}
//check X's
for (int win = 0; win <= 6; win += 3)
{
if ((grid_array[win] == 2) && (grid_array[win + 1] == 2) && (grid_array[win + 2] == 2))
{
winner = 1;
set_next_state(X_win);
}
}
for (int win = 0; win < 3; win++)
{
if ((grid_array[win] == 2) && (grid_array[win + 3] == 2) && (grid_array[win + 6] == 2))
{
winner = 1;
set_next_state(X_win);
}
}
if ((grid_array[0] == 2) && (grid_array[4] == 2) && (grid_array[8] == 2))
{
winner = 1;
set_next_state(X_win);
}
if ((grid_array[2] == 2) && (grid_array[4] == 2) && (grid_array[6] == 2))
{
winner = 1;
set_next_state(X_win);
}
//check TIE
if ((grid_array[0] != 0) && (grid_array[1] != 0) && (grid_array[2] != 0) && (grid_array[3] != 0) && (grid_array[4] != 0) && (grid_array[5] != 0) && (grid_array[6] != 0) && (grid_array[7] != 0) && (grid_array[8] != 0) && (winner == NULL))
{
set_next_state(Tie);
}
}
void playerTwo::render()
{
//logic
//rendering
//background
SDL_FillRect(screen, &screen->clip_rect, SDL_MapRGB(screen->format, 0xFF, 0xFF, 0xFF));
//grid
apply_surface(0, 0, sprites, screen, &sprite_clip[2]);
//highlight
if (highlight != -1)
{
apply_surface(grid_region[highlight].x, grid_region[highlight].y, sprites, screen, &sprite_clip[3]);
}
//APPLY PLAYER SHAPE
for (int grid = 0; grid < number_elements; grid++)
{
//O's
if ((grid_array[grid] == 1))
{
apply_surface(grid_region[grid].x + 7, grid_region[grid].y + 6, sprites, screen, &sprite_clip[0]);
}
else if ((grid_array[grid] == 2))
{
//X's
apply_surface(grid_region[grid].x + 7, grid_region[grid].y + 6, sprites, screen, &sprite_clip[1]);
}
}
}
win::win(int winner)
{
shape_winner = winner;
font = TTF_OpenFont("font.ttf", 45);
X_win = TTF_RenderText_Solid(font, "X WINS", win_Color);
O_win = TTF_RenderText_Solid(font, "O wins", win_Color);
Tie = TTF_RenderText_Solid(font, "Tie", win_Color);
}
win::~win()
{
TTF_CloseFont(font);
SDL_FreeSurface(X_win);
SDL_FreeSurface(O_win);
}
void win::events()
{
while (SDL_PollEvent(&event))
{
if (event.type == SDL_QUIT)
{
set_next_state(EXIT);
}
}
}
void win::logic()
{
if (shape_winner == 3)
{
SDL_Delay(2000);
set_next_state(CHOICE);
winner = NULL;
}
}
void win::render()
{
//background
SDL_FillRect(screen, &screen->clip_rect, SDL_MapRGB(screen->format, 0xFF, 0xFF, 0xFF));
//grid
apply_surface(0, 0, sprites, screen, &sprite_clip[2]);
//highlight
if (highlight != -1)
{
apply_surface(grid_region[highlight].x, grid_region[highlight].y, sprites, screen, &sprite_clip[3]);
}
//APPLY PLAYER SHAPE
for (int grid = 0; grid < number_elements; grid++)
{
//O's
if ((grid_array[grid] == 1))
{
apply_surface(grid_region[grid].x + 7, grid_region[grid].y + 6, sprites, screen, &sprite_clip[0]);
}
else if ((grid_array[grid] == 2))
{
//X's
apply_surface(grid_region[grid].x + 7, grid_region[grid].y + 6, sprites, screen, &sprite_clip[1]);
}
}
if (shape_winner == 1)
{
apply_surface((SCREEN_WIDTH - X_win->w) / 2, (SCREEN_HEIGHT - X_win->h) / 2, X_win, screen);
//enable delay and reset
shape_winner = 3;
}
if (shape_winner == 0)
{
apply_surface((SCREEN_WIDTH - O_win->w) / 2, (SCREEN_HEIGHT - O_win->h) / 2, O_win, screen);
//enable delay and reset
shape_winner = 3;
}
if (shape_winner == 2)
{
apply_surface((SCREEN_WIDTH - Tie->w) / 2, (SCREEN_HEIGHT - Tie->h) / 2, Tie, screen);
//enable delay and reset
shape_winner = 3;
}
}
Exit::Exit()
{
}
Exit::~Exit()
{
}
void Exit::events()
{
}
void Exit::logic()
{
}
void Exit::render()
{
}
//MAAAAAAAAAAAAAAAAAAIN//
int main(int argc, char* args[])
{
//init SDL
init();
//load files
load_files();
//set clips
set_clip_regions();
//set state
stateID = INTRO;
//set game object
currentState = new intro();
while (stateID != EXIT)
{
//handle state events
currentState->events();
// do state logic
currentState->logic();
//change state if needed
change_state();
//render state
currentState->render();
if (SDL_Flip(screen) == -1)
{
return 1;
}
}
clean_up();
return 0;
}
It's pretty strange. But I'm 99% sure that it's the "set_grid_regions()" that is effecting the rendering inside the choice::render() or choice::event() class fucntions. Can anyone help with this?
The error causing the clipping problem is an incorrect declaration for sprite_clip. You've declared it as sprite_clip[10], but you have 13 sprites. Change this to sprite_clip[13].
Other things I noticed:
You allocate all these state objects with new, but you never delete them. You need to delete them when you change states. Otherwise you will leak memory.
The font font.ttf seems to be a global resource but is haphazardly managed by a mixture of global and local methods. Load it once in load_files() and release it once in clean_up().
While your project may be complete, you might tinker around with it and work on improving the implementation now that you have something working. (If this was a homework, keep a copy of a working version to hand in. ;-) )
Consider packaging all of your global state (your fonts, sprites, clipping arrays, etc.) into a single class for the game. Your init_xxx and load_xxx functions move to its constructor. Your clean_up moves to its destructor.
Consider converting your dynamic state objects into statically allocated state objects. They could even be members of the same global state class I mentioned in the previous bullet. Now you've encapsulated the whole game in one class.
Consider merging playerOne and playerTwo into a single class. Distinguish them at construction time, just as you did with win(0), win(1), win(2).
Related
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 15 hours ago.
Improve this question
I am making a Cpp sdl game which launches as it should and is playable but only for about 7 second as after that the screen goes black. If I keep the game running with black screen it eventually shows LLVM ERROR: out of memory. While using local windows debugger in Visual Studio I get:
Exception thrown at 0x71002A85 (SDL2_ttf.dll) in game.exe: 0xC0000005: Access violation reading location 0x00000000
and/or
Unhandled exception at 0x7A6C3ED8 (nvoglv32.dll) in game.exe: Fatal program exit requested
Not sure if this information is of any use but changing SDL_Delay() from 1 to higher value e.g. 100, extends the amount of time it takes before the screen goes black. Also changing the SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED) from '-1' to '1' extends the time it takes for the game to go black screen.
I tried different versions of SDL libraries and I don't think its a hardware issue. I used Visual Studio performance profiler and the CPU and GPU usage was normal. Performance profiler did show a 1.8 GB for process memory which did seem quite high, perhaps it's because of inefficient code(?).
I included some of the code and a snapshot.
main.cpp:
int main(int argc, char **argv)
{
/* initialize SDL */
if ( SDL_Init( SDL_INIT_VIDEO ) < 0 )
{
assert(0 && "Failed to initialize video!");
exit(-1);
}
SDL_Window* window = SDL_CreateWindow("Pacman", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 1024, 768, SDL_WINDOW_OPENGL);
SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); //changing from -1 to 1 increase the amout of time the app runs before black screen
if(!window)
{
assert(0 && "Failed to create window!");
exit(-1);
}
IMG_Init(IMG_INIT_JPG | IMG_INIT_PNG);
if (TTF_Init() == -1)
{
assert(0 && "Failed to create ttf!");
exit(-1);
}
Drawer* drawer = Drawer::Create(window, renderer);
Pacman* pacman = Pacman::Create(drawer);
float lastFrame = (float) SDL_GetTicks() * 0.001f;
SDL_Event event;
while (SDL_PollEvent(&event) >= 0)
{
float currentFrame = (float) SDL_GetTicks() * 0.001f;
float elapsedTime = currentFrame - lastFrame;
if (!pacman->Update(elapsedTime))
break;
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderClear(renderer);
pacman->Draw();
lastFrame = currentFrame;
SDL_RenderPresent(renderer);
SDL_Delay(1);
}
delete pacman;
delete drawer;
TTF_Quit();
IMG_Quit();
SDL_Quit( );
return 0;
}
Drawer.cpp:
Drawer* Drawer::Create(SDL_Window* aWindow, SDL_Renderer* aRenderer)
{
Drawer* drawer = new Drawer(aWindow, aRenderer);
if (!drawer->Init())
{
delete drawer;
drawer = NULL;
}
return drawer;
}
Drawer::Drawer(SDL_Window* aWindow, SDL_Renderer* aRenderer)
: myWindow(aWindow)
, myRenderer(aRenderer)
, world()
{
}
Drawer::~Drawer(void)
{
}
bool Drawer::Init()
{
if (!myWindow)
return false;
return true;
}
void Drawer::Draw(const char* anImage, int aCellX, int aCellY)
{
SDL_Surface* surface = IMG_Load( anImage ) ;
if (!surface)
return;
SDL_Texture* optimizedSurface = SDL_CreateTextureFromSurface(myRenderer, surface);
SDL_Rect sizeRect;
sizeRect.x = 0 ;
sizeRect.y = 0 ;
sizeRect.w = surface->w ;
sizeRect.h = surface->h ;
SDL_Rect posRect;
posRect.x = aCellX;
posRect.y = aCellY;
posRect.w = sizeRect.w;
posRect.h = sizeRect.h;
SDL_RenderCopy(myRenderer, optimizedSurface, &sizeRect, &posRect);
}
void Drawer::DrawText(const char* aText, const char* aFontFile, int aX, int aY)
{
TTF_Font* font = TTF_OpenFont(aFontFile, 24);
if (font == nullptr) {
// Handle font loading error here (e.g. log an error message, throw an exception, etc.)
return;
}
SDL_Color fg = { 255, 0, 0, 255 };
SDL_Surface* surface = TTF_RenderText_Solid(font, aText, fg);
SDL_Texture* optimizedSurface = SDL_CreateTextureFromSurface(myRenderer, surface);
SDL_Rect sizeRect;
sizeRect.x = 0;
sizeRect.y = 0;
sizeRect.w = surface->w;
sizeRect.h = surface->h;
SDL_Rect posRect;
posRect.x = aX;
posRect.y = aY;
posRect.w = sizeRect.w;
posRect.h = sizeRect.h;
SDL_RenderCopy(myRenderer, optimizedSurface, &sizeRect, &posRect);
SDL_DestroyTexture(optimizedSurface);
SDL_FreeSurface(surface);
TTF_CloseFont(font);
}
world.cpp:
World::World(void)
{
}
World::~World()
{
for (auto tile : myPathmapTiles)
{
delete tile;
}
for (auto dot : myDots)
{
delete dot;
}
for (auto bigDot : myBigDots)
{
delete bigDot;
}
}
void World::Init()
{
InitPathmap();
InitDots();
InitBigDots();
}
bool World::InitPathmap()
{
std::string line;
std::ifstream myfile ("map.txt");
if (myfile.is_open())
{
int lineIndex = 0;
while (! myfile.eof() )
{
std::getline (myfile,line);
for (unsigned int i = 0; i < line.length(); i++)
{
PathmapTile* tile = new PathmapTile(i, lineIndex, (line[i] == 'x'));
myPathmapTiles.push_back(tile);
}
lineIndex++;
}
myfile.close();
}
return true;
}
bool World::InitDots()
{
std::string line;
std::ifstream myfile ("map.txt");
if (myfile.is_open())
{
int lineIndex = 0;
while (! myfile.eof() )
{
std::getline (myfile,line);
for (unsigned int i = 0; i < line.length(); i++)
{
if (line[i] == '.')
{
Dot* dot = new Dot(Vector2f(i * 22.0f, lineIndex * 22.0f));
myDots.push_back(dot);
}
}
lineIndex++;
}
myfile.close();
}
return true;
}
bool World::InitBigDots()
{
std::string line;
std::ifstream myfile ("map.txt");
if (myfile.is_open())
{
int lineIndex = 0;
while (! myfile.eof() )
{
std::getline (myfile,line);
for (unsigned int i = 0; i < line.length(); i++)
{
if (line[i] == 'o')
{
BigDot* dot = new BigDot(Vector2f(i * 22.0f, lineIndex * 22.0f));
myBigDots.push_back(dot);
}
}
lineIndex++;
}
myfile.close();
}
return true;
}
void World::Draw(Drawer* aDrawer)
{
aDrawer->Draw("playfield.png");
for(std::list<Dot*>::iterator list_iter = myDots.begin(); list_iter != myDots.end(); list_iter++)
{
Dot* dot = *list_iter;
dot->Draw(aDrawer);
}
for(std::list<BigDot*>::iterator list_iter = myBigDots.begin(); list_iter != myBigDots.end(); list_iter++)
{
BigDot* dot = *list_iter;
dot->Draw(aDrawer);
}
}
bool World::TileIsValid(int anX, int anY)
{
for(std::list<PathmapTile*>::iterator list_iter = myPathmapTiles.begin(); list_iter != myPathmapTiles.end(); list_iter++)
{
PathmapTile* tile = *list_iter;
if (anX == tile->myX && anY == tile->myY && !tile->myIsBlockingFlag)
return true;
}
return false;
}
bool World::HasIntersectedDot(const Vector2f& aPosition)
{
for(std::list<Dot*>::iterator list_iter = myDots.begin(); list_iter != myDots.end(); list_iter++)
{
Dot* dot = *list_iter;
if ((dot->GetPosition() - aPosition).Length() < 5.f)
{
myDots.remove(dot);
delete dot;
return true;
}
}
return false;
}
bool World::HasIntersectedBigDot(const Vector2f& aPosition)
{
for(std::list<BigDot*>::iterator list_iter = myBigDots.begin(); list_iter != myBigDots.end(); list_iter++)
{
BigDot* dot = *list_iter;
if ((dot->GetPosition() - aPosition).Length() < 5.f)
{
myBigDots.remove(dot);
delete dot;
return true;
}
}
return false;
}
bool World::HasIntersectedCherry(const Vector2f& aPosition)
{
return true;
}
void World::GetPath(int aFromX, int aFromY, int aToX, int aToY, std::list<PathmapTile*>& aList)
{
PathmapTile* fromTile = GetTile(aFromX, aFromY);
PathmapTile* toTile = GetTile(aToX, aToY);
for(std::list<PathmapTile*>::iterator list_iter = myPathmapTiles.begin(); list_iter != myPathmapTiles.end(); list_iter++)
{
PathmapTile* tile = *list_iter;
tile->myIsVisitedFlag = false;
}
Pathfind(fromTile, toTile, aList);
}
PathmapTile* World::GetTile(int aFromX, int aFromY)
{
for(std::list<PathmapTile*>::iterator list_iter = myPathmapTiles.begin(); list_iter != myPathmapTiles.end(); list_iter++)
{
PathmapTile* tile = *list_iter;
if (tile->myX == aFromX && tile->myY == aFromY)
{
return tile;
}
}
return NULL;
}
bool World::ListDoesNotContain(PathmapTile* aFromTile, std::list<PathmapTile*>& aList)
{
for(std::list<PathmapTile*>::iterator list_iter = aList.begin(); list_iter != aList.end(); list_iter++)
{
PathmapTile* tile = *list_iter;
if (tile == aFromTile)
{
return false;
}
}
return true;
}
bool SortFromGhostSpawn(PathmapTile* a, PathmapTile* b)
{
int la = abs(a->myX - 13) + abs(a->myY - 13);
int lb = abs(b->myX - 13) + abs(b->myY - 13);
return la < lb;
}
bool World::Pathfind(PathmapTile* aFromTile, PathmapTile* aToTile, std::list<PathmapTile*>& aList)
{
aFromTile->myIsVisitedFlag = true;
if (aFromTile->myIsBlockingFlag)
return false;
if (aFromTile == aToTile)
return true;
std::list<PathmapTile*> neighborList;
PathmapTile* up = GetTile(aFromTile->myX, aFromTile->myY - 1);
if (up && !up->myIsVisitedFlag && !up->myIsBlockingFlag && ListDoesNotContain(up, aList))
neighborList.push_front(up);
PathmapTile* down = GetTile(aFromTile->myX, aFromTile->myY + 1);
if (down && !down->myIsVisitedFlag && !down->myIsBlockingFlag && ListDoesNotContain(down, aList))
neighborList.push_front(down);
PathmapTile* right = GetTile(aFromTile->myX + 1, aFromTile->myY);
if (right && !right->myIsVisitedFlag && !right->myIsBlockingFlag && ListDoesNotContain(right, aList))
neighborList.push_front(right);
PathmapTile* left = GetTile(aFromTile->myX - 1, aFromTile->myY);
if (left && !left->myIsVisitedFlag && !left->myIsBlockingFlag && ListDoesNotContain(left, aList))
neighborList.push_front(left);
neighborList.sort(SortFromGhostSpawn);
for(std::list<PathmapTile*>::iterator list_iter = neighborList.begin(); list_iter != neighborList.end(); list_iter++)
{
PathmapTile* tile = *list_iter;
aList.push_back(tile);
if (Pathfind(tile, aToTile, aList))
return true;
aList.pop_back();
}
return false;
}
Snapshot:
Snapshot
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]);
}
Ok so this week I have put a lot of time into learning how to open and run multiple sfml widows at once. I have a vector of unique pointers to the windows and a for loop that displays them and processes their events. The issue I am having that I can't think of a way to fix is that the for loop also runs a gravity function. As more and more windows open the keys drop at slower rates. What is something I can do to make sure that they are always dropping at a somewhat consistent rate no matter how many windows I am processing. For reference here is my code:
MakeKey.h
class MakeKey
{
public:
class NewKey {
public:
sf::Image Img;
sf::Texture Tex;
sf::Sprite Sprite;
int velocetyX;
int velocetyY;
int accelerationX;
int accelerationY;
static bool hitLeft(sf::RenderWindow * window, sf::Image image)
{
int XPos{ window->getPosition().x };
if (XPos <= -40)
return true;
return false;
}
static bool hitRight(sf::RenderWindow * window, sf::Image image)
{
int XPos{ window->getPosition().x };
if (XPos >= sf::VideoMode::getDesktopMode().width - image.getSize().x + 30)
return true;
return false;
}
static bool hitTop(sf::RenderWindow * window, sf::Image image)
{
int YPos = window->getPosition().y;
if (YPos <= -22)
return true;
return false;
}
static bool hitBottom(sf::RenderWindow * window, sf::Image image)
{
int YPos = window->getPosition().y;
if (YPos >= sf::VideoMode::getDesktopMode().height - image.getSize().y + 20)
return true;
return false;
}
static void impact(int & velocity)
{
if (velocity > 0) {
velocity -= 6;
velocity *= -1;
}
if (velocity < 0) {
velocity += 6;
velocity *= -1;
}
}
sf::Clock OffGroundClock;
};
sf::Vector2i Gravity(MakeKey::NewKey& Key, sf::RenderWindow* window);
bool setShape(sf::RenderWindow * window, const sf::Image& image);
sf::Vector2i RandSpawn(sf::Image image);
bool setTransparency(HWND hwnd, unsigned char alpha);
void MakeTopWindow(sf::RenderWindow* window);
void StepWindows();
void RunEvents(sf::RenderWindow* window);
void DrawKey(string input);
private:
vector <MakeKey::NewKey> KeyArray;
vector <unique_ptr <sf::RenderWindow>> WindowArray;
bool grabbedWindow{ false };
sf::Vector2i grabbedOffSet;
};
MakeKey.cpp
static void StepVelocity(MakeKey::NewKey* Key) {
Key->velocetyX += Key->accelerationX;
Key->velocetyY += Key->accelerationY;
}
sf::Vector2i MakeKey::Gravity(MakeKey::NewKey & Key, sf::RenderWindow * window)
{
int XPos = window->getPosition().x;
int YPos = window->getPosition().y;
YPos += 10;
if (Key.hitTop(window, Key.Img))
YPos = -22;
if (Key.hitBottom(window, Key.Img))
YPos = sf::VideoMode::getDesktopMode().height - Key.Img.getSize().y + 20;
if (Key.hitRight(window, Key.Img))
XPos = sf::VideoMode::getDesktopMode().width - Key.Img.getSize().x + 30;
if (Key.hitLeft(window, Key.Img))
XPos = (-44);
/*StepVelocity(Key);
XPos += Key->velocetyX;
YPos += Key->velocetyY;*/
return sf::Vector2i(XPos, YPos);
}
bool MakeKey::setShape(sf::RenderWindow * window, const sf::Image& image)
{
HWND hwnd = window->getSystemHandle();
const sf::Uint8* pixelData = image.getPixelsPtr();
HRGN hRegion = CreateRectRgn(0, 0, image.getSize().x, image.getSize().y);
// Determine the visible region
for (unsigned int y = 0; y < image.getSize().y; y++)
{
for (unsigned int x = 0; x < image.getSize().x; x++)
{
if (pixelData[y * image.getSize().x * 4 + x * 4 + 3] == 0)
{
HRGN hRegionPixel = CreateRectRgn(x, y, x + 1, y + 1);
CombineRgn(hRegion, hRegion, hRegionPixel, RGN_XOR);
DeleteObject(hRegionPixel);
}
}
}
SetWindowRgn(hwnd, hRegion, true);
DeleteObject(hRegion);
return true;
}
sf::Vector2i MakeKey::RandSpawn(sf::Image image)
{
std::random_device rand;
int RandX = (rand() % sf::VideoMode::getDesktopMode().width) - image.getSize().x;
int RandY = (rand() % sf::VideoMode::getDesktopMode().height) - image.getSize().y;
if (RandX < 1 + image.getSize().x)
RandX = image.getSize().x;
if (RandY < 1 + image.getSize().y)
RandY = image.getSize().y;
return sf::Vector2i(RandX, RandY);
}
bool MakeKey::setTransparency(HWND hwnd, unsigned char alpha)
{
SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED);
SetLayeredWindowAttributes(hwnd, 0, alpha, LWA_ALPHA);
return true;
}
void MakeKey::MakeTopWindow(sf::RenderWindow* window)
{
HWND hwndPoint = window->getSystemHandle();
SetWindowPos(hwndPoint, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
}
void MakeKey::RunEvents(sf::RenderWindow* window) {
sf::Event event;
while (window->pollEvent(event))
{
//Key Presses
if (event.type == sf::Event::KeyPressed) {
if (event.key.code == sf::Keyboard::A) {
DrawKey("A");
}
else if (event.key.code == sf::Keyboard::D)
{
DrawKey("D");
}
else if (event.key.code == sf::Keyboard::E)
{
DrawKey("E");
}
else if (event.key.code == sf::Keyboard::Q)
{
DrawKey("Q");
}
else if (event.key.code == sf::Keyboard::S)
{
DrawKey("S");
}
else if (event.key.code == sf::Keyboard::W)
{
DrawKey("W");
}
else if (event.key.code == sf::Keyboard::X)
{
DrawKey("X");
}
else if (event.key.code == sf::Keyboard::Z)
{
DrawKey("Z");
}
else if (event.key.code == sf::Keyboard::Escape)
{
DrawKey("Esc");
}
}
else if (event.type == sf::Event::MouseButtonPressed)
{
if (event.mouseButton.button == sf::Mouse::Left)
{
grabbedOffSet = window->getPosition() - sf::Mouse::getPosition();
grabbedWindow = true;
}
}
else if (event.type == sf::Event::MouseButtonReleased)
{
if (event.mouseButton.button == sf::Mouse::Left)
grabbedWindow = false;
}
else if (event.type == sf::Event::MouseMoved)
{
if (grabbedWindow)
window->setPosition(sf::Mouse::getPosition() + grabbedOffSet);
}
}
}
void MakeKey::StepWindows()
{
for (int i{ 0 }; i < WindowArray.size(); i++)
{
cout << "Inside Step Windows For Loop" << endl;
WindowArray[i]->setActive(true);
MakeTopWindow(WindowArray[i].get());
setShape(WindowArray[i].get(), KeyArray[i].Img);
RunEvents(WindowArray[i].get());
WindowArray[i]->clear(sf::Color::Transparent);
KeyArray[i].Sprite.setTexture(KeyArray[i].Tex);
WindowArray[i]->draw(KeyArray[i].Sprite);
WindowArray[i]->display();
WindowArray[i]->setPosition(Gravity(KeyArray[i], WindowArray[i].get()));
}
}
void MakeKey::DrawKey(string input)
{
unique_ptr <sf::RenderWindow> window = make_unique<sf::RenderWindow>();
NewKey Key;
if (input == "A")
Key.Img.loadFromFile("Assets/Images/A.png");
else if (input == "D")
Key.Img.loadFromFile("Assets/Images/D.png");
else if (input == "E")
Key.Img.loadFromFile("Assets/Images/E.png");
else if (input == "Q")
Key.Img.loadFromFile("Assets/Images/Q.png");
else if (input == "S")
Key.Img.loadFromFile("Assets/Images/S.png");
else if (input == "W")
Key.Img.loadFromFile("Assets/Images/W.png");
else if (input == "X")
Key.Img.loadFromFile("Assets/Images/X.png");
else if (input == "Z")
Key.Img.loadFromFile("Assets/Images/Z.png");
else if (input == "Esc")
Key.Img.loadFromFile("Assets/Images/esc.png");
window->create(sf::VideoMode(Key.Img.getSize().x, Key.Img.getSize().y, 32), "Key", sf::Style::None);
Key.Tex.loadFromImage(Key.Img);
Key.Sprite.setTexture(Key.Tex);
window->setPosition(RandSpawn(Key.Img));
//Make Transparent
const unsigned char opacity = 1000;
setTransparency(window->getSystemHandle(), opacity);
KeyArray.emplace_back(move(Key));
WindowArray.emplace_back(move(window));
}
Main
int main()
{
sf::RenderWindow window(sf::VideoMode(100, 100, 32), "Main Window", sf::Style::Default);
MakeKey MakeKey;
while (window.isOpen())
{
MakeKey::NewKey Key;
MakeKey.RunEvents(&window);
MakeKey.StepWindows();
}
return EXIT_SUCCESS;
}
So I was determined to get collision working myself from online resources without asking for help & I successfully followed a tutorial on lazyfoo to get collision working but quickly realised other problems.
So I started trying to get my collision working on my player (just a rect with controls atm) but couldn't actually access the rect inside the player class, I had to initialize it in main & use that for collision which meant transferring my collision function to main (not exactly perfect).
My question: So my collision is limited to two rects which I dedicated to the player & a single asteroid but my idea was to create multiple asteroids coming from the top of the screen, how would I go about altering the below code to accept a vector of "asteroids". Any advice about altering my code to be better object oriented etc is more then welcome but my main problem is collision. Please read collision carefully before posting, the specific X and Y params from the .cpp provides are hardcoded there.
Below Code order:
My current way of defining both player & asteroid for collision
Collision function
Asteroid.cpp & Player.cpp
My new way of loading asteroids - This is what I need collision to work with
Player aPlayer(200, 50, 50, 50);
Asteroid oneAsteroid(200, -50, 50, 50);
bool check_collision(SDL_Rect aPlayer, SDL_Rect oneAsteroid) {
//The sides of the rectangles
int leftA, leftB;
int rightA, rightB;
int topA, topB;
int bottomA, bottomB;
//Calculate the sides of rect A
leftA = aPlayer.X;
rightA = aPlayer.X + aPlayer.width;
topA = aPlayer.Y;
bottomA = aPlayer.Y + aPlayer.height;
//Calculate the sides of rect B
leftB = oneAsteroid.X;
rightB = oneAsteroid.X + oneAsteroid.width;
topB = oneAsteroid.Y;
bottomB = oneAsteroid.Y + oneAsteroid.height;
if (bottomA <= topB)
{
return false;
}
if (topA >= bottomB)
{
return false;
}
if (rightA <= leftB)
{
return false;
}
if (leftA >= rightB)
{
return false;
}
//If none of the sides from A are outside B
return true;
}
#include "asteroids.h"
Asteroid::Asteroid()
{
}
Asteroid::~Asteroid()
{
}
Asteroid::Asteroid(int x, int y, int w, int h)
{
X = x; Y = y; width = w; height = h;
//SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, "Square Constructed with Param(&p)", this);
}
void Asteroid::Render(SDL_Renderer * aRenderer)
{
printf("");
SDL_Rect* aAsteroid = new SDL_Rect();
aAsteroid->x = X; aAsteroid->y = Y; aAsteroid->w = width; aAsteroid->h = height;
SDL_SetRenderDrawColor(aRenderer, 0, 0, 0, 255);
SDL_RenderFillRect(aRenderer, aAsteroid);
}
void Asteroid::Init()
{
velocity.X = 0;
velocity.Y = 5;
}
void Asteroid::Update()
{
Y = Y + velocity.Y;
// printf("%d \n", Astero);
if (Y > 650) {
Y = -50;
}
}
#include "Player.h"
#include "asteroids.h"
using namespace std;
Player::Player()
{
}
Player::~Player()
{
SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, "Player Destroyed (&p)", this);
}
Player::Player(int x, int y, int w, int h)
{
X = x; Y = y; width = w; height = h;
//SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, "Square Constructed with Param(&p)", this);
}
void Player::Init()
{
velocity.X = 10;
velocity.Y = 10;
}
void Player::Update()
{
Y = Y + floorStart;
if (X < 0) {
X = 0;
}
if (KEY_RIGHT == true) {
X++;
}
if (X > SCREEN_WIDTH - 50) {
X = SCREEN_WIDTH - 50;
}
if (Y > SCREEN_HEIGHT - 100) {
Y = SCREEN_HEIGHT - 100;
}
if (KEY_LEFT == true) {
X--;
}
}
void Player::Input(SDL_Event event)
{
if (event.type == SDL_KEYDOWN && event.key.repeat == NULL)
{
if (event.key.keysym.sym == SDLK_LEFT) {
KEY_LEFT = true;
}
else {
KEY_LEFT = false;
}
if (event.key.keysym.sym == SDLK_RIGHT) {
KEY_RIGHT = true;
}
else {
KEY_RIGHT = false;
}
}
if (event.type == SDL_KEYUP && event.key.repeat == NULL) {
if (event.key.keysym.sym == SDLK_LEFT) {
KEY_LEFT = false;
}
if (event.key.keysym.sym == SDLK_RIGHT) {
KEY_RIGHT = false;
}
}
}
void Player::Render(SDL_Renderer * aRenderer)
{
SDL_Rect* thePlayer = new SDL_Rect;
thePlayer->x = X; thePlayer->y = Y; thePlayer->w = width; thePlayer->h = height;
SDL_SetRenderDrawColor(aRenderer, 0, 0, 0, 255);
SDL_RenderFillRect(aRenderer, thePlayer);
//SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, "Rendering (&p)", this);
}
What I need collision to work with, a vector of asteroids:
std::vector<Asteroid*> asteroidList;
asteroidList.push_back(new Asteroid(150, 350, 50, 50));
asteroidList.push_back(new Asteroid(70, 120, 125, 125));
The function to check collision in main.cpp
if (check_collision(aPlayer.thePlayer, oneAsteroid.aAsteroid)) {
printf("#######################");
}
And lastly, how would I go about rendering the array of asteroids?
I have some SFML 2.0 code, where I draw a robot which moves in a grid. Grid is drawn using OpenGL, the robot image is loaded using sf::Texture. I have some code that makes walls on user left mouse click (no collision detection). I made a function which erases them on right click.
Walls are stored in sf::Sprite, then put into std::list and drawn in a loop. When I call list.erase() program segfaults. Debugger shows some problem in sf::transformable = operator.
How to fix that.
Here is the code:
#include <SFML/Graphics.hpp>
#include <SFML/OpenGL.hpp>
#include <iostream>
#include <list>
using namespace std;
static const size_t WIN_HEIGHT=800, WIN_WIDTH=800;
void drawGrid();
void fixCoords(int & x, int & y);
static list<sf::Sprite> walls;
int main()
{
// Create the main window
sf::RenderWindow window(sf::VideoMode(WIN_WIDTH, WIN_HEIGHT), "SFML window");
/*** Robot code ***/
sf::Image robotImg;
robotImg.loadFromFile("robot.png");
robotImg.createMaskFromColor(sf::Color(89, 167, 45));
sf::Texture robotTexture;
robotTexture.loadFromImage(robotImg);
sf::Sprite robotSpr(robotTexture);
sf::Sprite t;
robotSpr.setPosition(sf::Vector2f(400, 405));
/***** Wall code ****/
int x = 0, y = 0;
sf::Image wallimg;
wallimg.loadFromFile("wall.png");
wallimg.createMaskFromColor(sf::Color(255, 0, 255));
sf::Texture walltex;
walltex.loadFromImage(wallimg);
sf::Sprite wall;
wall.setTexture(walltex);
int movex = 0, movey = 0;
gluOrtho2D(0, WIN_WIDTH, 0, WIN_HEIGHT);
while (window.isOpen())
{
// Process events
sf::Event event;
while (window.pollEvent(event))
{
// Close window : exit
if (event.type == sf::Event::Closed)
{
window.close();
return 0;
}
if (event.type == sf::Event::MouseButtonPressed )
{
if(event.mouseButton.button == sf::Mouse::Left)
{
x = event.mouseButton.x;
y = event.mouseButton.y;
fixCoords(x, y);
wall.setPosition(sf::Vector2f(x, y));
walls.push_back(wall);
}
if(event.mouseButton.button == sf::Mouse::Right)
{
x = event.mouseButton.x;
y = event.mouseButton.y;
fixCoords(x, y);
for(list<sf::Sprite>::iterator it = walls.begin(); it != walls.end(); it++)
{
if((it->getPosition().x == x) && (it->getPosition().y == y)) // This line
walls.erase(it);
}
}
}
if(event.type == sf::Event::KeyPressed)
{
if((movex == 0) && (movey == 0))
{
if(event.key.code == sf::Keyboard::Up)
movey -= 37;
if(event.key.code == sf::Keyboard::Down)
movey += 37;
if(event.key.code == sf::Keyboard::Left)
movex -= 40;
if(event.key.code == sf::Keyboard::Right)
movex += 40;
}
}
}
window.pushGLStates();
window.clear(sf::Color(90, 167, 45));
// Insert SFML Draws here
if(movex > 0)
{
robotSpr.move(1, 0);
movex--;
}
if(movex < 0)
{
robotSpr.move(-1, 0);
movex++;
}
if(movey > 0)
{
robotSpr.move(0, 1);
movey--;
}
if(movey < 0)
{
robotSpr.move(0, -1);
movey++;
}
window.draw(robotSpr);
if((x != 0) && (y != 0))
{
for(list<sf::Sprite>::iterator it = walls.begin(); it != walls.end(); it++)
window.draw(*it);
}
window.popGLStates();
// OpenGL Here
drawGrid();
// Update the window
window.display();
}
}
void drawGrid()
{
glColor3ub(0, 0, 0);
glBegin(GL_LINES); // Horizontal lines
for(int i = 0; i < WIN_HEIGHT; i += WIN_HEIGHT / 20)
{
glVertex2i(0, i);
glVertex2i(WIN_WIDTH, i);
}
glEnd();
glColor3ub(0, 0, 0);
glBegin(GL_LINES); // Vertical lines
for(int i = 0; i < WIN_WIDTH; i += WIN_WIDTH / 20)
{
glVertex2i(i, 0);
glVertex2i(i, WIN_HEIGHT);
}
glEnd();
}
void fixCoords(int &x, int &y)
{
/**** Find the nearest x sqare ****/
for(int i = 1; i < WIN_WIDTH - 1; i += 40)
{
if((x >= i) && x <= (i + 40))
{
x = i - 1;
break;
}
}
for(int i = WIN_HEIGHT; i > 0; i -= 40)
{
if((y >= i) && y <= (i + 40))
{
y = i;
break;
}
}
}
This is an annoyance of the way the list<T> container works.
A list<T> container is implemented as a doubly linked list. So an iterator needs to access its current element to get to the next element. If you have just erased its current element, everything explodes.
You can make it work like this:
list<sf::Sprite>::iterator it=walls.begin(),next;
while(it!=walls.end()) {
next = it; next++;
if((it->getPosition().x == x) && (it->getPosition().y == y))
walls.erase(it);
it = next;
}
you could also use remove_if with an appropriate predicate class, but that would just be uglier and more convoluted.