Rye123
Rye123

Reputation: 28

Emitter follows Particle in canvas

in this HTML5 code, I try to fire particles from the Emitter in objects[0]. This can be run with objects[0].play in the console. However, the Emitter follows the Particle fired.

My question is, why?

HTML Code

<html>
<head>
    <style>
        #canv{
            border: 1px solid black;
        }
    </style>

    <script src = "setup.js"></script>
    <script src = "main.js"></script>
</head>

<body onLoad = "start()">
    <canvas width = "500" height = "500" id = "canv"></canvas>
</body>
</html>

setup.js

var objects = [];
var sprites = [];
var c;
var ctx;

var time;
var timeInterval;

function newSprite(url){
    var a = sprites.length;
    var b = new Image();
    b.src = url;

    b.onload = function(){
        sprites[a] = b;
    };
}

function randomN(min, max){
    return Math.floor((Math.random()*max) - min);
}

function Vector2(x,y){
    this.x = x;
    this.y = y;
}

function Particle(pos, life, vel, accel){
    this.pos = pos;
    this.life = life || Infinity;
    this.vel = vel || new Vector2(0,0);
    this.accel = accel || new Vector2(0,0);

    this.update = function(){
        this.pos.x += this.vel.x;
        this.pos.y += this.vel.y;
        this.vel.x += this.accel.x;
        this.vel.y += this.accel.y;

        this.life--;

        if(this.life <= 0){
            return false;
        }else{
            return true;
        }
    };
}

function Emitter(pos){
    this.pos = pos;
    this.actualPos = this.pos;

    this.particles = [];
    this.forces = [];
    this.playing = false;

    this.newParticle = function(life, vel, accel){
        var a = this.particles.length;
        this.particles[a] = new Particle(this.pos, life, vel, accel);
    };


    this.update = function(){
        console.log("(" + this.actualPos.x + ", " + this.actualPos.y + ") " + "(" + this.pos.x + ", " + this.pos.y + ")");
        for(var a = 0; a < this.particles.length; a++){
            for(var b = 0; b < this.forces.length; b++){
                this.forces[b](this.particles[a]);
            }
            var that = this.particles[a];
            var particleAlive = that.update();

            if(particleAlive == false){
                this.particles.splice(a, 1);
                a--;
            }
        }
    };


    this.play = function(){
        this.playing = true;
    };
}


function timeStep(){
    for(var a = 0; a < objects.length; a++){
        if(objects[a].__proto__.constructor.name == "Emitter"){
            if(objects[a].playing == true){
                objects[a].update();
            }
        }
    }

    objects[1].newParticle(1000, new Vector2(1, 0), new Vector2(0.1, 0));
    time++;
}

function gravity(p){
    //p.vel.y += 0.1;
}   

main.js

function start(){
    c = document.getElementById("canv");
    ctx = c.getContext('2d');


    newSprite("spark.png");
    objects[0] = new Emitter(new Vector2(50, 60));
    objects[0].forces.push(gravity);
    objects[0].newParticle(1000, new Vector2(1, 0), new Vector2(0.1, 0));

    objects[1] = new Emitter(new Vector2(100, 100));
    objects[1].forces.push(gravity);
    time = 0;
    timeInterval = window.setInterval(timeStep, 10);
    reDraw();
}

function reDraw(){
    ctx.clearRect(0, 0, c.width, c.height);
    for(var a = 0; a < objects.length; a++){
        if(objects[a].__proto__.constructor.name == "Emitter"){
            ctx.beginPath();
            ctx.arc(objects[a].pos.x, objects[a].pos.y, 5, 0, 2*Math.PI);
            ctx.fillStyle = "black";
            ctx.fill();
            ctx.closePath();
            if(objects[a].playing == true){
                for(var b = 0; b < objects[a].particles.length; b++){
                    ctx.beginPath();
                    ctx.drawImage(sprites[0], objects[a].particles[b].pos.x, objects[a].particles[b].pos.y, 8,8);
                    //ctx.arc(objects[a].particles[b].pos.x, objects[a].particles[b].pos.y, 5, 0, 2*Math.PI);
                    ctx.fillStyle = "black";
                    ctx.fill();
                    ctx.closePath();
                }
            }
        }
    }

    requestAnimationFrame(reDraw);
}

Upvotes: 0

Views: 87

Answers (2)

GameAlchemist
GameAlchemist

Reputation: 19294

Your mistake is a quite classical error of using an object reference rather than using the object's values : when you create a new particle, you set its position to be the very position of the emitter.
So any latter change to the particle's position changes the emitter's position : again, the position (pos) Object is the very same object.
What you want is each particle to have its own position, that has the initial value of the emitter's position.
Done, for instance, with :

function Particle(pos, life, vel, accel){
      this.pos = new Vector2(pos.x, pos.y);
      //...

Notice that velocity and acceleration are also references, which here is not an issue since you call the function calling the constructor with a new Vector2.
However you might want to protect your code against future possible errors by performing a copy also for those two properties.

Upvotes: 1

user2808054
user2808054

Reputation: 1386

I havne't followed the full logic so sorry if this isn't the cause, but there's something odd about this :

function newSprite(url){
    var a = sprites.length;
    var b = new Image();
    b.src = url;

    b.onload = function(){
        sprites[a] = b;
    };
}

At the time when onload runs, a and b are lno longer in scope (I believe): The onload event would be fired as a new function call outside of newSprite.

Wouldn't you need to do something like this ...

    b.onload = function(){
        sprites[sprites.length] = this;
    };

so that the function runs correctly when it's called ?

Upvotes: 1

Related Questions