Search code examples
javascriptconstructorhtml5-canvas

My JavaScript Particle Collision Engine Failing to render, "Particle is not a constructor" Error


I have the following code in which I attempt to create an engine for basic particle collisions. However, it is failing to render and leaves an empty canvas with the error: "Particle is not a constructor". Please aid in solving this problem, as well as suggestions for the engine.

I have tried changing the temporary 'Particle' variable to 'p'. My code is as follows:

var Canvas = document.createElement('canvas');

    Canvas.width = 300;
    Canvas.height = 300;
    Canvas.style = 'border: 1px solid black';
    document.body.appendChild(Canvas);
    
var CTX = Canvas.getContext('2d');
    
var ParticleIndex = [];

function Particle(X, Y, Xvel, Yvel, Size, Xfric, Yfric, Xacc, Yacc, Bounce){
    
    this.X = X;
    this.Y = Y;
    this.XVel = Xvel;
    this.YVel = Yvel;
    this.Size = Size;
    this.Xfric = Xfric;
    this.Yfric = Yfric;
    this.Xacc = Xacc;
    this.Yacc = Yacc;
    this.Bounce = Bounce
    
    this.appendToIndex = function(){
        
        ParticleIndex.push(this);
        
    }
    
    this.Render = function(){
        
        CTX.fillRect(this.X, this.Y, this.Size, this.Size);
        
    }; 
    
    this.Physics = function(){
        
        this.X += this.Xvel;
        
        for(var i = 0; i < ParticleIndex.length; i++){
            
            if(isCollide(this, ParticleIndex[i])){
                
                this.X -= this.Xvel
                this.Xvel *= this.Bounce * -1;
                
            }
            
        }
        
        this.Y += this.Yvel;
        
        for(var i = 0; i < ParticleIndex.length; i++){
            
            if(isCollide(this, ParticleIndex[i])){
                
                this.Y -= this.Yvel
                this.Yvel *= this.Bounce * -1;
                
            }
            
        }
        
        this.Xvel *= this.Xfric;
        this.Yvel *= this.Yfric;
        
    }
    
    
}

function isCollide(a, b) {
    
    return !(
        
        ((a.Y + a.Size) < (b.Y)) ||
        (a.Y > (b.Y + b.Size)) ||
        ((a.X + a.Size) < b.X) ||
        (a.X > (b.X + b.Size))
        
    );
    
}

function Render(){
    
    CTX.clearRect(0, 0, Canvas.width, Canvas.height);
    
    for(var k = 0; k < ParticleIndex.length; k++){
        
        ParticleIndex[k].Physics;
        ParticleIndex[k].Render;
        
    }
    
}

for(var j = 0; j < 100; j++){
    
    var p = new Particle(
        
        Math.round(Math.random() * Canvas.width), 
        Math.round(Math.random() * Canvas.height),
        (Math.random * 10) - 5,
        (Math.random * 10) - 5,
        5,
        0.9,
        0.9,
        0,
        0,
        0.8
    
    )
    
    p.appendToIndex();    
    p.Render();
    
}

alert('Rendering...');

Render()

The following part gives the error:

for(var j = 0; j < 100; j++){
    
    var p = new Particle(
        
        Math.round(Math.random() * Canvas.width), 
        Math.round(Math.random() * Canvas.height),
        (Math.random * 10) - 5,
        (Math.random * 10) - 5,
        5,
        0.9,
        0.9,
        0,
        0,
        0.8
    
    )
    
    p.appendToIndex();    
    p.Render();
    
}

Thank you for reading.


Solution

  • You seem to be missing () for the function calls ( a few of them), and the function Physics() doesn't work well, yet – it is because bad variable names (XVel vs Xvel). And the final bonus, you can't collide with yourself therefore added that condition to isCollide.

    Other Issues you might have are the boundaries (update: I did some checking for this case), and time factor, you should have variable t which will increase according to time passed, not according to frequency of timeout.

    Also consider using a library for this, such as matter.js

    var Canvas = document.createElement('canvas');
    
    Canvas.width = 400;
    Canvas.height = 180;
    Canvas.style = 'border: 1px solid black; background: white;';
    document.body.appendChild(Canvas);
    
    var CTX = Canvas.getContext('2d');
    
    var ParticleIndex = [];
    
    function Particle(X, Y, Xvel, Yvel, Size, Xfric, Yfric, Xacc, Yacc, Bounce) {
    
      this.X = X;
      this.Y = Y;
      this.Xvel = Xvel;
      this.Yvel = Yvel;
      this.Size = Size;
      this.Xfric = Xfric;
      this.Yfric = Yfric;
      this.Xacc = Xacc;
      this.Yacc = Yacc;
      this.Bounce = Bounce
    
      this.appendToIndex = function() {
    
        ParticleIndex.push(this);
    
      }
    
      this.Render = function() {
    
        CTX.fillRect(this.X, this.Y, this.Size, this.Size);
    
      };
    
      this.Physics = function() {
    
        this.X += this.Xvel;
    
        for (var i = 0; i < ParticleIndex.length; i++) {
    
          if (isCollide(this, ParticleIndex[i])) {
    
            this.X -= this.Xvel
            this.Xvel *= this.Bounce * -1;
    
          }
    
        }
    
        this.Y += this.Yvel;
    
        for (var i = 0; i < ParticleIndex.length; i++) {
    
          if (isCollide(this, ParticleIndex[i])) {
    
            this.Y -= this.Yvel
            this.Yvel *= this.Bounce * -1;
    
          }
    
        }
    
        this.Xvel *= this.Xfric;
        this.Yvel *= this.Yfric;
    
        // boundaries
        if (this.X < 0) {
          this.X = 0
          this.Xvel *= this.Bounce * -1;
        }
        if (this.Y < 0) {
          this.Y = 0
          this.Yvel *= this.Bounce * -1;
        }
        if (this.X + this.Size > Canvas.width - 1) {
          this.X = Canvas.width - this.Size - 1
          this.Xvel *= this.Bounce * -1;
        }
        if (this.Y + this.Size > Canvas.height - 1) {
          this.Y = Canvas.height - this.Size - 1
          this.Yvel *= this.Bounce * -1;
        }
    
      }
    
    }
    
    function isCollide(a, b) {
      if (a == b) {
        return false
      }
    
      return !(
    
        ((a.Y + a.Size) < (b.Y)) ||
        (a.Y > (b.Y + b.Size)) ||
        ((a.X + a.Size) < b.X) ||
        (a.X > (b.X + b.Size))
      );
    
    }
    
    function Render() {
    
      CTX.clearRect(0, 0, Canvas.width, Canvas.height);
    
      for (var k = 0; k < ParticleIndex.length; k++) {
    
        ParticleIndex[k].Physics();
        ParticleIndex[k].Render();
    
      }
    
      requestAnimationFrame(Render)
    
    }
    
    for (var j = 0; j < 10; j++) {
    
      var Size = 12;
      var p = new Particle(
    
        Math.round(Math.random() * (Canvas.width - Size)),
        Math.round(Math.random() * (Canvas.height - Size)),
        (Math.random() * 10) - 5,
        (Math.random() * 10) - 5,
        Size,
        0.999,
        0.999,
        0,
        0,
        1
    
      )
    
      p.appendToIndex();
      // p.Render();
    
    }
    
    
    Render()