Having problems moving Snake in Qt - c++

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.

Related

Correctly managing pointers in C++ Quadtree implementation

I'm working on a C++ quadtree implementation for collision detection. I tried to adapt this Java implementation to C++ by using pointers; namely, storing the child nodes of each node as Node pointers (code at the end). However, since my understanding of pointers is still rather lacking, I am struggling to understand why my Quadtree class produces the following two issues:
When splitting a Node in 4, the debugger tells me that all my childNodes entries are identical to the first one, i.e., same address and bounds.
Even if 1. is ignored, I get an Access violation reading location 0xFFFFFFFFFFFFFFFF, which I found out is a consequence of the childNode pointees being deleted after the first split, resulting in undefined behaviour.
My question is: what improvements should I make to my Quadtree.hpp so that each Node can contain 4 distinct child node pointers and have those references last until the quadtree is cleared?
What I have tried so far:
Modifying getChildNode according to this guide and using temporary variables in split() to avoid all 4 entries of childNodes to point to the same Node:
void split() {
for (int i = 0; i < 4; i++) {
Node temp = getChildNode(level, bounds, i + 1);
childNodes[i] = &(temp);
}
}
but this does not solve the problem.
This one is particularly confusing. My initial idea was to just store childNodes as Nodes themselves, but turns out that cannot be done while we're defining the Node class itself. Hence, it looks like the only way to store Nodes is by first creating them and then storing pointers to them as I tried to do in split(), yet it seems that those will not "last" until we've inserted all the objects since the pointees get deleted (run out of scope) and we get the aforementioned undefined behaviour. I also thought of using smart pointers, but that seems to only overcomplicate things.
The code:
Quadtree.hpp
#pragma once
#include <vector>
#include <algorithm>
#include "Box.hpp"
namespace quadtree {
class Node {
public:
Node(int p_level, quadtree::Box<float> p_bounds)
:level(p_level), bounds(p_bounds)
{
parentWorld = NULL;
}
// NOTE: mandatory upon Quadtree initialization
void setParentWorld(World* p_world_ptr) {
parentWorld = p_world_ptr;
}
/*
Clears the quadtree
*/
void clear() {
objects.clear();
for (int i = 0; i < 4; i++) {
if (childNodes[i] != nullptr) {
(*(childNodes[i])).clear();
childNodes[i] = nullptr;
}
}
}
/*
Splits the node into 4 subnodes
*/
void split() {
for (int i = 0; i < 4; i++) {
childNodes[i] = &getChildNode(level, bounds, i + 1);;
}
}
/*
Determine which node the object belongs to. -1 means
object cannot completely fit within a child node and is part
of the parent node
*/
int getIndex(Entity* p_ptr_entity) {
quadtree::Box<float> nodeBounds;
quadtree::Box<float> entityHitbox;
for (int i = 0; i < 4; i++) {
nodeBounds = childNodes[i]->bounds;
ComponentHandle<Hitbox> hitbox;
parentWorld->unpack(*p_ptr_entity, hitbox);
entityHitbox = hitbox->box;
if (nodeBounds.contains(entityHitbox)) {
return i;
}
}
return -1; // if no childNode completely contains Entity Hitbox
}
/*
Insert the object into the quadtree. If the node
exceeds the capacity, it will split and add all
objects to their corresponding nodes.
*/
void insertObject(Entity* p_ptr_entity) {
if (childNodes[0] != nullptr) {
int index = getIndex(p_ptr_entity);
if (index != -1) {
(*childNodes[index]).insertObject(p_ptr_entity); // insert in child node
return;
}
}
objects.push_back(p_ptr_entity); // add to parent node
if (objects.size() > MAX_OBJECTS && level < MAX_DEPTH) {
if (childNodes[0] == nullptr) {
split();
}
int i = 0;
while (i < objects.size()) {
int index = getIndex(objects[i]);
if (index != -1)
{
Entity* temp_entity = objects[i];
{
// remove i-th element of the vector
using std::swap;
swap(objects[i], objects.back());
objects.pop_back();
}
(*childNodes[index]).insertObject(temp_entity);
}
else
{
i++;
}
}
}
}
/*
Return all objects that could collide with the given object
*/
std::vector<Entity*> retrieve(Entity* p_ptr_entity, std::vector<Entity*> returnObjects) {
int index = getIndex(p_ptr_entity);
if (index != -1 && childNodes[0] == nullptr) {
(*childNodes[index]).retrieve(p_ptr_entity, returnObjects);
}
returnObjects.insert(returnObjects.end(), objects.begin(), objects.end());
return returnObjects;
}
World* getParentWorld() {
return parentWorld;
}
private:
int MAX_OBJECTS = 10;
int MAX_DEPTH = 5;
World* parentWorld; // used to unpack entities
int level; // depth of the node
quadtree::Box<float> bounds; // boundary of nodes in the game's map
std::vector<Entity*> objects; // list of objects contained in the node: pointers to Entitites in the game
Node* childNodes[4];
quadtree::Box<float> getQuadrantBounds(quadtree::Box<float> p_parentBounds, int p_quadrant_id) {
quadtree::Box<float> quadrantBounds;
quadrantBounds.width = p_parentBounds.width / 2;
quadrantBounds.height = p_parentBounds.height / 2;
switch (p_quadrant_id) {
case 1: // NE
quadrantBounds.top = p_parentBounds.top;
quadrantBounds.left = p_parentBounds.width / 2;
break;
case 2: // NW
quadrantBounds.top = p_parentBounds.top;
quadrantBounds.left = p_parentBounds.left;
break;
case 3: // SW
quadrantBounds.top = p_parentBounds.height / 2;
quadrantBounds.left = p_parentBounds.left;
break;
case 4: // SE
quadrantBounds.top = p_parentBounds.height / 2;
quadrantBounds.left = p_parentBounds.width / 2;
break;
}
return quadrantBounds;
}
Node& getChildNode(int parentLevel, Box<float> parentBounds, int quadrant) {
static Node temp = Node(parentLevel + 1, getQuadrantBounds(parentBounds, quadrant));
return temp;
}
};
}
Where Box is just a helper class that contains some helper methods for rectangular shapes and collision detection. Any help would be greatly appreciated!

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;
}

Moving player on board

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.

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.

platform game using sfml 1.6 in c++ collision using AABB's

I am using tile mapping and have my map class in place to draw the map by using an array of sprites. i have it to set the position of the sprite and then create a bounding box array around it and then draw the sprite.
i then have a collision class which gets the player bounding box and compares it with each bounding box for the sprite. i have an array called platformboundingBox. this stores each bounding box of each sprite in the array. however when i compare the values it seems that the platform bounding box has no values in any of the locations yet the i have checked that the values of each sprite go into the bounding box array.
here is my map class. see the drawmap and collision functions to take a look. if anyone can help i would really appreciate it.
#include "Map.h"
#include "Block.h"
#include <sstream>
using namespace std;
Map::Map()
{
//map ctor;
}
Map::~Map()
{
// map dtor
}
void Map::Initialise(const char *filename)
{
if(!BlockImage.LoadFromFile("Images/block.png"))
cout<<endl<<"failed to load block image"<<endl;
if(!GemImage.LoadFromFile("Images/Gem.png"))
cout<<endl<<"failed to load Gem Image"<<endl;
if(!leftBlockImage.LoadFromFile("Images/blockLeft.png"))
cout<<endl<<"failed to load left block Image"<<endl;
if(!rightBlockImage.LoadFromFile("Images/blockRight.png"))
cout<<endl<<"failed to load right block Image"<<endl;
std::ifstream openfile(filename);
std::vector <int> tempvector;
std::string line;
while(std::getline(openfile, line))
{
for(int i =0; i < line.length(); i++)
{
if(line[i] != ' ') // if the value is not a space
{
char value = line[i];
tempvector.push_back(value - '0');
}
}
mapVector.push_back(tempvector); // push back the value of the temp vector into the map vector
tempvector.clear(); // clear the temp vector readt for the next value
}
}
void Map::DrawMap(sf::RenderWindow &Window)
{
Player playermap;
for(i = 0; i < mapVector.size(); i++)
{
for(j = 0; j < mapVector[i].size(); j++)
{
if(mapVector[i][j] == 1)
{
sprite[j].SetImage(BlockImage);
sprite[j].SetPosition(j * BLOCKSIZE, i * BLOCKSIZE);
platformBoundingBox[j].Bottom = sprite[j].GetPosition().y;
platformBoundingBox[j].Left = sprite[j].GetPosition().x - 5;
platformBoundingBox[j].Right = sprite[j].GetPosition().x;
Window.Draw(sprite[j]);
}
else if(mapVector[i][j] == 2)
{
sprite[j].SetImage(GemImage);
sprite[j].SetPosition(j * BLOCKSIZE, i * BLOCKSIZE);
platformBoundingBox[j].Top = sprite[j].GetPosition().y - 5;
platformBoundingBox[j].Bottom = sprite[j].GetPosition().y;
platformBoundingBox[j].Left = sprite[j].GetPosition().x - 5;
platformBoundingBox[j].Right = sprite[j].GetPosition().x;
Window.Draw(sprite[j]);
}
else if(mapVector[i][j] == 3)
{
sprite[j].SetImage(leftBlockImage);
sprite[j].SetPosition(j * BLOCKSIZE, i * BLOCKSIZE);
platformBoundingBox[j].Top = sprite[i].GetPosition().y - 5;
platformBoundingBox[j].Bottom = sprite[i].GetPosition().y;
platformBoundingBox[j].Left = sprite[i].GetPosition().x - 5;
platformBoundingBox[j].Right = sprite[i].GetPosition().x;
Window.Draw(sprite[j]);
}
else if(mapVector[i][j] == 4)
{
sprite[j].SetImage(rightBlockImage);
sprite[j].SetPosition(j * BLOCKSIZE, i * BLOCKSIZE);
platformBoundingBox[j].Top = sprite[i].GetPosition().y - 5;
platformBoundingBox[j].Bottom = sprite[i].GetPosition().y;
platformBoundingBox[j].Left = sprite[i].GetPosition().x - 5;
platformBoundingBox[j].Right = sprite[i].GetPosition().x;
Window.Draw(sprite[j]);
}
}
}
}
void Map::collisions(float x, float y)
{
Player playermap;
this->x = x;
this->y = y;
playerboundingbox.Top = y - 5;
playerboundingbox.Bottom = y ;
playerboundingbox.Left = x - 5;
playerboundingbox.Right = x;
for(i = 0; i < 100; i++)
{
if(playerboundingbox.Intersects(platformBoundingBox[i]))
cout << " praise the lord";
}
}
Please switch to SFML 2 because 1.6 have a lot of bugs.
Let's say you create a class named handler where you will put:
handler::handler()
// window initialization
Map Map; // here initialize the Map class
/* why don't use Map::Map( ctor) for initialization? */
// player initialization
// run the program as long as the window is open
while (window.isOpen())
{
// check all the window's events that were triggered since the last iteration of the loop
sf::Event event;
while (window.pollEvent(event))
{
// "close requested" event: we close the window
if (event.type == sf::Event::Closed)
window.close();
}
window.clear();
//draw player
Map.DrawMap(); // also need to improve the drawing for less lag
window.display();
update(Map &Map, Player &Player);
// every time to use & because without the compiler will create another object
}
}
update(Map &Map, Player &Player)
{
// verify if exists some interacts
if (intersects(Map &Map, Player &Player))
{
//verify from where and put to the correct position
/* e.g: if we have a collide with a down tile map will say something like this:
Player.setPosition(Player.getPosition().x, (Player.getGlobalBounds().top+Player.getGlobalBounds().height)-(Map.getGlobalBounds().top-(Player.getGlobalBounds().top+Player.getGlobalBounds().height)); */
}
}
intersects(Map &Map, Player &Player)
{
sf::FloatRect fPlayer = Player.getGlobalBounds();
for (int i=0; i<Map.NrOfYourTiles; ++i)
{
sf::FloatRect fMap = YourTile.getGlobalBounds();
if (fPlayer.intersects(fMap))
return 1;
}
return 0;
}
Hope this will help you( the code is in SFML 2.0). You can find a lot more help on forums of the creator sfml-dev.org.