I am making a google chrome dinosaur game using an SDL Template for graphics and I am almost finished but I have run into the issue of needing to be able to reset to the starting values of the game just like how you reset when you hit spacebar after dying in the google chrome dinosaur game. I have made an isGameOver function but I don't know how to reset the values to start a new game which is my main problem.
this is my GameScene class where the game over logic is located
GameScene.h
#pragma once
#include "Scene.h"
#include "GameObject.h"
#include "Player.h"
#include "Floor.h"
#include "Obstacle.h"
#include "FlyingObstacle.h"
#include <vector>
#include "util.h"
#include "text.h"
class GameScene : public Scene
{
public:
GameScene();
~GameScene();
void start();
void draw();
void update();
std::vector<Obstacle*> spawnedObstacle;
std::vector<FlyingObstacle*> spawnedBird;
private:
Player* player;
Floor* floor;
float spawnTime;
float currentSpawnTimer;
void floorCollision();
void obstacleCollision();
void birdCollision();
void obstacleSpawn();
void birdSpawn();
void despawnObstacle(Obstacle* obstacle);
void despawnBird(FlyingObstacle* bird);
int points;
int highscore;
bool isGameOver;
};
GameScene.cpp
#include "GameScene.h"
GameScene::GameScene()
{
// Register and add game objects on constructor
player = new Player();
this->addGameObject(player);
floor = new Floor();
this->addGameObject(floor);
points = 0;
highscore = 0;
}
GameScene::~GameScene()
{
delete player;
}
void GameScene::start()
{
Scene::start();
// Initialize any scene logic here
initFonts();
isGameOver = true;
currentSpawnTimer = 300;
spawnTime = rand() % 300; //spawn time of 5 seconds
for (int i = 0; i < 1; i++)
{
obstacleSpawn();
}
for (int i = 0; i < 3; i++)
{
birdSpawn();
}
}
void GameScene::draw()
{
Scene::draw();
drawText(110, 20, 255, 255, 255, TEXT_CENTER, "POINTS: %03d", points);
if (player->getIsAlive() == true)
{
drawText(900, 20, 255, 255, 255, TEXT_CENTER, "PRESS SPACE TO START MOVING");
}
if (player->getIsAlive() == false)
{
if (isGameOver == false)
drawText(SCREEN_WIDTH / 2, 200, 255, 255, 255, TEXT_CENTER, "YOU LOSE! PRESS SPACE TO SHOW POINTS");
if (isGameOver == true)
{
drawText(SCREEN_WIDTH / 2, 200, 255, 255, 255, TEXT_CENTER, "HIGHSCORE: %03d", highscore);
if (points > highscore)
{
drawText(SCREEN_WIDTH / 2, 200, 255, 255, 255, TEXT_CENTER, "NEW HIGHSCORE: %03d", points, highscore);
}
}
}
}
void GameScene::update()
{
if (isGameOver == true)
{
if (app.keyboard[SDL_SCANCODE_SPACE])
{
isGameOver = false;
}
}
if (isGameOver == false)
{
Scene::update();
floorCollision();
obstacleCollision();
birdCollision();
if (currentSpawnTimer > 0)
currentSpawnTimer--;
if (currentSpawnTimer <= 0)
{
for (int i = 0; i < 1; i++)
{
obstacleSpawn();
}
for (int i = 0; i < 3; i++)
{
birdSpawn();
}
currentSpawnTimer = spawnTime;
}
//This is where Gravity strength is located
if (player->getOnFloor() == false) {
player->setY(player->getY() + 7);
}
else {
player->getY() + 0;
}
}
}
void GameScene::floorCollision()
{
//Checks for collisions
for (int i = 0; i < objects.size(); i++)
{
//Cast to floor
Floor* floor = dynamic_cast<Floor*>(objects[i]);
//Check if the floor was casted
if (floor != NULL)
{
int collision = checkCollision(
player->getX(), player->getY(), player->getWidth(), player->getHeight(),
floor->getX(), floor->getY(), floor->getWidth(), floor->getHeight()
);
if (collision == 1)
{
player->setOnFloor(true);
if (player->getIsAlive() == true)
{
points++;
highscore++;
}
break;
}
}
}
}
void GameScene::obstacleCollision()
{
for (int i = 0; i < objects.size(); i++)
{
Obstacle* obstacle = dynamic_cast<Obstacle*>(objects[i]);
if (obstacle != NULL)
{
if (obstacle != NULL)
{
int collision = checkCollision(
player->getX(), player->getY(), player->getWidth(), player->getHeight(),
obstacle->getX(), obstacle->getY(), obstacle->getWidth(), obstacle->getHeight()
);
if (collision == 1)
{
player->doDeath();
isGameOver = true;
break;
}
}
}
}
}
void GameScene::birdCollision()
{
for (int i = 0; i < objects.size(); i++)
{
FlyingObstacle* bird = dynamic_cast<FlyingObstacle*>(objects[i]);
if (bird != NULL)
{
if (bird != NULL)
{
int collision = checkCollision(
player->getX(), player->getY(), player->getWidth(), player->getHeight(),
bird->getX(), bird->getY(), bird->getWidth(), bird->getHeight()
);
if (collision == 1)
{
player->doDeath();
isGameOver = true;
break;
}
}
}
}
}
void GameScene::obstacleSpawn()
{
Obstacle* obstacle = new Obstacle();
this->addGameObject(obstacle);
obstacle->setPosition(1200, 300 + (rand() % 300));
spawnedObstacle.push_back(obstacle);
}
void GameScene::birdSpawn()
{
FlyingObstacle* bird = new FlyingObstacle();
this->addGameObject(bird);
bird->setPos(1200, 300 + (rand() % 300));
spawnedBird.push_back(bird);
}
void GameScene::despawnObstacle(Obstacle* obstacle)
{
int index = -1;
for (int i = 0; i < spawnedObstacle.size(); i++)
{
//If pointer matches
if (obstacle == spawnedObstacle[i])
{
index = i;
break;
}
}
//If any match is found
if (index != -1)
{
spawnedObstacle.erase(spawnedObstacle.begin() + index);
delete obstacle;
}
}
void GameScene::despawnBird(FlyingObstacle* bird)
{
int index = -1;
for (int i = 0; i < spawnedBird.size(); i++)
{
//If pointer matches
if (bird == spawnedBird[i])
{
index = i;
break;
}
}
//If any match is found
if (index != -1)
{
spawnedBird.erase(spawnedBird.begin() + index);
delete bird;
}
}
I tried making an isGameOver bool inside my GameScene.h and I made it so that pressing spacebar would reset the game but in reality when the player dies the screen pauses every movement instead of resetting and if I press space again the game continues to move even though the player is dead.
Your entry point for the game(probably main function will probably have some form like this)
#include ...
#include ...
...
int main()
{
bool game_running = true;
while(game_running)
{
GameInstance* game = new Game(); //Allocation and initialization of all resources needed to play the game.
game.play(); // This function will finish when you lose the game.
delete game; // Deallocation.
if(!run_again())
{
game_running = false;
}
}
return 0;
}
After deallocation, you check if user wants to play again, you enter the next iteration of while loop, and new instance of Game is allocated, where all the resource handling takes place. The only problem is that allocating Game instance for every iteration might be a little costly, but you can ignore that if you don't worry about performance much.
Related
I was working on changing sprite in my SFML project but it just doesn't want to wark. I tried everything - pointers, refrences and normal string objects. You can see in debug console i printed out this->path that containted a path to file to change but when it left the function this->path was null (empty "" string). Can you please help me? Here's the code:
Player.cpp
#include "Player.h"
#include <iostream>
Player::Player(std::string path)
{
this->path = path;
texture.loadFromFile(path);
sprite.setTexture(texture);
sprite.setPosition(500.f, 700.f);
}
void Player::update()
{
// Movement //
//if (sf::Keyboard::isKeyPressed(sf::Keyboard::W))
//sprite.move(0.f, -velocity);
//if (sf::Keyboard::isKeyPressed(sf::Keyboard::S))
//sprite.move(0.f, velocity);
if (sf::Keyboard::isKeyPressed(sf::Keyboard::A))
sprite.move(-velocity, 0.f);
if (sf::Keyboard::isKeyPressed(sf::Keyboard::D))
sprite.move(velocity, 0.f);
// Movement //
float szerokosc = sprite.getGlobalBounds().width;
float wysokosc = sprite.getGlobalBounds().height;
// Collision with screen //
// Left
if (sprite.getPosition().x < 0.f)
sprite.setPosition(0.f, sprite.getPosition().y);
// Top
if (sprite.getPosition().y < 0.f)
sprite.setPosition(sprite.getPosition().x, 0.f);
// Right
if (sprite.getPosition().x + szerokosc > 1000)
sprite.setPosition(1000 - szerokosc, sprite.getPosition().y);
// Bottom
if (sprite.getPosition().y + wysokosc > 800)
sprite.setPosition(sprite.getPosition().x, 800 - wysokosc);
// Collision with screen //
}
sf::Sprite Player::getSprite()
{
return sprite;
}
bool Player::collides(Food obj)
{
if (sprite.getGlobalBounds().intersects(obj.getSprite().getGlobalBounds()))
return true;
else
return false;
}
void Player::changeSkin(std::string path)
{
this->path = path;
std::cout << "changeSkin(): *this->path" << this->path << std::endl;
std::cout << "changeSkin(): *path" << path << std::endl;
texture.loadFromFile(path);
sprite.setTexture(texture);
}
void Player::updateSkin()
{
std::cout << "updateSkin() *this->path: " << this->path << std::endl;
std::cout << "updateSkin() *path: " << path << std::endl;
}
Player::~Player()
{
//delete texture;
//delete sprite;
}
Player.h
#pragma once
#include <SFML/Graphics.hpp>
#include <SFML/Audio.hpp>
#include <time.h>
#include "Food.h"
#include "ShopItem.h"
class Player
{
sf::Texture texture;
sf::Sprite sprite;
std::string path;
bool has_skin = false;
float velocity = 5.f;
public:
explicit Player(std::string path);
~Player();
void update();
sf::Sprite getSprite();
bool collides(Food obj);
void changeSkin(std::string path);
void updateSkin();
};
main.cpp - main() has Player::updateSkin() call
int main()
{
RenderWindow window(VideoMode(1000, 800), "Tomczyszyn Eater v0.21 BETA");
window.setFramerateLimit(60);
srand(time(nullptr));
Texture bg_text;
bg_text.loadFromFile("Assets/Textures/bg.png");
Sprite bg{ bg_text };
Player player{ "Assets/Textures/player.png" };
time_t start{};
Event event;
Intro* intro = new Intro{window};
MainMenu* menu = new MainMenu{&window};
Shop shop(&window);
Game game{player, &window};
bool intro_deleted{ false };
bool menu_deleted{ false };
bool game_started{ false };
int menuState{ 2 };
while (window.isOpen())
{
while (window.pollEvent(event))
{
if (event.type == Event::EventType::Closed)
window.close();
if (Keyboard::isKeyPressed(Keyboard::Space) && game.gameRunning() == false)
{
start = time(nullptr);
game.runGame();
}
else if (Keyboard::isKeyPressed(Keyboard::Escape) && game.gameRunning() == false)
{
menuState = 0;
}
}
window.clear();
if (game.gameRunning() == true && menuState == 1) // Main Game
{
if (game_started == false)
{
start = time(nullptr);
game_started = true;
}
if (intro_deleted == false)
{
delete intro;
intro_deleted = true;
}
if (menu_deleted == false)
{
delete menu;
menu_deleted = true;
}
window.draw(bg);
game.drawMoney();
player.update();
window.draw(player.getSprite());
// Player::updateSkin() - source of the problem
player.updateSkin();
game.update();
game.draw();
if (time(nullptr) - start == 2)
{
start = time(nullptr);
int liczba = rand() % 6 + 1;
string file_name{ "Assets/Textures/food" + to_string(liczba) + ".png" };
Food* newFood = new Food{file_name};
game.spawn(newFood);
}
game.catched();
game.fell();
}
else if (menuState == 0) // Menu
{
if (menu_deleted == true) menu = new MainMenu{ &window };
menu->draw();
menu->update(menuState);
menu_deleted = false;
}
else if (menuState == -1) // Intro
{
start = time(nullptr);
intro->play();
if (intro->intro_started() == true) menuState = 0;
}
else if (menuState == 2) // Shop
{
shop.draw();
shop.backIfClicked(menuState);
shop.checkIfBought(player, game.balance());
game.drawMoney();
}
else
{
game.drawDeathScreen();
}
window.display();
}
}
Shop.cpp - checkIfBought() has Player::changeSkin() call
void Shop::checkIfBought(Player player, int& money)
{
for (int i = 0; i < skins.size(); i++)
{
if (isClicking(skins[i].getSprite()) == true)
{
skins[i].buy(money);
player.changeSkin(skins[i].getPath()); //Plaer::changeSkin() here - source of the problem
std::cout << skins[i].getPath() << std::endl;
}
}
for (int i = 0; i < bg_skins.size(); i++)
{
if (isClicking(bg_skins[i].getSprite()) == true)
{
bg_skins[i].buy(money);
player.changeSkin(bg_skins[i].getPath()); //Plaer::changeSkin() here - source of the problem
}
}
}
Debug Console output:
changeSkin(): this->path = Assets/Textures/skin1.png
changeSkin(): path = Assets/Textures/skin1.png
Assets/Textures/skin1.png
updateSkin() this->path = Assets/Textures/player.png
updateSkin() path = Assets/Textures/player.png
Sorry if you didn't understand my english is bad. I really do need help i've spent so much time on fixing this that i just gave up.
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 attempting to make a simple scrolling shooter game with SDL2. I have a moving player on a screen, and I am trying to make the player shoot a bullet using an array (so they can shoot multiple bullets) however, when I press the space bar, nothing happens, and instead the bullet image sort of flashes in the top left corner.
Heres the same code in codepad: http://codepad.org/rOhE1AqY
#include <SDL.h>
#include <stdio.h> //use for things like printf, same as cout
#include <iostream>
#include <string>
#include <time.h>
using namespace std;
//screend dimensions& sprtie dimensions
const int SCREEN_HEIGHT = 600;
const int SCREEN_WIDTH = 400;
const int SPRITE_WIDTH = 60;
const int SPRITE_HEIGHT = 80;
const int MAX_BULLETS = 50;
SDL_Window* Window = NULL;//the window rendering to
SDL_Surface* ScreenSurface = NULL;//surface contained by window
SDL_Surface* Background = NULL;
SDL_Surface* Player = NULL;
SDL_Surface* Enemy = NULL;
SDL_Surface* Bullet = NULL;
SDL_Surface* newBullet = NULL;
SDL_Rect posPlayer, posEnemy, posBullet, posnewBullet;
const Uint8* keystate = SDL_GetKeyboardState(NULL);
SDL_Event event;
class thePlayer
{
public:
thePlayer();
void player_movement();
void show_player();
private:
};
class theBullet
{
public:
theBullet();
bool isActive;
int x_position;
int y_position;
void bullet_movement();
void add_new_bullet();
void show_bullet();
private:
};
theBullet arrayofBullets[MAX_BULLETS];
class theEnemy
{
public:
theEnemy();
void enemy_movement();
void show_enemy();
private:
};
thePlayer::thePlayer()
{
posPlayer.x = 170;
posPlayer.y = SCREEN_HEIGHT;
posPlayer.w = 20;
posPlayer.h = 30;
}
void thePlayer::player_movement()
{
if(keystate[SDL_SCANCODE_LEFT])
{
posPlayer.x -= 2;
}
if(keystate[SDL_SCANCODE_RIGHT])
{
posPlayer.x += 2;
}
if(keystate[SDL_SCANCODE_UP])
{
posPlayer.y -= 2;
}
if(keystate[SDL_SCANCODE_DOWN])
{
posPlayer.y += 2;
}
if ((posPlayer.x + SPRITE_WIDTH) > SCREEN_WIDTH)
{
posPlayer.x = (SCREEN_WIDTH - SPRITE_WIDTH);
}
if ((posPlayer.y + SPRITE_HEIGHT) > SCREEN_HEIGHT)
{
posPlayer.y = (SCREEN_HEIGHT - SPRITE_HEIGHT);
}
}
void thePlayer::show_player()
{
SDL_BlitSurface(Player, NULL, ScreenSurface, &posPlayer);
SDL_SetColorKey(Player, SDL_TRUE, SDL_MapRGB(Player->format, 255, 255, 255));
}
theBullet::theBullet()
{
/*posBullet.x;
posBullet.y;
posBullet.w = 10;
posBullet.h = 15;*/
}
void theBullet::bullet_movement()
{
/*if(keystate[SDL_SCANCODE_SPACE])
{
posBullet.x = posPlayer.x + 25;
posBullet.y = posPlayer.y + 10;
}
posBullet.y -= 2;
if(posBullet.y < 0)
{
posBullet.y = -50;
}*/
}
void theBullet::show_bullet()
{
//SDL_BlitSurface(Bullet, NULL, ScreenSurface, &posBullet);
//SDL_SetColorKey(Bullet, SDL_TRUE, SDL_MapRGB(Player->format, 255, 255, 255));//removes white background
}
theEnemy::theEnemy()
{
srand (time(NULL));
posEnemy.x = rand() % 300 + 50;
posEnemy.y =0;
posEnemy.w = 35;
posEnemy.h = 60;
}
void theEnemy::enemy_movement()
{
posEnemy.y += 1;
if(posEnemy.y > SCREEN_HEIGHT)
{
posEnemy.y = SCREEN_HEIGHT +50;
}
}
void theEnemy::show_enemy()
{
SDL_BlitSurface(Enemy, NULL, ScreenSurface, &posEnemy);
SDL_SetColorKey(Enemy, SDL_TRUE, SDL_MapRGB(Player->format, 255, 255, 255));
}
bool initialise()
{
bool success = true;
if (SDL_Init(SDL_INIT_EVERYTHING) !=0)
{
cout<<"SDL_Init Error."<<SDL_GetError()<<endl;
success = false;
}
else
{
//create the window for game
Window = SDL_CreateWindow("Scrolling Shooter Game", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
if (Window == NULL)
{
cout<<"Window Error"<<SDL_GetError()<<endl;
success = false;
}
else
{
//get window surface
ScreenSurface = SDL_GetWindowSurface(Window);
}
}
return success;
}
bool LoadingMedia()
{
bool success = true;
Background = SDL_LoadBMP("background.bmp");
if (Background == NULL)
{
cout<<"Error in loading background."<<SDL_GetError()<<endl;
success = false;
}
Player = SDL_LoadBMP("spaceship.bmp");
if (Player == NULL)
{
cout<<"Error in loading player."<<SDL_GetError()<<endl;
success = false;
}
Enemy = SDL_LoadBMP("enemy.bmp");
if (Enemy == NULL)
{
cout<<"Error in loading enemy."<<SDL_GetError()<<endl;
success = false;
}
Bullet = SDL_LoadBMP("bullet.bmp");
if (Bullet == NULL)
{
cout<<"Error in loading bullet."<<SDL_GetError()<<endl;
success = false;
}
return success;
}
void closedown()
{
SDL_FreeSurface(Background);
Background = NULL;
SDL_FreeSurface(Player);
Player = NULL;
SDL_FreeSurface(Enemy);
Enemy = NULL;
SDL_DestroyWindow(Window);
Window = NULL;
SDL_Quit();
}
int main(int argc, char** argv)
{
bool quit = false;
thePlayer myPlayer;
theEnemy myEnemy;
theBullet myBullet;
if (!initialise())
{
cout<<"Failed to initialise"<<SDL_GetError()<<endl;
}
else
{
if (!LoadingMedia())
{
cout<<"Error loading media"<<SDL_GetError()<<endl;
}
}
//makes all bullets false
for (int i=0; i<MAX_BULLETS; i++)
{
arrayofBullets[i].isActive = false;
}
//GAME LOOP
while (quit == false)
{
SDL_BlitSurface(Background, NULL, ScreenSurface, NULL);
myPlayer.show_player();
myPlayer.player_movement();
while (SDL_PollEvent(&event))
{
if( event.type == SDL_QUIT )
{
quit = true;
break;
}
if(keystate[SDL_SCANCODE_SPACE])
{
for (int i=0; i<MAX_BULLETS; i++)
{
if (arrayofBullets[i].isActive == false)
{
arrayofBullets[i].x_position = posPlayer.x + 25;
arrayofBullets[i].y_position = posPlayer.y + 10;
arrayofBullets[i].isActive = true;
break;
}
}
}
//update game objects
for (int i=0; i<MAX_BULLETS; i++)
{
if (arrayofBullets[i].isActive == true)
{
arrayofBullets[i].y_position -= 2;
if (arrayofBullets[i].y_position < 0)
{
arrayofBullets[i].isActive = false;
}
}
}
for (int i=0; i<MAX_BULLETS; i++)
{
if (arrayofBullets[i].isActive == true)
{
SDL_BlitSurface(Bullet, NULL, ScreenSurface, NULL);
}
}
}
//myPlayer.show_player();
//myBullet.show_bullet();
//myEnemy.show_enemy();
//myPlayer.player_movement();
//myBullet.bullet_movement();
//myEnemy.enemy_movement();
SDL_UpdateWindowSurface(Window); //updates screen
}
closedown();
return 0;
}
SDL_BlitSurface(Bullet, NULL, ScreenSurface, NULL);
You haven't specified destination rect, so it will blit on left-top corner.
It should be
SDL_Rect dstrect;
dstrect.x = arrayofBullets[i].x_position;
dstrect.y = arrayofBullets[i].y_position;
dstrect.w = Bullet->w;
dstrect.h = Bullet->h;
SDL_BlitSurface(Bullet, NULL, ScreenSurface, &dstrect);