I am a novice C++ programmer, and I have been experimenting with the SDL libraries for 2d graphics. I have been messing around with try to create some simple GUI controls; however, my button control is having a weird issue that I have been unable to resolve.
Whenever I launch the program in debug mode Visual Studio immediately gives me an error window saying
"Windows has triggered a breakpoint in Default_SDL_Setup.exe.
This may be due to a corruption of the heap, which indicates a bug in
Default_SDL_Setup.exe or any of the DLLs it has loaded.
This may also be due to the user pressing F12 while
Default_SDL_Setup.exe has focus.
The output window may have more diagnostic information."
and pops up a file called crtexe.c in the code editor window.
I have no idea what is wrong or what is going on.
Here is my button header:
#ifndef BUTTON_T
#define BUTTON_T
#include <vector>
#include <string>
#include "SDL.h"
#include "SDL_ttf.h"
#include "SDLFunctions.h"
#endif
class button
{
private:
//refreshes the button
void refresh_image();
bool refresh;
//surface to hold the created text
SDL_Surface *message;
//color of text
SDL_Color tC;
//the different box types
struct boxColor
{
unsigned int r;
unsigned int g;
unsigned int b;
};
boxColor boxes[4];
SDL_Rect rects[4];
//font of text
TTF_Font *f;
/*button's state
* States are:
* 0 - mouseout
* 1 - mouseover
* 2 - mousedown
* 3 - mouseup
*/
int state;
public:
//autosize based off of text?
bool autoSize;
//size of text
int textSize;
//x,y,w,and h for the box
int x, y, w, h;
//text of the label
std::string text;
button(int xpos, int ypos, int width, int height);
~button();
//handles all events for the button
void handleEvents(SDL_Event *event);
//sets text's font style off of a string location, color expects a 3 dimensional array
void setFont(std::string fontFileLocation);
void setFont(int color[3]);
void setFont(std::string fontFileLocation, int color[3]);
//gets the text color as a vector
std::vector<int> textColor();
//returns the color of the specified box
std::vector<int> boxColor(int boxnum);
//sets the color of the specified box, expects a 3 dimensional array
void setBoxColor(int boxnum, int color[3]);
void render(SDL_Surface *screen);
};
Here is the button code:
#include "button.h"
using namespace std;
void button::refresh_image()
{
if (refresh)
{
if (f != NULL)
{
message = TTF_RenderText_Solid(f, text.c_str(), tC);
if ((message != NULL) && autoSize)
{
if (message->w >= w)
{
w = (message->w + 10);
}
if (message->h >= h)
{
h = (message->h + 10);
}
}
}
for (int i = 0; i != 4; ++i)
{
rects[i].x = x;
rects[i].y = y;
rects[i].w = w;
rects[i].h = h;
}
refresh = false;
}
}
button::button(int xpos, int ypos, int width, int height)
{
string defaultFontLocation = DEFAULT_FONT;
//set pos
x = xpos;
y = ypos;
w = width;
h = height;
//set text stuff
text = "";
textSize = DEFAULT_FONT_SIZE;
tC.r = 0;
tC.g = 0;
tC.b = 0;
f = TTF_OpenFont(defaultFontLocation.c_str(), textSize);
message = NULL;
//create box types
state = 0;
for (int i = 0; i != 4; ++i)
{
boxes[i].r = 255;
boxes[i].g = 255;
boxes[i].b = 255;
rects[i].x = x;
rects[i].y = y;
rects[i].w = w;
rects[i].h = h;
}
autoSize = true;
refresh = true;
refresh_image();
}
button::~button()
{
if (f != NULL)
{
TTF_CloseFont(f);
}
if (message != NULL)
{
SDL_FreeSurface(message);
}
}
void button::setFont(string fontFileLocation)
{
if (f != NULL)
{
TTF_CloseFont(f);
}
f = TTF_OpenFont(fontFileLocation.c_str(), textSize);
refresh_image();
}
void button::setFont(int color[3])
{
tC.r = color[0];
tC.g = color[1];
tC.b = color[2];
refresh_image();
}
void button::setFont(string fontFileLocation, int color[3])
{
if (f != NULL)
{
TTF_CloseFont(f);
}
f = TTF_OpenFont(fontFileLocation.c_str(), textSize);
tC.r = color[0];
tC.g = color[1];
tC.b = color[2];
refresh_image();
}
vector<int> button::textColor()
{
vector<int> colors;
colors.resize(3);
colors[0] = tC.r;
colors[1] = tC.g;
colors[2] = tC.b;
return colors;
}
vector<int> button::boxColor(int boxnum)
{
vector<int> colors;
colors.resize(3);
if ((boxnum <= 3) && (boxnum >= 0))
{
colors[0] = boxes[boxnum].r;
colors[1] = boxes[boxnum].g;
colors[2] = boxes[boxnum].b;
} else {
colors[0] = -1;
colors[1] = -1;
colors[2] = -1;
}
return colors;
}
void button::setBoxColor(int boxnum, int color[3])
{
if ((boxnum <= 3) && (boxnum >= 0))
{
boxes[boxnum].r = color[0];
boxes[boxnum].g = color[1];
boxes[boxnum].b = color[2];
//refresh_image();
}
}
void button::render(SDL_Surface *screen)
{
refresh_image();
SDL_FillRect(screen, &rects[state], SDL_MapRGB(screen->format, static_cast<Uint8>(boxes[state].r), static_cast<Uint8>(boxes[state].g), static_cast<Uint8>(boxes[state].b)));
if (message != NULL)
{
apply_surface((((x + w) - message->w) / 2), (((y + h) - message->h) / 2), message, screen);
}
refresh = true;
}
void button::handleEvents(SDL_Event *event)
{
//the mouse offsets
int mx, my;
//if mouse moved
if (event->type == SDL_MOUSEMOTION)
{
//get the mouse offsets
mx = event->motion.x;
my = event->motion.y;
//if the mouse is over the button
if ((mx > x) && (mx < (x + w)) && (my > y) && (my < (y + h)))
{
//set the button sprite
state = 1;
} else {
//set the button sprite
state = 0;
}
}
//if a mouse button was pressed
if (event->type == SDL_MOUSEBUTTONDOWN)
{
//if it was the left mouse button
if (event->button.button == SDL_BUTTON_LEFT)
{
//get the mouse offsets
mx = event->motion.x;
my = event->motion.y;
//if the mouse is over the button
if ((mx > x) && (mx < (x + w)) && (my > y) && (my < (y + h)))
{
//set the button sprite
state = 2;
}
}
}
//if a mouse button was released
if (event->type == SDL_MOUSEBUTTONUP)
{
//if it was the left mouse button
if (event->button.button == SDL_BUTTON_LEFT)
{
//get the mouse offsets
mx = event->motion.x;
my = event->motion.y;
//if the mouse is over the button
if ((mx > x) && (mx < (x + w)) && (my > y) && (my < (y + h)))
{
//set the button sprite
state = 3;
}
}
}
}
and here is the code I have been calling to create the button in my main() function:
int color1[3] = {105,240,81};
int color2[3] = {230,188,62};
button testbutton(360, 130, 50, 30);
testbutton.text = "TEST";
testbutton.setBoxColor(1, color1);
testbutton.setBoxColor(2, color2);
delete[] &color1;
delete[] &color2;
The problem is most probably in these two lines:
delete[] &color1;
delete[] &color2;
color1 and color2are automatic variables, and calling delete on them invokes undefined behavior. You should only delete variables you previously allocated with new.
Related
I generate a matrix about my game map by random Prim. Then I draw the map by menuItem in the cocos2d-x 4.0. I want to control a sprite to move by key "asdw". But I cannot control the sprite to cross the bamboo walls. Sometimes the sprite is surrounded by bamboo walls.
How can I check the wall? How can I ensure a path that the sprite can move to the destination?
This is my result image
cross the wall
sprite is surrounded
My code is below.
MazeScene.h
#pragma once
#ifndef __Maze_SCENE_H__
#define __Maze_SCENE_H__
#endif // __SECOND_SCENE_H__
#include "cocos2d.h"
#include<vector>
#include <time.h>
#define m 18 //row
#define n 10 //columns
#define down 1
#define right 2
#define left 4
#define up 8
#define WALL -1
#define NOTHING 2
USING_NS_CC;
class MazeScene : public cocos2d::Layer {
public:
Sprite* sprite;
int backgroundAudioID;
static cocos2d::Scene* createScene();
virtual bool init();
void initMap();
void onReset();
void changeScene(float dt);
void update(float dt);
void GameOver();
void Success();
void onKeyPressed(EventKeyboard::KeyCode keyCode, Event* event);
void FindBlock();
CREATE_FUNC(MazeScene);
private:
struct block {
int row, column, direction;
block(int _row, int _column, int _direction) {
row = _row;
column = _column;
direction = _direction;
}
};
struct point {
int x;
int y;
}start, end;
std::vector<block> myblock;
int x_num = 1, y_num = 1;//Miner location
int G[100][100];
Sprite* _player;
Sprite* _door;
Menu* mazeMenu;
int life = 180;
int i = 0;
};
MazeScene.cpp
#include "MazeScene.h"
#include <AudioEngine.h>
#include <MenuScene.h>
USING_NS_CC;
using namespace std;
#define UserDefault UserDefault::getInstance()
cocos2d::Scene* MazeScene::createScene()
{
auto scene = Scene::create();
auto layer = MazeScene::create();
scene->addChild(layer);
return scene;
}
bool MazeScene::init()
{
if (!Layer::init())
{
return false;
}
AudioEngine::play2d("music/leisure.mp3",true);
// special effect
auto particleSystem = ParticleSystemQuad::create("image/mist.plist");
particleSystem->setPosition(1500, 500);
this->addChild(particleSystem, 1);
auto particleSystem1 = ParticleSystemQuad::create("image/mist2.plist");
particleSystem1->setPosition(-500, 400);
this->addChild(particleSystem1, 1);
this->schedule(CC_SCHEDULE_SELECTOR(MazeScene::update), 1.0);
initMap();
return true;
}
void MazeScene::initMap() {
//Put all the map as a wall
memset(G, WALL, sizeof(G));
//Define the starting point
G[1][1] = NOTHING;
start.x = start.y = 1;
srand((unsigned)time(NULL));//Random number seed
FindBlock();
//The first step is to press into the two walls (to the right of the starting point and
below the starting point) to enter the loop
while (myblock.size()) {
int BlockSize = myblock.size();
//Randomly select a wall (generate a random number between 0 and BlockSize-1, which
is also the subscript of the wall in the vector)
int randnum = rand() % BlockSize;
block SelectBlock = myblock[randnum];
x_num = SelectBlock.row;//Miners come to our "wall of choice" here
y_num = SelectBlock.column;
//Follow-up operations based on the direction of the currently selected wall
//At this time, the three areas of the wall and target block selected as the
starting point are on the same straight line.
//We let the miners move on from the "wall of choice" to the "target block"
//Miners have the ability to penetrate walls :)
switch (SelectBlock.direction) {
case down: {
x_num++;
break;
}
case right: {
y_num++;
break;
}
case left: {
y_num--;
break;
}
case up: {
x_num--;
break;
}
}
//If the target block is a wall
if (G[x_num][y_num] == WALL) {
//Break through the wall and target block
G[SelectBlock.row][SelectBlock.column] = G[x_num][y_num] = NOTHING;
//Find again the wall adjacent to the current location of the miner
FindBlock();
}
else {//If not? It means that our miners have dug into an empty path and just take a
rest.
//relax
}
//Delete this wall (delete the unusable wall, for those who have already been
constructed, there is no need to construct it, but also to ensure that we can jump
out of the loop)
myblock.erase(myblock.begin() + randnum);
}
Vector<MenuItem*> spriteBtns;
std::string imgName;
for (int i = 0; i <= m + 1; i++) {
for (int j = 0; j <= n + 1; j++) {
if (i == start.x && j == start.y) {
imgName = "general.png";
}else if(G[i][j] == NOTHING) {
imgName = "nud.png";
}
else if(i == m + 1 && j == n + 1) {
imgName = "image/evidence.png";
}
else {
imgName = "bamboo.png";
}
auto btnSprite = MenuItemImage::create(imgName,imgName);
Size cts = btnSprite->getContentSize();
btnSprite->setPosition(i*cts.width,j*cts.height);
spriteBtns.pushBack(btnSprite);
}
}
mazeMenu = Menu::createWithArray(spriteBtns);
addChild(mazeMenu);
mazeMenu->setPosition(30,80);
_player = Sprite::create("general.png");
_player->setPosition(90, 130);
this->addChild(_player);
_door = Sprite::create("image/evidence.png");
_door->setPosition(900,630);
this->addChild(_door);
// Keyboard event monitoring
auto keyListener = EventListenerKeyboard::create();
keyListener->onKeyPressed = CC_CALLBACK_2(MazeScene::onKeyPressed, this);
_eventDispatcher->addEventListenerWithSceneGraphPriority(keyListener, this);
}
void MazeScene::onReset() {
initMap();
}
void MazeScene::changeScene(float dt) {
Director::getInstance()->replaceScene(TransitionSlideInT::create(3.0f,
MenuScene::createScene()));
}
void MazeScene::update(float dt) {
life--;
if (_player->getBoundingBox().intersectsRect(_door->getBoundingBox())) {
initMap();
i++;
}
if (life<=0) {
GameOver();
this->scheduleOnce(CC_SCHEDULE_SELECTOR(MazeScene::changeScene), 5.0);
}
if (i == 3 && life > 0) {
UserDefault->setIntegerForKey("step4", 4);
UserDefault->flush();
Success();
this->scheduleOnce(CC_SCHEDULE_SELECTOR(MazeScene::changeScene), 5.0);
}
}
void MazeScene::GameOver() {
this->unscheduleAllCallbacks();
AudioEngine::play2d("music/over.mp3", false, 0.3);
AudioEngine::stopAll();
auto sprite = Sprite::create("image/over2.png");
auto visibleSize = Director::getInstance()->getVisibleSize();
Vec2 origin = Director::getInstance()->getVisibleOrigin();
sprite->setPosition(Vec2(visibleSize.width / 2 + origin.x, visibleSize.height / 2 +
origin.y));
this->addChild(sprite, 10);
}
void MazeScene::Success() {
this->unscheduleAllCallbacks();
AudioEngine::play2d("music/victory.mp3", false, 0.3);
AudioEngine::stopAll();
auto sprite = Sprite::create("image/success2.png");
auto visibleSize = Director::getInstance()->getVisibleSize();
Vec2 origin = Director::getInstance()->getVisibleOrigin();
sprite->setPosition(Vec2(visibleSize.width / 2 + origin.x, visibleSize.height / 2 +
origin.y));
this->addChild(sprite, 10);
}
void MazeScene::onKeyPressed(EventKeyboard::KeyCode keyCode, Event* event) {
log("Key with keycode %d pressed", keyCode);
switch (keyCode) {
case EventKeyboard::KeyCode::KEY_D:
{
auto moveBy = MoveBy::create(0.5f, Vec2(25, 0));
_player->runAction(Sequence::create(moveBy, nullptr));
break;
}
case EventKeyboard::KeyCode::KEY_A:
{
auto moveBy = MoveBy::create(0.5f, Vec2(-25, 0));
_player->runAction(Sequence::create(moveBy, nullptr));
break;
}
case EventKeyboard::KeyCode::KEY_W:
{
auto moveBy = MoveBy::create(0.5f, Vec2(0, 25));
_player->runAction(Sequence::create(moveBy, nullptr));
break;
}
case EventKeyboard::KeyCode::KEY_S:
{
auto moveBy = MoveBy::create(0.5f, Vec2(0, -25));
_player->runAction(Sequence::create(moveBy, nullptr));
break;
}
default:
break;
}
}
void MazeScene::FindBlock() {
//find Find nearby walls
if (x_num + 1 <= m && G[x_num + 1][y_num] == WALL) {//down
myblock.push_back(block(x_num + 1, y_num, down));
}
if (y_num + 1 <= n && G[x_num][y_num + 1] == WALL) {//right
myblock.push_back(block(x_num, y_num + 1, right));
}
if (x_num - 1 >= 1 && G[x_num - 1][y_num] == WALL) {//up
myblock.push_back(block(x_num - 1, y_num, up));
}
if (y_num - 1 >= 1 && G[x_num][y_num - 1] == WALL) {//left
myblock.push_back(block(x_num, y_num - 1, left));
}
}
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.
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 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.
Here is the code of draw()
My problem is that it draws things only once, then it draws the same.
I am using SLD_Flip(surface) after it.
sqrbmp, applebmp, blank are surfaces with BMP loaded
des is a rectangle
screen is main surface
It is supposed to draw a game map.
Where is my bad?
// includes, defines etc.
const int mapx = 32; // 640 / 20(bmp size)
const int mapy = 24; // 480 / 20
//nothing important
SDL_Event keys;
SDL_Surface * screen = NULL;
SDL_Surface * blank = NULL;
SDL_Surface * sqrbmp = NULL;
SDL_Surface * applebmp = NULL;
SDL_Rect des;
int map[mapy][mapx];
// nothing important
// nothing important
struct square
{
square(int xt, int yt, int dirt);
int x;
int y;
int dir;
};
std::vector <square> snake;
square::square(int xt, int yt, int dirt)
{
x = xt;
y = yt;
dir = dirt;
snake.push_back(*this);
}
bool squareontile(int x, int y)
{
// returns true if there is square on that tile
}
void draw()
{
for(int yt = 0 ; yt < mapy; yt++)
{
for(int xt = 0 ; xt < mapx; xt++)
{
des.y = yt * 20;
des.x = xt * 20;
if(squareontile(xt,yt) == 1)
{
SDL_BlitSurface(sqrbmp, NULL, screen, &des);
}
else
{
switch(map[yt][xt])
{
case 5:
SDL_BlitSurface(applebmp, NULL, screen, &des);
break;
default:
SDL_BlitSurface(blank, NULL, screen, &des);
break;
}
}
}
}
}
// nothing important
void checkmap()
{
for(int yt = 0 ; yt < mapy; yt++)
{
for(int xt = 0 ; xt < mapx; xt++)
{
if(squareontile(xt,yt) == 0 && map[yt][xt] != 5)
{
map[yt][xt] = 0;
}
}
}
}
bool checksnake()
{
for(int a = 0; a < snake.size() ; a++)
{
for(int b = 0; b < snake.size() ; b++)
{
if(snake[a].y == snake[b].y && snake[a].x == snake[b].x && a != b)
{
return true;
}
}
}
}
// nothing important
void apple()
{
if(!appleisonmap())
{
int y;
int x;
do
{
y = rand() % mapy;
x = rand() % mapx;
} while(squareontile(x,y));
map[y][x] = 5;
}
}
// nothing important
int main( int argc, char * args[] )
{
// nothing important
SDL_Init( SDL_INIT_EVERYTHING );
screen = SDL_SetVideoMode( 640, 480, 24, SDL_SWSURFACE );
applebmp = SDL_LoadBMP("apple.bmp"); // 20x20
blank = SDL_LoadBMP("blank.bmp"); // 20x20
sqrbmp = SDL_LoadBMP( "sqr.bmp" ); // 20x20
for(int b = 7 ; b > 0 ; b--)
{
square(5,b,1);
}
while(!exitv && !game_over)
{
draw();
game_over = checksnake();
SDL_Flip( screen );
SDL_Delay(100);
while( SDL_PollEvent( & keys ) )
{
if( keys.type == SDL_QUIT )
{
exitv = true;
}
// detecting other keys, input for moving, ya know
}
checkmap();
apple();
checksquare();
}
SDL_FreeSurface( sqrbmp );
SDL_FreeSurface( applebmp );
SDL_FreeSurface( blank );
SDL_Quit();
return 0;
}
Ahh found the error. I just forgot 1 function and the snake wasnt moving.