I'm developing a game and I have a tree class. The class has an int called "wood" which keeps the amount of wood left in the tree. There is also a function to keep track of all events. When the value reaches zero I want to delete this object.(By the way I'm using CodeBlocks and SDL library)
The handle_events functions:
void Tree::handle_events(SDL_Event event, int MouseX, int MouseY, int Xoffset, int Yoffset) {
if(event.type == SDL_MOUSEBUTTONDOWN) {
if( event.button.button == SDL_BUTTON_LEFT ) {
if((MouseX >= (xPos - Xoffset)) && (MouseX <= ((xPos + 50) - Xoffset)) && (MouseY >= (yPos - Yoffset)) && (MouseY <= ((yPos + 50) - Yoffset))) {
selected = true;
} else {
selected = false;
}
}
}
if(wood <= 0) {
delete this;
}
}
When I launch the game and "wood" gets to zero the tree is still there and working.
Please help
EDIT:
while(SDL_PollEvent(&event)) {
MouseX = event.motion.x;
MouseY = event.motion.y;
menu_button.handle_button_events(event, MouseX, MouseY);
exit_button.handle_button_events(event, MouseX, MouseY);
for(int i = 0; i < trees.size(); i++)
{
trees[i].handle_events(event, MouseX, MouseY, Xoffset, Yoffset);
}
for(int i = 0; i < stones.size(); i++)
{
stones[i].handle_events(event, MouseX, MouseY, Xoffset, Yoffset);
}
for(int i = 0; i < bushes.size(); i++)
{
bushes[i].handle_events(event, MouseX, MouseY, Xoffset, Yoffset);
}
if(event.type == SDL_QUIT) {
running = false;
}
if(event.type == SDL_KEYDOWN) {
switch(event.key.keysym.sym) {
case SDLK_ESCAPE:
running = false;
}
}
if(exit_button.clicked) {
running = false;
}
if(menu_button.clicked) {
paused = true;
}
}
Trees is a vector containing all trees on the map
What is trees in trees[i]? I presume it is std::vector<Tree*> or similar?
When you call delete this you are deleting the object referenced by trees[i], but you are not deleting the entry in the vector. After the deletion trees[i] points to some freed up memory which - if not overwritten by something else - will still look like a tree object.
I would suggest doing something like this:
for(int i = trees.size()-1; i >= 0; i--) {
trees[i].handle_events(event, MouseX, MouseY, Xoffset, Yoffset);
if (trees[i].isEmpty()) {
delete trees[i];
trees.erase(trees.begin()+i);
}
}
Note, that iterating from the end is important, because when element is removed, all the follow-ups will be shifted.
If the vector is big and you do a lot of deletions, consider using a different structure which does not provide random access, since deletion from a vector can be time consuming.
Update:
I assume the member function Tree::isEmpty() is implemented by you with the logic specifying when an object is empty and no longer needed, but without actually deleting the object.
Since, as you say, trees is a vector of Tree objects, and not pointers of thereof, you should never delete those objects yourself! The vector is the owner of those objects and it will delete them.
For that reason, you should just call trees.erase(trees.begin()+i) and it will discard the object.
delete this does not actually clean up pointers that still exist to the object. (It does call the destructor -- but since you have an empty destructor, no such cleanup gets done here, either.) After you delete this, there will still be a (dangling!) pointer to the deleted Tree in the trees vector.
One possible result of using this pointer is that the tree still appears, another possibility is a crash. There is no way to tell a dangling pointer from a valid pointer using just the pointer.
For this reason among others, delete this is often a bad idea and never a simple solution.
You could consider checking whether a tree has run out of wood from the function that calls handle_events() -- then you can both delete the tree, and also remove the pointer from the vector.
Related
i try to making a little game in SFML (C++) So i have a simple Problem with pick up an object by a player. The object is in a vector, i have to objects on the screen and when the player intersect(Collision) with them they have to erase. But still booth objects erase when the player intersect with a single object, how i can fix ?
so my question is: How can i erase (or delete) a single object from a vector who is collide with a player?
here is my code
RectangleShape object;
std::vector<RectangleShape> objects(3);
RectangleShape player;
...
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event));
...
for (int j = 0; j < objects.size(); j++){
objects[j].setFillColor(Color::Cyan);
objects[1].setSize(Vector2f(100, 100));
objects[1].setPosition(Vector2f(650, 1000));
objects[2].setSize(Vector2f(100, 100));
objects[2].setPosition(Vector2f(650, 700));
}
for (auto& object: objects){
FloatRect playerBounds = player.getGlobalBounds();
FloatRect objectBounds = object.getGlobalBounds();
for ( auto it = objects.begin(); it != objects.end();)
{
if(objectBounds. intersects(playerBounds)) {
it = objects.erase(it);
}
else
{
++it;
}
window.draw(objects[1]);
window.draw(objects[2]);
}
}
I tried to keep as much from your code as it was possible so you could see the difference, not the whole different code. The reason why your code wasn't deleting just one obj. and every single is that your loops weren't correctly written.
See this:
for (auto& object: objects){ //iterating through objects
FloatRect playerBounds = player.getGlobalBounds();
FloatRect objectBounds = object.getGlobalBounds(); //<- this *1*
for ( auto it = objects.begin(); it != objects.end();) //iterating through every single one AGAIN
{
if(objectBounds. intersects(playerBounds)) { //what this if does is that it check if this *1* obj. intersects with your player
it = objects.erase(it); //however it doesn't particularly erase that *1* obj., it just erases every obj. in the vector, because when that if is true it will iterate through the vector and delete one by one.
}
else
{
++it;
}
}
The main problem is that 2nd iteration for ( auto it = objects.begin(); it != objects.end();) and if in it are 2 separate things.
There are many solutions to make it work, but then it would be just adding more code to make more spaghetti, so just redo it to make it better.
Here is the working ver:
RectangleShape object;// not needed
std::vector<RectangleShape> objects(3);
for (int j = 0; j < objects.size(); j++) {
objects[j].setFillColor(Color::Cyan);
objects[j].setSize(Vector2f(100, 100));
objects[j].setPosition(Vector2f(650, 1000 - j * 300)); //just a quick way to set position of "every" to be 1000, 700, 400 etc.
//objects[1].setSize(Vector2f(100, 100));
//objects[1].setPosition(Vector2f(650, 1000)); // shouldn't set the obj[1] parameters withing loop since you are setting them the n times where n is number of objs. in the vector
//objects[2].setSize(Vector2f(100, 100));
//objects[2].setPosition(Vector2f(650, 700));//and you should just delete it from there
}
RectangleShape player;
player.setSize(Vector2f(100, 100));
player.setPosition(Vector2f(650, 1000)); //setting player parameters separately since it is not in the vector
// Also I moved making and setting of player and other obj in the vector out of the game loop so it won't be creating them every single frame
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event));
...
for (auto it = objects.begin(); it != objects.end();) {
FloatRect playerBounds = player.getGlobalBounds();
FloatRect objectBounds = it->getGlobalBounds(); //iterator is just similar to a pointer, so to access what it points to you need to use "->" or "*" instead of "."
if (objectBounds.intersects(playerBounds)) {
it = objects.erase(it);
}
else
it++;
}
for (auto& object : objects) { //drawing every single obj. in the vector
window.draw(object);
}
window.draw(player); //drawing player separately since it is not in the vector
}
For the tutorial part that you mentioned in the comments, I would recommend you checking ones from sfml site since they not only contain descriptions of methods, etc. but also demo code.
Trying to make a collision system in sfml for the first time in SFML without using a tutorial, using a array-based thing like so:
bool moright, moleft, moup, xcollision, ycollision;
float xvel, yvel;
int position, predictx, predicty, cm, arraynum;
class playerClass{
public:
playerClass(){
}
void update()
{
if (moright == true){
xvel = 2;}
if (moleft == true){
xvel = -2;}
if (!(moright || moleft)){
if (xvel < 0)
xvel = 0;
if (xvel > 0)
xvel = 0;}
}
};
int main()
{
playerClass playerObject;
// Create the main window
RenderWindow window(VideoMode(1080, 720), "SFML window");
// Load a sprite to display
Texture texture;
if (!texture.loadFromFile("gsquare100x100.png"))
return EXIT_FAILURE;
Sprite sprite(texture);
Sprite floor(texture);
Sprite wall(texture);
floor.setPosition(Vector2f(0.f, 498.f));
wall.setPosition(Vector2f(598.f,0.f));
floor.setColor(Color(0, 255, 0));
floor.setScale(12.f, 12.f);
wall.setScale(12.f, 12.f);
wall.setColor(Color(0, 0, 255));
int collisions[2][4]{
{0, 400, 500, 600},
};
// Start the game loop
while (window.isOpen())
{
Vector2f position = sprite.getPosition();
cout << position.y << endl;
predictx = position.x + xvel;
predicty = position.y + yvel;
yvel = 1;
for (arraynum = 0; arraynum < 2; arraynum++){
if ((predictx > collisions[arraynum][0])&&(predictx < collisions[arraynum][1])&&(predicty > collisions[arraynum][2])&&(predicty < collisions[arraynum][3])){
if ((position.y > collisions[arraynum][3])||(position.y < collisions[arraynum][2])){
xcollision = true;}
if ((position.x > collisions[arraynum][1])||(position.x < collisions[arraynum][0])){
ycollision = true;}
}
}
if (xcollision == true)
xvel = 0;
xcollision = false;
if (ycollision == true)
yvel = 0;
ycollision = false;
sprite.move(sf::Vector2f(0.f+xvel, 0.f+yvel));
// Process events
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == Event::KeyPressed)
{if (event.key.code == Keyboard::D)
moright = true;
if (event.key.code == Keyboard::A)
moleft = true;}
if (event.type == Event::KeyReleased)
{if (event.key.code == Keyboard::D)
moright = false;
if (event.key.code == Keyboard::A)
moleft = false;}
playerObject.update();
}
However the collision never registers, removing the bit that checks from which direction the sprite is moving in from doesn't help.
Very new to c++ so apologies if this is a stupid question and for my likely overly elaborate collision system.
I've written simple collisions with SFML before, and here's my advice to you: make your code as readable as possible! Things are going to get more complicated, and you need to have a system is reusable and easy to understand.
I've read your code but I don't understand why you've used an array. I assume you're trying to check if a smaller rectangle sprite is about to exit the collisions array?
For this purpose I suggest using a FloatRect object. It has useful functions like .contains() and .intersects() that you might need in the future. One downside it that is has top and left only, and to make it more and short, we'll define a simple struct to handle that part for us, as well as work for rectangular sprites as well.
I've left comments that explain the code, but haven't tested it personally. You can do that and integrate what you've learned into your project. Good luck
#include <SFML/Graphics.hpp>
#include <SFML/System.hpp>
using namespace sf;
//using a struct is not necessarily faster. BUT it does give your code more readability and is reusable for future needs
//this struct just stores a floatRect of the given sprite/Floatrecct, defining some useful functions allowing for shorter code and more readability
struct rectangularShape
{
FloatRect containingRectangle;
//constructor with sprite input
rectangularShape(Sprite sprite)
{
this -> containingRectangle = FloatRect(Vector2f(sprite.getGlobalBounds().left, sprite.getGlobalBounds().top),
Vector2f(sprite.getGlobalBounds().left + sprite.getGlobalBounds().width,sprite.getGlobalBounds().top + sprite.getGlobalBounds().height));
}
//constructor with floatrect
rectangularShape(FloatRect rect)
{
this -> containingRectangle = rect;
}
//any useful functions for rectangular shapes-- you could add more if you want
float getTop() {return containingRectangle.top;}
float getbottom() {return containingRectangle.top + containingRectangle.height;}
float getleft() {return containingRectangle.left;}
float getright() {return containingRectangle.left + containingRectangle.width;}
};
//note the use of a FloatRect instead of the arrays you were using, this just makes it easier to understand
FloatRect inclusionArea(TopLeftVector, BottomRightVector);
Sprite sprite(texture);
//declare rectangularShapes, here we check if smallRectangle is exiting it's container
rectangularShape containingRectangle(inclusionArea);
rectangularShape smallRectangle(sprite);
//alternatively you can use the sprite's next position:
/*
spriteCopy = sprite;
spriteCopy.move(deltaTime * Vector2f(xSpeed, ySpeed));
rectangularShape smallRectangle(spriteCopy);
*/
//do the check:
if (smallRectangle.getTop() < containingRectangle.getTop() or smallRectangle.getBottom() > containingRectangle.getBottom())
//exiting in Y axis
//do something;
;
if (smallRectangle.getLeft() < containingRectangle.getLeft() or smallRectangle.getRight() > containingRectangle.getRight())
//exiting in X axis
//do something;
;
I can't comment due to low reputation.
From the code presented, it's seems like you never set xcollision or ycollision to true anywhere.
I'm making a small OpenGL program for my intro to C++ class in Uni. I have a program that is complete but I want to change it up a bit to make it more unique. I have a Cube class:
class Cube {
public:
Cube(Mesh* mesh, Texture2D* texture, float x, float y, float z);
~Cube();
void Draw();
void Update(float rSpeed);
Vector3 position;
private:
GLfloat rotationSpeed;
Vector3 rotationVector;
Mesh* _mesh;
Texture2D* _texture;
};
I then create an array of type Cube:
Cube* cubes[CUBE_AMOUNT];
I then fill each index of this array with data to draw the cube on screen later in the program:
for (int i = 0; i < CUBE_AMOUNT; i++) {
float x = ((rand() % 400) / 10.0f) - 20.0f;
float y = ((rand() % 200) / 10.0f) - 10.0f;
float z = -(rand() % 1000);
if (i % 2 == 1) {
cubes[i] = new Cube(cubeMesh, textureStars, x, y, z);
}
else {
cubes[i] = new Cube(cubeMesh, texturePenguins, x, y, z);
}
}
With this new thing I want to add to the program, I want to check whether an index of cubes[] has been filled with the data yet. However I keep getting exceptions when running. I have tried to check whether cubes[i] is equal to nullptr, and tried checking whether it is NULL too, but neither seem to match.
Sorry for any errors in terminology that I used. New to C++, and having come from only doing Python before this, it is confusing!
Solution:
When I create the array, I changed it to Cube* cubes[CUBE_AMOUNT] = { NULL }, and now when checking the array, cubes[i] == NULL!
If cubes is not a global variable, you can use:
Cube* cubes[CUBE_AMOUNT] = {};
to initialize all the elements to nullptr.
You can also use:
std::vector<std::unique_ptr<Cube>> cubes(CUBE_AMOUNT);
to remove the burden of having to deallocate dynamic memory in your code.
In either case, can use:
if ( cubes[index] )
{
// Got a valid pointer. Use it.
}
Your cubes variable is not automatically initialized with null_ptr's. Until you either fill it with null_ptr's or good pointers it initially points to random garbage.
I think this would work
//This bit should check if theres anything stored currently.
cout << "\nWhich Slot would you like to store the informaton in ?(1-10)";
cin >> i;
i--;
if (information[i] != NULL){
// Already written
cout << "THERES SOMETHING HERE";
}
else{
cout << "\nEMPTY!!!!!!!!!";
}
So, I have gotten quite far in my mission to finish a chess game in c++. However, I have hit a bit of a small issue I would like to get some input on, please.
SITUATION:
My PAWN, KING, KNIGHT move validations work perfect. But;
When moving a piece(such as a white ROOK) it follows most of the rules. For example, it will only move vertical or horizontal, it will not pass another white piece, it will not replace a white piece, and lastly it WILL replace a black (opposing) piece.
The problem is when moving it past a another black piece, it allows passing in order to replace a piece that's past it. So lets say we have a white piece at x=2,y=6 and black piece at x=2,y=4, and another black piece at x=2,y=3. The White piece will be allowed to move to move to x=2,y=3, which should not be allowed. Would love to get some input on how to fix this. Current code below.
bool Rook:: canMove(int startx, int starty, int endx, int endy)
{
int i;
if(board[endx][endy] !=NULL && board[endx][endy]->color==color)
return false;
if (startx == ends) //Collision Detection...
{
// Horizontal move
if (starty < endy)
{
// Move down
for (i = starty + 1; i <= endy; ++i)
if (board[startx][i] != NULL && board[startx][i]->color==color)
return false;
}
else
{
// Move up
for (i = starty - 1; i >= endy; --i)
if (board[startx][i] != NULL && board[startx][i]->color==color) //cant allow passing of non color piece
return false;
}
}
else if (starty == endy)
{
// Vertical move
if (startx < endx)
{
// Move right
for (i = startx + 1; i <= endx; ++i)
if (board[i][starty] != NULL && board[i][starty]->color==color)
return false;
}
else
{
// Move left
for (i = startx - 1; i >= endx; --i)
if (board[i][starty] != NULL && board[i][starty]->color==color)
return false;
}
}
else
{
// Not a valid rook move (neither horizontal nor vertical)
return false;
}
return true;
}
your function has refers to a lot of member variables in the class, e.g. ends, color, board, which isn't good, and makes the function hard to test at a unit level
can you test that function standalone? No you can't.
but it looks like your loops aren't breaking when they should (when they have found a valid move perhaps?)
if the function is allowing move to (2,3) as well as (2,4), then it is looping past (2,4) to (2,3)
also, just using an array and ints for indexing the board isn't very good.
i would have expected a higher-level board class and maybe a coordinate class so you can easily iterate and index the board.
I am working on an SFML game and for some reason after spawning the player, the player gets teleported down. On the first tick he is correctly positioned, but after wards, he is moved down. Any idea on how to diagnose this problem? I set up breakpoints in the move mechanism, the only place where the player's co-ordinates change, and it seems to happen right after the function ends. This is the main function:
int main(){
App.ShowMouseCursor(false);
mainch.mainchinventory.read();
while (App.IsOpened())
{
sf::Event Event;
while (App.GetEvent(Event))
{
if (Event.Type == sf::Event::Closed)
App.Close();
}
float time = App.GetFrameTime();
mainch.move(time);
App.Clear();
drawall();
App.Display();
}
return EXIT_SUCCESS;
}
Mainch.move(t):
void cmainchar::move(float t){
if (App.GetInput().IsKeyDown(sf::Key::S)) mainchinventory.save();
if (App.GetInput().IsKeyDown(sf::Key::R)) mainchinventory.read();
if (App.GetInput().IsKeyDown(sf::Key::A)) A = true;
else A = false;
if (App.GetInput().IsKeyDown(sf::Key::D)) D = true;
else D = false;
if(grounded)
if (App.GetInput().IsKeyDown(sf::Key::W)) first = true;
if ((App.GetInput().IsKeyDown(sf::Key::I)) && (keyreleased)){
if (mainchinventory.drawmain){
mainchinventory.drawmain = false;
mainchinventory.press(mainchinventory.selectionx, 3);
}
else{
mainchinventory.drawmain = true;
}
keyreleased = false;
}
else if (!App.GetInput().IsKeyDown(sf::Key::I))
keyreleased = true;
if(!mainchinventory.drawmain){
if(App.GetInput().IsKeyDown(sf::Key::Num1)) mainchinventory.press(0, 3);
else if(App.GetInput().IsKeyDown(sf::Key::Num2)) mainchinventory.press(1, 3);
else if(App.GetInput().IsKeyDown(sf::Key::Num3)) mainchinventory.press(2, 3);
else if(App.GetInput().IsKeyDown(sf::Key::Num4)) mainchinventory.press(3, 3);
else if(App.GetInput().IsKeyDown(sf::Key::Num5)) mainchinventory.press(4, 3);
else if(App.GetInput().IsKeyDown(sf::Key::Num6)) mainchinventory.press(5, 3);
else if(App.GetInput().IsKeyDown(sf::Key::Num7)) mainchinventory.press(6, 3);
else if(App.GetInput().IsKeyDown(sf::Key::Num8)) mainchinventory.press(7, 3);
else if(App.GetInput().IsKeyDown(sf::Key::Num9)) mainchinventory.press(8, 3);
else if(App.GetInput().IsKeyDown(sf::Key::Num0)) mainchinventory.press(9, 3);
if(App.GetInput().IsMouseButtonDown(sf::Mouse::Button::Left)){
mainchinventory.dockitems[mainchinventory.selectionx].use();
spells.push_back(cspell());
}
}
else if ((App.GetInput().IsMouseButtonDown(sf::Mouse::Button::Left)) && (mainchinventory.drawmain) && (buttonreleased)){
mainchinventory.checkmouse();
buttonreleased = false;
}
else if (!App.GetInput().IsMouseButtonDown(sf::Mouse::Button::Left))
buttonreleased = true;
int xmap = (View.GetCenter().x - 320) / 40;
int ymap = (View.GetCenter().y - 240) / 40;
if ((xmap != xmapold) || (ymap != ymapold))
maps.read();
xmapold = xmap;
ymapold = ymap;
collisions();
for(std::list<cspell>::iterator i = mainch.spells.begin(); i != mainch.spells.end(); i++)
if (i->move(t))
spells.erase(i);
if (A && left){
animate(2, t);
you.Move(-160 * t, 0);
}
if (D && right){
animate(1, t);
you.Move(160 * t, 0);
}
if (!D && !A)
animate(0, t);
if (up){
if(grounded && first){
jump = 1.25;
first = false;
}
if (jump > 0){
you.Move (0,-250 * t * jump);
jump = jump - 1 * t;
}
if (jump <= 0){
jump = 0.f;
}
}
else{
first = false;
jump = 0.f;
}
if (down){
fall = fall + 10 * t;
you.Move(0, 25 * fall * t);
grounded = false;
}
else{
fall = 0.f;
grounded = true;
}
if(teleport){
mainchinventory.spawn = true;
fall = 0.f;
jump = 0.f;
maps.changemap(maps.nxtmap);
teleport = false;
}
moveview();
}
You don't show the code where you're manipulating the sf::Sprite object, so I (or anyone else, really) can't say for certain, but...that said, I have a strong guess as to what's happening.
I've been familiarizing myself with SFML recently, too, and I encountered this issue myself. What I think is happening is you're calling sf::Sprite::Move(x,y) to move the sprite to position (x,y). This is incorrect; what you should be calling is sf::Sprite::SetPosition(x,y). (Both these functions take will take a 2d vector as an argument instead, btw).
I'm operating under the assumption you're using SFML 1.6, yes? Looks that way...version 2.0 changes the API somewhat, so in case you're using that Sprite::Move() becomes Sprite::move() and Sprite::SetPosition() becomes Sprite::setPosition().
Anyways, to wrap this up: the difference between Move(x,y) and SetPosition(x,y) is that Move adjusts the sprite's position relative to its current position, whereas SetPosition moves the sprite to a new position regardless of where it was before.
Again, this is a blind guess since the relevant code was not included...so was my shot in the dark correct?
Unfortunately, you haven't provided enough code for a straightforward diagnosis. With what you provided however, my best guess is that your fall variable hasn't been initialized. If it hasn't been initialized, its values can be completely random, likely being much larger than expected. This would explain your 'teleporting' behavior.