Trying to create a particle system

Hi everyone
I’ve been having problems with my code. The particle system is not working as expected and I can’t figure out how to solve it.
What I’m trying to do is to make the object disappear when it collides with the player and then move to a random position but so far when they touch nothing happens. Please help!
This is the code: https://github.com/Bcode022/game

I’ve only taken a quick peak, but could it be that this snippet from ofApp.cpp is the culprit?

if (pos_p.x < myPlayer.width && pos_p.x > myPlayer.width && pos_p.y < myPlayer.height && pos_p.y > myPlayer.height) {
        alive = false; //kill self if moused over
 }

Shouldn’t it be something like this to check for inclusion?

if (pos_p.x >= myPlayer.getX() && pos_p.x <= myPlayer.getX() + myPlayer.width && pos_p.y >= myPlayer.getY() && pos_p.y <= myPlayer.getY() + myPlayer.height) {
        alive = false; //kill self if moused over
 }

I’m supposing here that the pos attribute of the “jugador” or player represents the top left corner of the player sprite.

Thank you for your help! I tried doing what you told me but there’s still no collision. I think my problem is the position of the player and the particle but I can’t figure out what to do. I’ll keep trying different things and see what happens

So far no luck, this is driving me nuts :(((

The ofRectangle class has some helpful functions, particularly ofRectangle::inside(). Maybe this recent thread is relevant: Collision problem.

@diff-arch and @TimChi have both really good points to this.
However, it’s not the only problem. I’ve downloaded your project from github and tried to change it according to:

if ( myPlayer.pos.x >= pos_p.x && myPlayer.pos.x < (pos_p.x + myPlayer.width) && myPlayer.pos.y >= pos_p.y && myPlayer.pos.y < (pos_p.y + myPlayer.height) ) {
        alive = false; //kill self if moused over
    }

Which should indeed work, but it didn’t.
Only then did I realize the problem is you have two declarations of jugador class (player in Spanish, eh?)
One in your header ofApp.h (jugador jugador_1) and the one you’re trying to reference you’ve declared as global variable inside ofApp.cpp (jugador myPlayer).

Now the major problem is, the functional jugador class object you’re referencing and updating is jugador_1 (declared in ofApp.h). jugador myPlayer, which you tried to declare globally, is undefined. And your particle update function is referencing myPlayer

ourJugadorIsInAnotherCastle

That’s it. I understand you have no access to jugador_1 from the scope of particle functions, so you tried to use it globally. I suppose you might try to use myPlayer only, delete or comment jugador_1, and set it all up for myPlayer. But I advise against it - it’s always a bad design to code this way because global variables declared in this way are commonly discouraged to use and it might kick you in the butt in the long run…

You have like 2 options: you could pass jugador & playerReference as an argument to particle class function update like this: void update(jugador & playerRef); and then reference playerRef as the object passed in, though you’ll also have to edit this in ofApp::update()

for (int i = 0; i < numParticles; i++) {
        myParticle[i].update(jugador_1);
    }

Now, as @TimChi also suggested, I would prefer working with ofRectangle class in OF (takes x,y, width, height parameters and has convenience functions inside(ofPoint point) and intersects(ofRect rect) ); note that you most probably might want to update the x,y positions of the rectangle in some update loop.

And so, you could make your particle update() function more versatile, passing ofRectangle reference inside…
(inside the class particle:)

void update(ofRectangle & collision);

and then, you can implement inside:

void particle::update(ofRectangle & collision) {
    if (!alive)
        return;

    time = ofGetFrameNum() / 60;
    lifetime = time % 10;

 if ( collision.x >= pos_p.x && collision.x < (pos_p.x + collision.getWidth() ) && collision.y >= pos_p.y && collision.y < (pos_p.y + collision.getHeight()) ) {
         alive = false; //kill self if moused over
     }
}

or you could also make use of:

if( collision.inside(pos_p) ){
		alive = false;
	}

or

// in this case, particle class will have to contain some ofRectangle variable as rectangular hit test area
if( collision.intersects(rectArea) ){
		alive = false;
	}

If you are to have ofRectangle collisions, either jugador class needs to have one ofRectangle (which you will setup and then update its x,y in your update loop), or just simply:

void ofApp::update() {
    seg = ofGetFrameNum() / 60;
    video.update();
    jugador_1.update();

    for (int i = 0; i < numParticles; i++) {
        if (myParticle[i].alive == false) {
            myParticle[i].alive = true;
            myParticle[i].pos_p = ofPoint(ofRandom(50, ofGetWidth() - 50), ofRandom(100, ofGetHeight() - 100));
            return;
        }
    }

	ofRectangle collisionArea = ofRectangle(jugador_1.pos.x, jugador_1.pos.y, jugador_1.width, jugador_1.height);

    for (int i = 0; i < numParticles; i++) {
        myParticle[i].update(collisionArea);
    }
}

Hope this helps…

2 Likes

I’ve found another issue apart from what @mr.Bitmap already mentioned above. You translate and draw the particle offset from its position pos_p, which causes the collision check to fail.

Here’s the revised code that should work now:

ofApp.h

#pragma once

#include "ofMain.h"
#include "Jugador.h"

class Particle {
private:
	ofVec2f vel;
    double r = 10.0;

public:
	Particle();
    
    bool collide(const Jugador& j, double shrink=0.0) const;
	void update();
	void draw();
	int getX() const;
	int getY() const;
    
    ofPoint pos_p;
    unsigned long lifetime;
	unsigned long time;
    bool alive;
};

class ofApp : public ofBaseApp {
public:
	void setup();
	void update();
	void draw();

	void keyPressed(int key);
	void keyReleased(int key);
	void mouseMoved(int x, int y);
	void mouseDragged(int x, int y, int button);
	void mousePressed(int x, int y, int button);
	void mouseReleased(int x, int y, int button);
	void mouseEntered(int x, int y);
	void mouseExited(int x, int y);
	void windowResized(int w, int h);
	void dragEvent(ofDragInfo dragInfo);
	void gotMessage(ofMessage msg);

	Jugador jugador_1;
	ofImage imagen_jugador;
	ofVideoPlayer video;
	ofPoint inicio_jugador;
	ofTrueTypeFont fuente;
    Particle p;
    
};

ofApp.cpp

#include "ofApp.h"

long seg;

const int numParticles = 1;
Particle myParticle[numParticles];

int Particle::getX() const {
    return pos_p.x;
}
int Particle::getY() const {
    return pos_p.y;
}

Particle::Particle() {

}

bool Particle::collide(const Jugador& j, double shrink) const {
    return (pos_p.x > j.getMinX()+shrink && pos_p.x < j.getMaxX()-shrink &&
            pos_p.y > j.getMinY()+shrink && pos_p.y < j.getMaxY()-shrink);
}

void Particle::update() {
    if (alive) {
        time = ofGetFrameNum() / 60;
        lifetime = time % 10;
    }
}

void Particle::draw() {
    ofSetLineWidth(3);
    ofDrawCircle(pos_p, 2);
    double delta = (M_PI * 2) / 3;
    double offset = 0.5 * M_PI;
    glm::vec2 vertices[3];
    
    for (int i = 0; i < 3; i++) {
        double angle = delta * i + offset;
        vertices[i] = {
            r * std::cos(angle) + pos_p.x,
            r * std::sin(angle) + pos_p.y
        };
    }
    
    ofDrawTriangle(vertices[0], vertices[1], vertices[2]);
}

//--------------------------------------------------------------
void ofApp::setup(){
    fuente.load("arial.ttf", 32);
	video.load("imagenes/agua.mp4");
    imagen_jugador.load("imagenes/player1.png");
    
    inicio_jugador.set(0.5 * ofGetWidth() - 0.5 * imagen_jugador.getWidth(),
                       0.5 * ofGetHeight() - 0.5 * imagen_jugador.getHeight());
    jugador_1.setup(imagen_jugador, inicio_jugador);
    
    for (int i = 0; i < numParticles; i++) {
        myParticle[i].pos_p = {
            ofRandom(50, ofGetWidth() - 50),
            ofRandom(100, ofGetHeight() - 100)
        };
        myParticle[i].alive = true;
    }
    
}

//--------------------------------------------------------------
void ofApp::update() {
    seg = ofGetFrameNum() / 60;
    video.update();
    
    jugador_1.update();
    
    for (int i = 0; i < numParticles; i++) {
        myParticle[i].alive = myParticle[i].collide(jugador_1, 5.0) ? false : true;
        if (!myParticle[i].alive) {
            myParticle[i].pos_p = ofPoint(ofRandom(50, ofGetWidth() - 50),
                                          ofRandom(100, ofGetHeight() - 100));
            myParticle[i].alive = true;
        }
        myParticle[i].update();
    }
}

//--------------------------------------------------------------
void ofApp::draw(){
    string tiempo = ofToString(0 + seg);
	video.draw(0, 0, 800, 600);
	video.play();
	
    fuente.drawString("tiempo:" + tiempo, 10, 50);

    for (int i = 0; i < numParticles; i++) {
        myParticle[i].draw();
    }
    
    jugador_1.draw();
}


//--------------------------------------------------------------
void ofApp::keyPressed(int key){
    if (key == OF_KEY_LEFT)
        jugador_1.izquierda = true;
    if (key == OF_KEY_RIGHT)
        jugador_1.derecha = true;
    if (key == OF_KEY_UP)
        jugador_1.arriba = true;
    if (key == OF_KEY_DOWN)
        jugador_1.abajo = true;
    if (key == ' ') {
    }
}

//--------------------------------------------------------------
void ofApp::keyReleased(int key){
    if (key == OF_KEY_LEFT)
        jugador_1.izquierda = false;
    if (key == OF_KEY_RIGHT)
        jugador_1.derecha = false;
    if (key == OF_KEY_UP)
        jugador_1.arriba = false;
    if (key == OF_KEY_DOWN)
        jugador_1.abajo = false;
}

//--------------------------------------------------------------
void ofApp::mouseMoved(int x, int y ){

}

//--------------------------------------------------------------
void ofApp::mouseDragged(int x, int y, int button){

}

//--------------------------------------------------------------
void ofApp::mousePressed(int x, int y, int button){

}

//--------------------------------------------------------------
void ofApp::mouseReleased(int x, int y, int button){

}

//--------------------------------------------------------------
void ofApp::mouseEntered(int x, int y){

}

//--------------------------------------------------------------
void ofApp::mouseExited(int x, int y){

}

//--------------------------------------------------------------
void ofApp::windowResized(int w, int h){

}

//--------------------------------------------------------------
void ofApp::gotMessage(ofMessage msg){

}

//--------------------------------------------------------------
void ofApp::dragEvent(ofDragInfo dragInfo){ 

}

Judador.h

#pragma once
#include "ofMain.h"

class Jugador {
    ofPoint pos;

public:
	void setup(const ofImage& _img, const ofPoint& _pos);
    int getMinX() const;
    int getMinY() const;
    int getMaxX() const;
    int getMaxY() const;
	void draw();
	void update();
	
    ofImage img;
	bool izquierda, derecha, abajo, arriba;
	float width, height, vel;

};

Jugador.cpp

#include "Jugador.h"

void Jugador::setup(const ofImage& _img, const ofPoint& _pos) {
    img = _img;
    pos.set(_pos);
    height = img.getHeight();
    width = img.getWidth();
    vel = 5;
}

int Jugador::getMinX() const {
    return pos.x;
}

int Jugador::getMinY() const {
    return pos.y;
}

int Jugador::getMaxX() const {
    return pos.x + width;
}

int Jugador::getMaxY() const {
    return pos.y + height;
}

void Jugador::draw() {
    img.draw(pos);
}

void Jugador::update() {
    if (arriba)
        pos.y -= vel;
    if (abajo)
        pos.y += vel;
    if (izquierda)
        pos.x -= vel;
    if (derecha)
        pos.x += vel;
}
2 Likes

Thank you @diff-arch , @mr.Bitmap !!
Your replies saved me, I was struggling so much and now I understand the reason why the code was not working properly. I’m so happy that it finally works and I really can’t thank you enough!! Also, @mr.Bitmap the picture you sent is really funny, my jugador (player in Spanish because I’m from Argentina) was in fact, in another castle :))
Thanks again for taking the time to revise the code and for helping me!!

1 Like

You’re welcome. Don’t hesitate to ask if you have questions about the changes I made.