C++ Struct Members not Updating in Funtion - c++

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.

Related

How do I get a destructor on an object in a vector not to throw a failed assertion? [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 1 year ago.
Improve this question
I'm programming a Breakout game in C++. I'm having a HUGE problem that's preventing me from giving the game multi-ball functionality. I think it has something to do with the destructor. Have a look:
for loop for the balls (Driver.cpp):
for (Ball& b : balls) { // Loops over all balls
(...)
// Collision for when you miss
if (b.getYPos() > HEIGHT) { // If the ball falls below the defined height of the screen
balls.erase(balls.begin() + b.getID()); // Wipe the ball out of memory to make room (Troublesome line)
Ball::addToID(-1); // Shift the ball ID to assign to the next ball back one
(...)
}
And I get this error:
Debug Error!
Program: Breakout.exe
abort() has been called
(Press Retry to debug the application)
Do you know why this mysterious crash is happening? Or more importantly, a fix for it?
Here's a replicable piece of code to help:
Driver.cpp:
#include <vector>
#include <allegro5\allegro.h>
#include "Ball.h"
using namespace std;
vector<Ball> balls(0); // Pay attention to this line
const POS WIDTH = 1600, HEIGHT = 900;
int main {
while (running) {
if (al_key_down(&key, ALLEGRO_KEY_SPACE)) { // Spawn the ball
balls.push_back(Ball(WIDTH / 2, 500, 10, 10)); // Spawn the ball
balls[Ball::getIDtoAssign()].setYSpeed(5);
}
for (Ball& b : balls) { // Pay attention to this loop
b.draw(); // This line is what's crashing.
b.move();
(...)
// Collision for when you miss
balls.erase(
remove_if(balls.begin(), balls.end(),
[=](Ball& b) {
// Collision for when you miss
return b.getYPos() > HEIGHT; // If the ball falls below the defined height of the screen, wipe the ball out of memory to make room
}
),
balls.end()
);
}
}
}
}
return 0;
}
Ball.h:
#pragma once
#include <allegro5\allegro_primitives.h>
using namespace std;
class Ball {
public:
Ball();
Ball(float x, float y, float w, float h);
~Ball();
void draw();
void move();
float getYPos();
void setYSpeed(float set);
private:
float xPos; // Horizontal position
float yPos; // Vertical position (upside down)
float width; // Sprite width
float height; // Sprite height
float xSpeed; // Horizontal speed
float ySpeed; // Vertical speed (inverted)
}
Ball.cpp:
#include "Ball.h"
short Ball::ballIDtoAssign = 0;
Ball::Ball() {
this->xPos = 0;
this->yPos = 0;
this->width = 0;
this->height = 0;
this->xSpeed = 0;
this->ySpeed = 0;
}
Ball::Ball(float x, float y, float w, float h) {
this->xPos = x;
this->yPos = y;
this->width = w;
this->height = h;
this->xSpeed = 0;
this->ySpeed = 0;
}
Ball::~Ball() {
// Destructor
}
void Ball::draw() {
al_draw_filled_rectangle(xPos, yPos, xPos + width, yPos + height, al_map_rgb(0xFF, 0xFF, 0xFF));
}
void Ball::move() {
xPos += xSpeed;
yPos += ySpeed;
}
float Ball::getYPos() {
return yPos;
}
void Ball::setYSpeed(float set) {
ySpeed = set;
}
You cannot modify a container while you are iterating through it with a range-for loop. You don't have access to the iterator that the loop uses internally, and erase() will invalidate that iterator.
You can use the container's iterators manually, paying attention to the new iterator that erase() returns, eg:
for(auto iter = balls.begin(); iter != balls.end(); ) { // Loops over all balls
Ball& b = *iter:
...
// Collision for when you miss
if (b.getYPos() > HEIGHT) { // If the ball falls below the defined height of the screen
...
iter = balls.erase(iter); // Wipe the ball out of memory to make room
}
else {
++iter;
}
}
Alternatively, use the erase-remove idiom via std::remove_if() instead:
balls.erase(
std::remove_if(balls.begin(), balls.end(),
[=](Ball &b){
// Collision for when you miss
return b.getYPos() > HEIGHT; // If the ball falls below the defined height of the screen, wipe the ball out of memory to make room
}
),
balls.end()
);
UPDATE: now that you have posted more of your code, it is clear to see that you are trying to use ID numbers as indexes into the vector, but you are not implementing those IDs correctly, and they are completely unnecessary and should be eliminated.
The Ball::ballID member is never being assigned any value, so in this statement:
balls.erase(balls.begin() + b.getID()); // The troublesome line
Trying to erase() the result of balls.begin() + b.getID() causes undefined behavior since the iterator has an indeterminate value, thus you can end up trying to erase the wrong Ball object, or even an invalid Ball object (which is likely the root cause of your runtime crash).
Also, in this section of code:
balls.push_back(Ball(WIDTH / 2, 500, 10, 10)); // Spawn the ball
balls[Ball::getIDtoAssign()].setYSpeed(5);
Ball::addToID(1);
Since you want to access the Ball object you just pushed, that code can be simplified to this:
balls.back().setYSpeed(5);
And I already gave you code further above to show you how to remove balls from the vector without using IDs.
So, there is need for an ID system at all.
With that said, try something more like this:
Driver.cpp:
#include <vector>
...
#include "Ball.h"
using namespace std;
vector<Ball> balls;
const POS WIDTH = 1600, HEIGHT = 900;
int main {
...
while (running) {
...
if (input.type == ALLEGRO_EVENT_TIMER) { // Runs at 60FPS
...
if (al_key_down(&key, ALLEGRO_KEY_SPACE)) { // Spawn the ball
balls.push_back(Ball(WIDTH / 2, 500, 10, 10)); // Spawn the ball
balls.back().setYSpeed(5);
}
for (auto iter = balls.begin(); iter != balls.end(); ) {
Ball &b = *iter;
...
if (b.getYPos() > HEIGHT) { // Collision for when you miss
iter = balls.erase(iter);
}
else {
++iter;
}
}
/* alternatively:
for (Ball& b : balls) {
b.draw();
b.move();
}
balls.erase(
std::remove_if(balls.begin(), balls.end(),
[=](Ball &b){
// Collision for when you miss
return b.getYPos() > HEIGHT; // If the ball falls below the defined height of the screen, wipe the ball out of memory to make room
}
),
balls.end()
);
*/
}
}
return 0;
}
Ball.h:
#pragma once
...
class Ball {
public:
...
// NO ID METHODS!
private:
...
// NO ID MEMBERS!
}
Ball.cpp:
#include "Ball.h"
...
// NO ID MEMBER/METHODS!
OK, so I managed to figure out why the program crashes. It was because I had the erase-remove inside the for loop which can cause all sorts of problems.

Synchronize functions in Cocos2d-x?

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 ...

SFML C++11 can't call the function twice from two different objects of the same type

I am trying to create two RedShip objects. Each of them has a bullets_.create() command which initializes a Bullet object in an array of Bullet objects. I have another similar object called GreenShip. When I instantiate 1 GreenShip and 1 RedShip they both can initialize seperate Bullet objects into the bullets_[] array. But when i have the same object twice only one of them is able to create the Bullet objects even though when debugged they both call the create() method succesfully.
Here is the problematic code:
the ObjectPool class:
class BulletPool{
public:
BulletPool();
void create(sf::Vector2f pos, sf::Color color, float bullet_radius, bool enemy, float velx, float vely);
void update(sf::RenderWindow& window, sf::Vector2f screen);
private:
Bullet* firstAvailable_;
static const int POOL_SIZE = 100;
Bullet bullets_[POOL_SIZE];
};
the BulletPool object constructor:
BulletPool::BulletPool() {
// The first one is available.
firstAvailable_ = &bullets_[0];
// Each particle points to the next. all linked!!
for (int i = 0; i < POOL_SIZE - 1; i++)
{
bullets_[i].setNext(&bullets_[i + 1]);
}
// The last one terminates the list.
bullets_[POOL_SIZE - 1].setNext(NULL);
}
BulletPool update method:
void BulletPool::update(sf::RenderWindow& window, sf::Vector2f screen) {
for (int i = 0; i < POOL_SIZE; i++) {
bullets_[i].update(screen);
if (!bullets_[i].inUse) {
printf("foundavailable:%i\n",i);
bullets_[i].setNext(firstAvailable_);
firstAvailable_ = &bullets_[i];
break;
} else {
std::printf("drawn::%i!\n",i);
window.draw(bullets_[i].shape);
}
}
}
BulletPool.create() which takes in the arguments to be passed to the actual Bullet.init() which initializes the object. The BulletPool.create() is only responsible for placing the new object into the array:
void BulletPool::create(sf::Vector2f pos, sf::Color color, float bullet_radius, bool enemy, float velx, float vely) {
assert(firstAvailable_ != NULL);
printf("new bullet\n");
Bullet* newBullet = firstAvailable_;
firstAvailable_ = newBullet->getNext();
newBullet->init(pos,color,bullet_radius,enemy,velx,vely);
newBullet->inUse=true;
return;
}
Each RedShip object tries to create a Bullet object periodically:
void RedShip::update(int base_time, BulletPool bullets) {
if(base_time%40==0) {
printf("red fire\n");
bullets.create({shape.getPosition().x, shape.getPosition().y + shape.getLocalBounds().height/2}, sf::Color::Red, 4,true, 0,3);
}
}
the console result:
drawn::0!
drawn::1!
foundavailable:2
red fire
new bullet
red fire
new bullet
drawn::0!
drawn::1!
drawn::2!
It should draw 1 more since i am trying to initialize 2 objects.
The Bullet object also has these components as default:
Bullet* getNext() const { return state_.next_; } //to get the next Bullet object in BulletPool
void setNext(Bullet* next) { state_.next_ = next; } //to set the next object
bool inUse{false}; //is the object active?
EDIT
I tried updating the BulletPool after running the create function but have discovered that the first time the method is called, the object is not being initialized into the array. The firstAvaliable stays the same.
foundavailable:4
red fire
new bullet
foundavailable:4
red fire
new bullet
foundavailable:5

SFML sprite.move() not working

I'm making a game in SFML and I'm trying to add shooting, but for some reason sprite.move() doesn't seem to work. Here's the relevant code:
Weapon.cpp
void Weapon::update(float delta, sf::RenderWindow& window, Player player) {
for (auto s : shots) {
s.move(delta);
s.draw(window);
}
switch (type) {
case RANGED:
if (sf::Mouse::isButtonPressed(sf::Mouse::Left)) {
shots.push_back(Shot(shot, player.position, Helper::getMousePos(window)));
Helper::log(std::to_string(shots.size()));
}
break;
case MELEE:
break;
}
}
Shot.cpp
Shot::Shot(sf::Sprite sprite_, sf::Vector2f origin, sf::Vector2f target)
{
sprite = sprite_;
sprite.setPosition(origin);
direction = Helper::normalizeVector(target - origin);
speed = 200;
}
void Shot::move(float delta) {
sprite.move(direction * speed * delta);
}
void Shot::draw(sf::RenderWindow& window) {
window.draw(sprite);
}
What happens is the shots spawn at the player's location, but they don't move. Rather, they kinda vibrate as if they're trying to move but something is stopping them. Let me know if you need more code.
The problem is most likely in the loop
for (auto s : shots) {
s.move(delta);
s.draw(window);
}
Here you create the loop variable s by value, meaning it's a copy of the elements in the container. Modifying a copy will of course not modify the original.
Instead loop using references:
// Notice the ampersand here
// |
// v
for (auto& s : shots) {
s.move(delta);
s.draw(window);
}

std::vector memory, vector of unwanted 0's

My Code works for my purely glut implementation, but I am trying to get it to work in qt.
I have a vector of masspoints for a wire mesh system
std::vector<masspoint> m_particles;
The problem is in my qt version none of what I write really sticks and I am left with an array of zeros. Basically I am confused why the glut version has correct values but the qt one does not given that it is basically identical code. What is wrong with the qt code?
Yes I only see zeros when using qDebug. When I am calling my drawing function in the qt version all vertex points turn out to be 0 in all components so nothing is seen.
int myboog = 1;
int county = 0;
// Constructors
Cloth::Cloth(float width, float height, int particles_in_width, int particles_in_height):
m_width(particles_in_width),
m_height(particles_in_height),
m_dimensionWidth(width),
m_dimensionHeight(height),
m_distanceX(width/(float)particles_in_width),
m_distanceY(height/(float)particles_in_height)
{
//Set the particle array to the given size
//Height by width
//mparticles is the name of our vector
m_particles.resize(m_width*m_height);
qDebug() << m_particles.size();
// Create the point masses to simulate the cloth
for (int x = 0; x < m_width; ++x)
{
for (int y=0; y < m_height; ++y)
{
// Place the pointmass of the cloth, lift the edges to give the wind more effect as the cloth falls
Vector3f position = Vector3f(m_dimensionWidth * (x / (float)m_width),
((x==0)||(x==m_width-1)||(y==0)||(y==m_height-1)) ? m_distanceY/2.0f:0,
m_dimensionHeight * (y / (float)m_height));
// The gravity effect is applied to new pmasspoints
m_particles[y * m_width + x] = masspoint(position,Vector3f(0,-0.06,0));
}
}
int num = (int)m_particles.size();
for (int i=0; i<num; ++i)
{
masspoint* p = &m_particles[i];
if(myboog)
{
qDebug() << "test " << *p->getPosition().getXLocation() << county;
county++;
}
}
myboog = 0;
// Calculate the normals for the first time so the initial draw is correctly lit
calculateClothNormals();
}
Code for masspoint involved in constructor for CLoth
#ifndef MASSPOINT_H
#define MASSPOINT_H
#include <QGLWidget>
#include "vector3f.h"
class masspoint
{
private:
Vector3f m_position; // Current Location of the pointmass
Vector3f m_velocity; // Direction and speed the pointmass is traveling in
Vector3f m_acceleration; // Speed at which the pointmass is accelerating (used for gravity)
Vector3f m_forceAccumulated; // Force that has been accumulated since the last update
Vector3f m_normal; // Normal of this pointmass, used to light the cloth when drawing
float m_damping; // Amount of velocity lost per update
bool m_stationary; // Whether this pointmass is currently capible of movement
public:
masspoint& operator= (const masspoint& particle);
//Some constructors
masspoint();
masspoint(const masspoint& particle);
masspoint(Vector3f position, Vector3f acceleration);
//Like eulur integration
void integrate(float duration);
// Accessor functions
//Get the position of the point mass
inline Vector3f getPosition() const {return m_position;}
Vector stuff involved in the constructor for CLoth
#ifndef VECTOR3F_H
#define VECTOR3F_H
#include <math.h>
// Vector library to be used
class Vector3f
{
private:
float m_x, m_y, m_z;
public:
const float* getXLocation() const { return &m_x; }