Moving player on board - c++

I'm writing board game using SFML and I want my player's figures to move around this board, but I want this to be smooth animated move, from field X to field Y. Unfortunatelly, it isnt happening, my player's figures just changes location, it jumps from lets say field 4 into field 8, while I want to move from 4 to 5, then to 6, then to 7 and finnaly to 8. Hopefully its clear enough. Now let's see some code.
Firstly Field class.
class Field {
int m_position_id;
int m_position_x;
int m_position_y;
std::string m_name;
public:
Field() {}
Field(int, int, int);
virtual ~Field() = default;
int getPosID() { return m_position_id; }
int getPosX() { return m_position_x; }
int getPosY() { return m_position_y; }
};
Then we got Board which is basicly just an array of Fields
constexpr int BOARD_SIZE = 40;
class Board {
std::array<Field, BOARD_SIZE> m_board;
public:
Board();
Field& getBoard(int index) { return m_board[index]; }
};
Player class
class Player {
int m_position_id = 0; //starting position
float m_position_x = 600;
float m_position_y = 600;
sf::CircleShape m_shape;
public:
Player(std::string, sf::Color, float);
sf::CircleShape& getShape() { return m_shape; }
int getPositionID() { return m_position_id; }
float getPositionX() { return m_position_x; }
float getPositionY() { return m_position_y; }
void setPositionID(int p_position_id) { m_position_id = p_position_id; }
void setPositionX(int p_position_x) { m_position_x = p_position_x; }
void setPositionY(int p_position_y) { m_position_y = p_position_y; }
};
And finnaly, method that isnt working as I expect it oo
void GameEngine::movePlayer(Player &p_player, int p_distance) {
int l_current_pos_id = p_player.getPositionID();
p_player.setPositionID(p_player.getPositionID() + p_distance);
p_player.setPositionX(m_game_board.getBoard(p_player.getPositionID()).getPosX());
p_player.setPositionY(m_game_board.getBoard(p_player.getPositionID()).getPosY());
if (p_player.getPositionID() > 39) {
p_player.setPositionID(p_player.getPositionID() - 40);
p_player.setPositionX(m_game_board.getBoard(p_player.getPositionID()).getPosX());
p_player.setPositionY(m_game_board.getBoard(p_player.getPositionID()).getPosY());
}
//going out of array range here probably
for (int i = l_current_pos_id; i < p_player.getPositionID(); i++) {
int x = m_game_board.getBoard(i + 1).getPosX() - m_game_board.getBoard(i).getPosX();
int y = m_game_board.getBoard(i + 1).getPosY() - m_game_board.getBoard(i).getPosY();
p_player.getShape().move(x, y);
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
}
And finnaly, class that handles the view
bool m_draw = false;
while (window.isOpen()) {
if (m_evnt.type == sf::Event::KeyReleased && m_evnt.key.code == sf::Keyboard::R) {
//Roll dice
m_game_engine.rollDice(m_game_status); //this just updates some text, result of rollDice is passed as p_distance to movePlayer
m_draw = true;
}
}
window.clear();
for (int i = 0; i < m_game_engine.getNumberOfPlayers(); i++) {
window.draw(m_game_engine.getPlayer(i).getShape());
}
if (m_draw) {
for (int i = 0; i < m_game_engine.getNumberOfPlayers(); i++) {
window.draw(m_game_engine.getPlayer(i).getShape());
}
window.display();
}
Ah and from GameEngine class
class GameEngine {
std::vector<Player> m_players;
Player& getPlayer(int index) { return m_players[index]; }
};
So as you can see, it stores in local variable current player position, then assigns new one, then by Field position ID it gets X and Y coordinate of this position. Next step is checking if this position is inside array (have to do the same for moving my figure around board, because it crashes after first circuit around the board. Still, thats not the point here. For loop at the end, should move from field i to (i+1), then it should wait for 0,5 sec, move again to next field, etc. Althought, after I run my program, it sleeps at beggining and then not moves, but changes position of my shape and its over, no animation at all.

You have a loop and you have waits. That's not how a game works. You can read up on the basics here.
Your game loop has to run. That's the place where the drawing takes place. If you move your token and don't draw it until it arrived, it will look like a teleport. You need to draw between your mini-moves.
Your token needs a target position and a current position and a speed. And every loop you need to add the appropriate numbers to the current position, until it finally arrives at the target position. But you cannot do that in a closed loop, this needs to happen inside your game loop, as a part of it. You probably also want a variable that indicates that a token is indeed moving, so nothing else can happen while it's not complete.

Related

Issues dynamic allocation of classes

I am currently trying to refresh my c++ skills with implementing a snake-game. I have created following class - relevant code snippet:
snake_class.h
#include <string>
#include <vector>
#include <windows.h>
typedef struct coordinates {
int x;
int y;
};
class Snake {
public:
std::vector<coordinates> body;
Snake(const int MAX_HEIGHT, const int MAX_WIDTH, const int initLengthSnake);
void updateSnakeBody(coordinates newDirection, int startingPoint);
};
... and with the corresponding code snippet of the .cpp-file:
snake_class.cpp
#include <vector>
#include "snake_class.h"
Snake::Snake(const int MAX_HEIGHT, const int MAX_WIDTH, const int initLengthSnake) {
for (int snakeLength = 0; snakeLength < initLengthSnake; snakeLength++) {
coordinates currentBodyPoint = { (MAX_WIDTH + initLengthSnake) / 2 - snakeLength, (MAX_HEIGHT) / 2 };
body.push_back(currentBodyPoint);
}
}
void Snake::updateSnakeBody(coordinates newDirection, int startingPoint) {
coordinates currentBodyPoint = body[startingPoint];
body[startingPoint].x += newDirection.x;
body[startingPoint].y += newDirection.y;
if (startingPoint + 1 < body.size()) {
coordinates nextDirection = { currentBodyPoint.x - body[startingPoint + 1].x,
currentBodyPoint.y - body[startingPoint + 1].y };
updateSnakeBody(nextDirection, startingPoint + 1);
}
}
My main-method looks like the following:
bool crashed = false;
int main()
{
//init-part for windows and snake length
const int windowHeight = 20;
const int windowWidth = 25;
const int initSnakeLength = 4;
//init part for snake game to move and some stock variables
coordinates direction = { 1, 0 };
bool initNeeded = false;
//snake init
Snake* snake = new Snake(windowWidth, windowHeight, initSnakeLength);
while (true) {
if (initNeeded) {
crashed = false;
Snake* snake = new Snake(windowWidth, windowHeight, initSnakeLength);
initNeeded = false;
}
if (!crashed) {
(*snake).updateSnakeBody(direction, 0);
crashed = true;
}
else {
delete snake;
initNeeded = true;
}
}
return 0;
}
Build is successfull and the first round of the game works as expected. When I feedback to the game, that I want to play another round, then the new snake class is constructed once again inside the if (initNeeded) {...}-condition. The vector also got the size of 4 after the construction.
But as soon as the program enters the line
(*snake).updateSnakeBody(direction, 0);
I retrieve the error-message vector subsrictp out of range and somehow the vector got the size 0.
I know, that I do not need to dynamically allocate a new class for getting the game to run as intended, but I wanted to try it out in this way.
I cannot really figure out why the new class behaves like that and hope some of you could help me resolving that issue!
Thanks in advance!
You construct two different snakes, one in the main scope, on in the scope of the if statement. Then you create two pointers to these, with the same name. You need to sort this out!
while (true) {
if (initNeeded) {
crashed = false;
Snake* snake = new Snake(windowWidth, windowHeight, initSnakeLength);
initNeeded = false;
}
This snake pointer cannot be accessed from anywhere else. Whatever you are trying to do, this must be wrong.
Perhaps this is what you meant to do
//declare snake pointer
Snake* snake;
while (true) {
if (initNeeded) {
crashed = false;
// set pointer to a new snake
snake = new Snake(windowWidth, windowHeight, initSnakeLength);
initNeeded = false;
}

Having problems moving Snake in Qt

I am doing my very first snake game with Qt creator and I have been stuck for a long time in one single problem. I am using QGraphicsPixmapItem in QGraphicsScene.
Snake head should be different than other body. Now I need to delete all items from scene to draw snake new position to get it work, but I think it's not right way to code it.
How I suppose to update the snake without need delete QGraphicsPixmapItems all the time?
header
struct Point{
int x;
int y;
};
std::vector<Point> snakecore_;
QGraphicsPixmapItem* head_ = nullptr;
QGraphicsPixmapItem* core_ = nullptr;
cpp
delete head_
for(unsigned int i = 0; i < snakecore_.size(); ++i){
if(i == 0){
head_ = scene_.addPixmap(red_);
head_->setPos(snakecore_[i].x, snakecore_[i].y);
}
//Add rest of the snake
}
Moving a snake is quite simple: move the head in the desired direction. Then, move each part to the position of the previous part.
Use a QGraphicsGroupItem to create your snake, it will be simpler to handle the parts.
For example:
class Snake: public QGraphicsItemGroup
{
public:
enum Direction
{
Left,
Right
};
Snake(QGraphicsItem* parent=nullptr): QGraphicsItemGroup(parent), partCount(10)
{
int const partCount = 10;
for( int i = 0; i != partCount; ++i)
{
QGraphicsRectItem* part = new QGraphicsRectItem(QRectF(0, 0, 10, 10), this);
part->setPos((partCount - i) * 10, 0); // Create a snake from left to right
parts << part;
}
}
void move(Direction direction)
{
QPointF const offset = getOffset(direction);
QPointF previousPartPosition = parts.first()->pos();
parts.first()->setPos(parts.first()->pos() + offset);
for( int i = 1; i != partCount; ++i)
{
QGraphicsRectItem* part = parts.at(i);
QPointF const partPosition = part->pos();
part->setPos(previousPartPosition);
previousPartPosition = partPosition;
}
}
QPointF getOffset(Direction direction)
{
switch (direction) {
case Right:
return QPointF(10, 0);
case Left:
return QPointF(-10, 0);
}
}
private:
QList<QGraphicsRectItem*> parts;
int partCount;
};
Your question isn't completely clear, but you don't need to remove the item(s) from the scene to move a graphic item. Just set a new position on each item. They'll move and redraw in their new locations automatically.
Create the head and the body items initially (which you must already be doing), and then you don't need the "delete head_", nor the addPixmap. Just set the positions.

Error C2280. Attempting to reference a deleted function. Getting this error when trying to delete an enemy and laser from their data structures

I am building my first game in C++/SDL in OOP and have come across an issue that I didn't have in my singular file version of the game.
When checking for collisions, if a collision has been detected, I am wanting the players laser and enemy ship to be destroyed from my data structure but I am getting an error that I am unable to resolve.
Error C2280. Attempting to reference a deleted function.
Without posting too much code and trying to make it easier for you to read I have included what I think is necessary.
'CheckCollisions' is being called from the function that has the data structure holding both enemies and lasers.
Thank you.
game.cpp
void Game::start(Display& display, Media& media, Player& player)
{
//Initialize SDL
if (!display.init())
{
printf("Failed to initailize.", SDL_GetError());
}
else
{
//Load all media
if (!media.load(display))
{
printf("Failed to load media", SDL_GetError());
}
else
{
SDL_Event e;
std::vector<Enemy> enemies; //Holds all enemies
std::vector<Laser> playerLasers; //Holds all of player lasers
//std::vector<Laser> enemyLasers; //Holds all of the enemy lasers
addEnemy(enemies); //Adds enemies to the game
//Begin game loop
while (!m_quit)
{
//Event management
while (SDL_PollEvent(&e) != 0)
{
if(e.type == SDL_KEYDOWN)
{
player.movement(e);
}
if (e.type == SDL_MOUSEBUTTONDOWN)
{
player.shoot(playerLasers);
}
}
//Movement
player.movementBounds();
moveLasers(playerLasers);
moveEnemies(enemies);
//Collision detection
checkCollisions(player, enemies, playerLasers);
//Render
display.render(media, player, enemies, playerLasers);
//Simulate 60 fps - Read on tutorial, not entirely sure if this is ok.
SDL_Delay(16);
}
}
}
}
bool Game::checkCollisions(Player& player, std::vector<Enemy>& enemies, std::vector<Laser>& playerLasers, int& enemyPos)
{
//Check for playerLaser/Enemy collision
for (int playerLaser = 0; playerLaser < playerLasers.size(); playerLaser++)
{
for (int enemy = 0; enemy < enemies.size(); enemy++)
{
//If collision has been detected, delete both playerLaser and Enemy
if (findPlayerLaserCollision(playerLasers[playerLaser], enemies[enemy]))
{
printf("Collision");
playerLasers.erase(playerLasers.begin() + playerLaser);
enemies.erase(enemies.begin() + enemy);
}
}
}
}
Enemy.h
#pragma once
#include <SDL.h>
#include "MoveDirection.h"
struct Enemy
{
Enemy(SDL_Rect pos, int movementSpeed, MoveDirection dir)
{
m_pos = pos;
m_movementSpeed = movementSpeed;
m_dir = dir;
}
SDL_Rect m_pos; //Position of enemy
int m_movementSpeed = 0; //Movement speed of enemy
MoveDirection m_dir; //Movement direction of enemy
const int MIN_X_POS = 0;
const int MAX_X_POS = 750;
};
Laser.h
#pragma once
#include <SDL.h>
#include "MoveDirection.h"
struct Laser
{
Laser(SDL_Rect pos, int movementSpeed, MoveDirection dir)
{
m_pos = pos;
m_movementSpeed = movementSpeed;
m_dir = dir;
}
SDL_Rect m_pos; //Position of enemy
int m_movementSpeed = 0; //Movement speed of enemy
MoveDirection m_dir; //Movement direction of enemy
};
The Enemy class has two const members, so assignment operator can't be generated for this class by the compiler and is defined as deleted (hence the "deleted function" error happens).
You start your loop with for (int enemy = 0; enemy < enemies.size(); enemy++) but inside the loop you call enemies.erase(enemies.begin() + enemy); which means that the loop index is now invalid. You started with a vector of size enemies.size() but you resized it by calling erase in your loop

Cloning objects an arbitrary number of times

I am a beginner C++ programmer using SDL2. Here is my question.
I am trying to clone an object of the same class an arbitrary number of times without having to specify a specific name to each of the new objects.
I define a class Enemy1:
class Enemy1
{
public:
//The dimensions of the enemy
static const int Enemy1_WIDTH = 20;
static const int Enemy1_HEIGHT = 20;
//Maximum axis velocity of the dot
static const int Enemy1_VEL = 10;
//Initializes the variables
Enemy1();
//Moves the enemy
virtual void move();
//Shows the enemy on the screen
virtual void render();
private:
//The X and Y offsets of the enemy
int mPosX, mPosY;
//The velocity of the enemy
int mVelX, mVelY;
};
I define all the functions within the class such as this:
Enemy1::Enemy1()
{
//Initialize the offsets
mPosX = 320;
mPosY = 240;
//Initialize the velocity
mVelX = 5;
mVelY = 5;
}
Then within my main loop:
//Make first enemy
Enemy1 enemy1;
//While application is running
while (!quit)
{
//Handle events on queue
while (SDL_PollEvent(&e) != 0)
{
//User requests quit
if (e.type == SDL_QUIT)
{
quit = true;
}
//Make more enemies
if (SDL_GetTicks() > spawnTime)
{
Enemy1 **arbitrary_name_of_copied_enemy**
spawnTime = spawnTime + spawnTimeInterval;
}
}
How do I go about doing this without having to name each new enemy? How is this problem typically handled? I've looked into copy constructors and cloning but they don't seem to solve this problem.
If you use the copy constructor and presumably store them in a vector or array, then something like vector.push_back(new Enemy(orig)) will do the trick.

SFML Game Slows Down When Shooting Bullets

I am making an asteroids game in C++ using SFML. I seem to have a problem though with shooting bullets. Although the class seems to work each time a bullet is shot the game significantly slows down. This is the code for the spaceship and the bullets. I just can't seem to find what's wrong with it! Thank you for your time.
This is the Code of the Ship:
#include "Spaceship.h"
Spaceship::Spaceship(void){}
Spaceship::~Spaceship(void){}
void Spaceship::LoadResources()
{
if(!shipAnimImg.LoadFromFile("Resources/Images/SpaceshipAnimation.png"))
std::cout <<"Could not locate the ship animation image" <<std::endl;
if(!shipIdleImg.LoadFromFile("Resources/Images/SpaceshipIdle.png"))
std::cout <<"Could not locate the ship idle image" <<std::endl;
if(!bulletImg.LoadFromFile("Resources/Images/Bullet.png"))
std::cout <<"Could not locate the bullet image" <<std::endl;
shipSpr.SetImage(shipIdleImg);
shipSpr.SetScale(0.5,0.5);
shipSpr.SetCenter(shipIdleImg.GetWidth() / 2,shipIdleImg.GetHeight() / 2);
x = DEFAULT_SCREENWIDTH / 2;
y = DEFAULT_SCREENHEIGHT / 2;
shipSpr.SetPosition(x,y);
shipSpr.SetRotation(90);
std::cout<<shipSpr.GetCenter().x<<std::endl;
std::cout<<shipSpr.GetCenter().y<<std::endl;
vx = 0.2;
vy = 0.2;
isBulletOnScreen = false;
isPressed = false;
}
void Spaceship::UnloadResources(){}
void Spaceship::Update(sf::RenderWindow &Window,sf::Event event)
{
if (Window.GetInput().IsKeyDown(sf::Key::A))
{
shipSpr.Rotate(0.08);
}
if (Window.GetInput().IsKeyDown(sf::Key::D))
{
shipSpr.Rotate(-0.08);
}
if (Window.GetInput().IsKeyDown(sf::Key::W))
{
x += (cos(shipSpr.GetRotation() * (3.14159265/180.0)) *0.2);
y -= (sin(shipSpr.GetRotation() * (3.14159265/180.0)) *0.2);
shipSpr.SetPosition(x,y);
}
if (Window.GetInput().IsKeyDown(sf::Key::Space) && !isPressed)
{
isBulletOnScreen = true;
isPressed = true;
bullets.push_back(new Bullet(shipSpr.GetPosition().x,shipSpr.GetPosition().y,0.3,shipSpr.GetRotation(),bulletImg));
}
if (event.Type == sf::Event::KeyReleased)
{
isPressed = false;
}
if(bullets.size() != 0)
{
for (int i=0; i<bullets.size(); i++)
{
bullets[i]->Update(Window,event);
if ((bullets[i]->GetX() > DEFAULT_SCREENWIDTH + 40) || (bullets[i]->GetX() < 0 - 40) ||
(bullets[i]->GetY() > DEFAULT_SCREENWIDTH + 40) || (bullets[i]->GetY() < 0 - 40))
{
bullets.erase(bullets.begin() +i);
}
}
std::cout<<bullets.size()<<std::endl;
}
std::cout<<bullets.size()<<std::endl;
}
void Spaceship::Draw(sf::RenderWindow &Window)
{
if(isBulletOnScreen)
for (int i=0; i<bullets.size(); i++)
{
Bullet *cur = bullets[i];
bullets[i]->Draw(Window);
std::cout<<bullets.size()<<std::endl;
}
Window.Draw(shipSpr);
}
And this is for the Bullet:
#include "Bullet.h"
Bullet::Bullet(void){}
Bullet::Bullet(float x,float y,float v,float r,sf::Image image)
{
LoadResources(x,y,v,r,image);
}
Bullet::~Bullet(void){}
void Bullet::LoadResources(float x,float y,float v,float r , sf::Image image)
{
this->x = x;
this->y = y;
this->v = v;
bulletImg = image;
bulletSpr.SetImage(bulletImg);
bulletSpr.SetScale(0.5,0.5);
bulletSpr.SetCenter(bulletImg.GetWidth() / 2,bulletImg.GetHeight() / 2);
bulletSpr.SetPosition(x,y);
bulletSpr.SetRotation(r);
}
void Bullet::UnloadResources(){}
void Bullet::Update(sf::RenderWindow &Window,sf::Event event)
{
x += (cos(bulletSpr.GetRotation() * (3.14159265/180.0)) *v);
y -= (sin(bulletSpr.GetRotation() * (3.14159265/180.0)) *v);
bulletSpr.SetPosition(x,y);
}
void Bullet::SetX(float x)
{
this->x = x;
}
void Bullet::SetY(float y)
{
this->y = y;
}
void Bullet::SetRotation(float r)
{
rotation = r;
}
float Bullet::GetX()
{
return x;
}
float Bullet::GetY()
{
return y;
}
void Bullet::Draw(sf::RenderWindow &Window)
{
Window.Draw(bulletSpr);
}
EDIT: Changed the code so that it loads the image inside the Spaceship Class and passes it to the Bullet's Resources after it is created. The problem still remains the same though. The game becomes more and more slower each time a bullet is shot and it remains slow until it is erased.
1.
You are loading the Bullet PNG image from disk every time you create a new object (often, if you like shooting). The loading from disk is probably going to be very slow. Try to reuse the same image several times instead!
You could probably pull the LoadFromFile function out of LoadResources and put it somewhere where it would last for the duration of the game. Then just have LoadResources refer to that place whenever a new bullet needs to be created. The same goes for any other images or resources that can be reused in your game.
2.
I also saw that you have std::cout in your code. Try removing all of those that are in the rendering loop, as printing is slow.
for (int i=0; i<bullets.size(); i++)
{
Bullet *cur = bullets[i];
bullets[i]->Draw(Window);
std::cout<<bullets.size()<<std::endl; // This is going to be slow
}
3.
You might also want to look at the bullets vector. When adding bullets to it,push_back changes the size of the vector, and that takes some allocation and deallocation of memory each time. A better approach would be to make room for a maximum number of bullets to start with (using the resize function for example) so that the vector doesn't change size whenever a bullet is created.
if (Window.GetInput().IsKeyDown(sf::Key::Space) && !isPressed)
{
isBulletOnScreen = true;
isPressed = true;
bullets.push_back(new Bullet(...); // avoid this
}
Everytime you call a new Bullet
Bullet::Bullet(float x,float y,float v,float r)
{
LoadResources(x,y,v,r);
}
You also call LoadResources(x,y,v,r) which calls
bulletImg.LoadFromFile("Resources/Images/Bullet.png")
and that call read a file from a disk, which is a very slow operation, in order of magnitudes slower than anything else, so your program stops for the duration of the load.