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.
Related
// spawn
if (Keyboard::isKeyPressed(Keyboard::Q)) // coal
{
particleR = new RectangleShape;
particleV.push_back(*particleR);
particleV[particleV.size() - 1].setSize(Vector2f(1, 1));
particleV[particleV.size() - 1].setFillColor(Color(55, 55, 55));
particleV[particleV.size() - 1].setPosition(Vector2f(worldPosI));
parID.push_back(1);
}
for (int i = 0; i < particleV.size(); i++)
{
if (parID[i] == 1) // checks if its coal
{
if (particleV[i].getPosition().y < 10) // checks if the particle is in air
{
particleV[i].move(0, 1);
for (int q = 0; q < particleV.size(); q++) // goes through each particle with each other and checks if some particle is inside
{
if (particleV[i].getPosition().x == particleV[q].getPosition().x && particleV[i].getPosition().y == particleV[q].getPosition().y && i != q)
{
particleV[i].move(0, -1); // if a particle is inside other particle, go up.
}
}
}
}
}
The problem is that, these for loops can not handle going through thousands of particles every frame and checking of they are colliding with each other. I wonder if there is a better another way of checking the interactions between particles.
Firstly, while not new to programming, I am very new to C++, so please bear with me.
I am using the Raylib library to attempt making a particle system for a game.
This consists of a struct with a few private members and public functions:
struct Particle {
Particle() {
mPosVector = {(float)GetMouseX(), (float)GetMouseY()};
mVelVector = {(float)GetRandomValue(15, 70)/100, (float)GetRandomValue(15, 70)/100};
mSize = GetRandomValue(5, 15);
}
void update(double deltaTime) {
mPosVector.x += mVelVector.x;
mPosVector.y += mVelVector.y;
}
void draw() {
DrawRectangleV(mPosVector, {(float)mSize, (float)mSize}, WHITE);
}
private:
Vector2 mPosVector;
Vector2 mVelVector;
int mSize;
};
The Vector2 type is defined by Raylib:
struct Vector2 {
float x;
float y;
};
In my main function I have an std::vector storing Particles. A particle gets added when the left mouse button is pressed. I loop through the Particles vector twice, once for updating position based on velocity and once for drawing. I was originally doing these both in one loop, but was still getting the problem that I will get onto, so tried it this way.
This is the current code:
std::vector<Particle> particles = {Particle()};
while (!WindowShouldClose()) {
deltaTime = GetFrameTime();
if (IsMouseButtonDown(0)) {
particles.push_back(Particle());
}
for (Particle part : particles) {
part.update(deltaTime);
}
BeginDrawing();
ClearBackground(BLACK);
DrawFPS(10, 10);
DrawText((numToString<double>(deltaTime*1000).substr(0, 5) + "ms").c_str(), 10, 40, 20, WHITE);
for (Particle part : particles) {
part.draw();
}
EndDrawing();
So, my problem: While particles are being instantiated as expected while pressing the left mouse button and being drawn, for some reason their positions are not being updated by their velocity. I have tried printing debug information to the console, such as the velocity, and it is as expected, but for some unknown reason to me (probably just me being stupid) their positions aren't being updated.
Any help would be greatly appreciated.
for (Particle part : particles) {
part.update(deltaTime);
}
this is making a copy of each entry , you need
for (Particle &part : particles) {
part.update(deltaTime);
}
to get a reference to the object in the vector to update it in place
To understand, think that the ranged for is just short hand for this
for(int i = 0; i < particles.size(); i++)
{
// this line copies the value
particle p = particles[i];
}
whereas the one with & in it does
for(int i = 0; i < particles.size9); i++)
{
// this line gets a reference to the ith entry
particle &p = particles[i];
}
Its nothing special to do with the ranged for loop.
I use cocos2d-x-3.13...
I'm just starting out but I've had problems with something: I have a number of sprites in a vector and every second I move them to each position, the problem arises when I want to delete them, since I have a function that moves them With a loop:
HelloWorldScene.cpp
bool HelloWorld::init() {
...
_enemies.reserve(15);
for (unsigned i = 0; i < 5; i++) {
//Here I create the sprites, and I activate the physics in each one :p
_enemies.push_back(sprite);
}
...
/*
This event checks for each collision, what I do is find the sprite
in my vector and then delete the sprite with "removeFromParent ()",
then delete the sprite from my vector. it's good, no? :v
*/
auto contactListener = EventListenerPhysicsContact::create();
contactListener->onContactBegin = [=](PhysicsContact &contact) {
auto a = contact.getShapeA()->getBody(); //bullet
auto b = contact.getShapeB()->getBody(); //enemy
if (a->getCollisionBitmask() == 2 && b->getCollisionBitmask() == 2) {
a->getNode()->removeFromParent();
auto f = std::find(_enemies.begin(), _enemies.end(), ((cocos2d::Sprite*)b->getNode()));
if (f != _enemies.end()) {
_enemies.at(std::distance(_enemies.begin(), f))->removeFromParent();
_enemies.erase(f);
}
}
return true;
};
this->getEventDispatcher()->addEventListenerWithSceneGraphPriority(contactListener, this);
...
this->schedule(schedule_selector(HelloWorld::moveEnemies), 1.0f);
...
}
void HelloWorld::moveEnemies(float f) {
for (unsigned i = 0; i < _enemies.size(); i++)
{
cocos2d::Vec2 pos = _enemies.at(i)->getPosition(); //This line throws the exception
_enemies.at(i)->setPosition(pos.x + 5, pos.y);
}
}
I have an event that when I press a key, creates a sprite and if it collides with one of the enemies just disappears, everything is fine here, very simple, but when you put the "moveEnemies" function and the sheduler everything is complicated. .. by shooting at one of the enemies quickly throws me an exception (Visual Studio 2015):
"Access violation when reading location 0xDDDDDE35"
I think this occurs because is the vector manipulated at the same time by the event and the "moveEnemies" function?
Is it possible to solve this? Or am I doing something wrong? I would appreciate anyone guiding me ...
I found a simple solution, it is possible that someone will serve you ...
We simply add a scheduler to each sprite and the vector would become obsolete.
auto enemy = cocos2d::Sprite::createWithSpriteFrameName("x.png");
enemy->setPosition(x, y);
enemy->setScale(0.5, 0.5);
//bla bla bla...
enemy->schedule([enemy](float x) {
//Code
auto pos = enemy->getPosition();
enemy->setPosition(pos.x + 10, pos.y);
}, 2.0f, "tag");
It is assumed that deleting the sprite in the event would eliminate the scheduler ...
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 9 years ago.
Improve this question
I have programmed a simple top-down car driving game that resembles the first GTA, on the GameBoyAdvance. I have used only vector graphics for doing so, and the GBA doesn't handle it very well; basically with 5 pedestrian instances it lags.
I don't have much experience in optimizing code, so I would like to know if there are some tweaks I could make to my code in order to make it run faster, not depending on the fact that it runs on a GBA.
The collision testing I use is SAT (separating axis theorem) as I've found it to be the easisest one for collision check with vector graphics; the game is very simple itself.
Here is the code:
/*
GTA Vector City
Author: Alberto Taiuti
Version: 2.0
*/
#include "Global.h"
#include <string.h>
#include <cstdio>
#include "font.h"
#include "CVector2D.h"
#include "CCar.h"
#include "CPed.h"
#include <vector>
#include <memory>
/* GLOBAL VARIABLES */
void CheckCollisionsRect(CRect *test_a, CRect *test_b);
std::vector<CVector2D> PrepVectors(CRect *shape);
CVector2D GetMinMaxShape(std::vector<CVector2D> vect_shape, CVector2D axis);
void CheckCollisionRectVSPoint(CRect *test_a, CVector2D *point);
/* MAIN */
// The entry point for the game
int main()
{
// Frame counter
uint32_t frames = 0;
// Previous & current buttons states
static uint16_t prev_buttons = 0, cur_buttons = 0;
// Put the display into bitmap mode 3, and enable background 2.
REG_DISPCNT = MODE4 | BG2_ENABLE;
// Set up the palette.
SetPaletteBG(BLACK, RGB(0, 0, 0)); // black
SetPaletteBG(WHITE, RGB(31, 31, 31)); // white
SetPaletteBG(GREY, RGB(15, 15, 15)); // grey
SetPaletteBG(RED, RGB(31, 0, 0)); // red
SetPaletteBG(GREEN, RGB(0, 31, 0)); // green
SetPaletteBG(BLUE, RGB(0, 0, 31)); // blue
// Create car instance
CCar *car = new CCar(50,50);
// Create a building
/*CRect *test_b = new CRect(100.0f, 100.0f, 30, 30);
CRect *test_c = new CRect(120.0f, 120.0f, 30, 30);
CRect *test_d = new CRect(30.0f, 30.0f, 30, 30);*/
// Pedestrian instances
int ped_number = 10; // Number of pedestrians
std::vector<CPed*> peds; // Ped. entities container (made of smart pointers)
typedef std::vector<CPed*>::iterator p_itor; // Iterator
for(int i = 1; i <= ped_number; i++)
{
peds.push_back(new CPed(i, RED, 2.0f));
}
// Check whether the game is over
bool end = false;
// Main loop
while (!end)
{
// Flip the screen
FlipBuffers();
//Clear the screen
ClearScreen8(BLACK);
// Update frame counter
frames ++;
// Get the current state of the buttons.
cur_buttons = REG_KEYINPUT;
// Handle Input
car->HandleInput(prev_buttons, cur_buttons);
// Logic
car->Update();
for(int i = 0; i < ped_number; i++)
{
peds[i]->Update();
}
for(int i = 0; i < ped_number; i++)
{
CheckCollisionRectVSPoint(car->shape, peds[i]->pos);
}
/*CheckCollisionsRect(car->shape, test_b);
CheckCollisionsRect(car->shape, test_c);
CheckCollisionsRect(car->shape, test_d);
CheckCollisionRectVSPoint(car->shape, test_ped->pos);*/
// Render
car->Draw();
for(int i = 0; i < ped_number; i++)
{
peds[i]->Draw();
}
/*test_b->DrawFrame8(GREEN);
test_c->DrawFrame8(WHITE);
test_d->DrawFrame8(RED);
test_ped->Draw();*/
prev_buttons = cur_buttons;
// VSync
WaitVSync();
}
// Free memory
delete car;
//delete test_b; delete test_c; delete test_d;
//delete test_ped;
for(p_itor itor = peds.begin(); itor != peds.end(); itor ++)// Delete pedestrians
{
peds.erase(itor);
}
return 0;
}
void CheckCollisionsRect(CRect *test_a, CRect *test_b)
{
// If the two shapes are close enough, check for collision, otherways skip and save calculations to the CPU
//if((pow((test_a->points[0]->x - test_b->points[0]->x), 2) + pow((test_a->points[0]->y - test_b->points[0]->y), 2)) < 25.0f)
{
// Prepare the normals for both shapes
std::vector<CVector2D> normals_a = test_a->GetNormalsAsArray();
std::vector<CVector2D> normals_b = test_b->GetNormalsAsArray();
// Create two containers for holding the various vectors used for collision check
std::vector<CVector2D> vect_test_a = PrepVectors(test_a);
std::vector<CVector2D> vect_test_b = PrepVectors(test_b);
// Get the min and max vectors for each shape for each projection (needed for SAT)
CVector2D result_P1 = GetMinMaxShape(vect_test_a, normals_a[1]); //
CVector2D result_P2 = GetMinMaxShape(vect_test_b, normals_a[1]); //
// If the two objects are not colliding
if(result_P1.y < result_P2.x || result_P2.y < result_P1.x)
{
return;
}
CVector2D result_Q1 = GetMinMaxShape(vect_test_a, normals_a[0]); // First axis couple
CVector2D result_Q2 = GetMinMaxShape(vect_test_b, normals_a[0]); //
if(result_Q1.y < result_Q2.x || result_Q2.y < result_Q1.x)
{
return;
}
CVector2D result_R1 = GetMinMaxShape(vect_test_a, normals_b[1]); //
CVector2D result_R2 = GetMinMaxShape(vect_test_b, normals_b[1]); //
if(result_R1.y < result_R2.x || result_R2.y < result_R1.x)
{
return;
}
CVector2D result_S1 = GetMinMaxShape(vect_test_a, normals_b[0]); // Second axis couple
CVector2D result_S2 = GetMinMaxShape(vect_test_b, normals_b[0]); //
if(result_S1.y < result_S2.x || result_S2.y < result_S1.x)
{
return;
}
// Do something
PlotPixel8(200, 10, WHITE);
PlotPixel8(200, 11, WHITE);
PlotPixel8(200, 12, WHITE);
}
}
// Check for collision between an OOBB and a point
void CheckCollisionRectVSPoint(CRect *test_a, CVector2D *point)
{
// Prepare the normals for the shape
std::vector<CVector2D> normals_a = test_a->GetNormalsAsArray();
// Create a container for holding the various vectors used for collision check
std::vector<CVector2D> vect_test_a = PrepVectors(test_a);
// Get projections for the OOBB (needed for SAT)
CVector2D result_P1 = GetMinMaxShape(vect_test_a, normals_a[1]);
float result_point = point->DotProduct(normals_a[1]);
// If the two objects are not colliding on this axis
if(result_P1.y < result_point || result_point < result_P1.x)
{
return;
}
CVector2D result_Q1 = GetMinMaxShape(vect_test_a, normals_a[0]);
result_point = point->DotProduct(normals_a[0]);
// If the two objects are not colliding on this axis
if(result_Q1.y < result_point || result_point < result_Q1.x)
{
return;
}
// Do something
PlotPixel8(200, 10, WHITE);
PlotPixel8(200, 11, WHITE);
PlotPixel8(200, 12, WHITE);
}
// Returns a container with projection vectors for a given shape
std::vector<CVector2D> PrepVectors(CRect *shape)
{
std::vector<CVector2D> vect;
// Create vectors for projection and load them into the arrays
for( uint16_t i=0; i < 5; i++)
{
// Get global position of vectors and then add them to the array
vect.push_back(shape->GetVectorGlobal(i));
}
return vect;
}
CVector2D GetMinMaxShape(std::vector<CVector2D> vect_shape, CVector2D axis)
{
// Set initial minimum and maximum for shape's projection vectors
float min_proj = vect_shape[1].DotProduct(axis);
float max_proj = vect_shape[1].DotProduct(axis);
// Calculate max and min projection vectors by iterating along all of the corners
for(uint16_t i = 2; i < vect_shape.size(); i ++)
{
float current_proj = vect_shape[i].DotProduct(axis);
// Select minimum projection on axis
if(current_proj < min_proj) // If current projection is smaller than the minimum one
min_proj = current_proj;
// Select maximum projection on axis
if(current_proj > max_proj) // If current projection is greater than the minimum one
max_proj = current_proj;
}
return (CVector2D(min_proj, max_proj)); // Return a vector2D as it is a handy way for returning a couple of values
}
Many thanks in advance to everyone and sorry for the messy code!
I gave it a really quick reading so I may have overlooked something. Well, there are obvious tips for improving performance such as passing vectors to functions by reference. Using prefix incrementation instead of postfix is also a good habit. These two rules are definitely nothing like 'premature optimization the, root of ...'. Do not delete pedestrians one by one but use std::vector::clear(). And If you claim you use smart pointers, you shoud, because it seems you have memory leak because you did not delete the pedestrian pointers. And use const keyword whereever possible. Once you make the obvious correction, and the speed is still not satisfactory, then you need to use profiler.
And read something about optimization, here for example: http://www.agner.org/optimize/optimizing_cpp.pdf
One thing leaps out at me (apart from the continuous passing of vectors by value rather than reference, which will be incredibly costly!)
In you collision detection, you're seeing if the car hits each pedestrian
for(int i = 0; i < ped_number; i++)
{
CheckCollisionRectVSPoint(car->shape, peds[i]->pos);
}
Then, in the collision detector, you're repeating a lot of the same processing on the car shape every time:-
// Prepare the normals for both shapes
std::vector<CVector2D> normals_a = test_a->GetNormalsAsArray();
// Create two containers for holding the various vectors used for collision check
std::vector<CVector2D> vect_test_a = PrepVectors(test_a);
.. etc...
You should rework that loop to create the normals etc for the car just once, and then reuse the results for each check against a pedestrian.
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.