0
\$\begingroup\$

i have Incorporated gravity mechanics and collision mechanics to my game. the game works fine for some period of time after that i am getting significant amount of frame rate drop.i am doing some heavy computation but i don't see any other approach to it.can anyone suggest improvement for frame rate drop. i have added code and output as snippet

<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<style>
canvas {
    border:1px solid #d3d3d3;
    background-color: #f1f1f1;
    width: 100%;
}
body
{
    overflow-x: hidden;
    overflow-y: hidden;
    margin:0px 0px 0px 0px;
}
</style>
</head>
<body onload="startGame()">
</body>
<script>
var blackhole;
var planet=[];
var flag=[];
var canvasWidth=window.innerWidth;
var canvasHieght=window.innerHeight;
var mousex=0;
var mousey=0;
var numofplanets=15;
function startGame() {
    myGameArea.start(); 
    blackhole = new BlackHole(canvasWidth/2,canvasHieght/2, 30,"black",70);
    for(let i=0;i<numofplanets;i++)
    {
      var plnt=new Planet(Math.floor((Math.random() *300) + 1),Math.floor((Math.random() * 300) + 1),10,"blue",20);
      planet.push(plnt);
    }
}
var myGameArea = {
    canvas : document.createElement("canvas"),
    start : function() {
        this.canvas.width = canvasWidth;
        this.canvas.height = canvasHieght;
        this.context = this.canvas.getContext("2d");
        document.body.insertBefore(this.canvas, document.body.childNodes[0]);
        this.frameNo = 0;
        this.interval = setInterval(updateCanvas,1);
    },
clear : function() {
        this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
        }
}
function BlackHole(x,y,radius,color,mass) {
    this.x = x;
    this.y = y; 
    this.color=color;
    this.mass=mass;
    this.radius=radius;
    this.outerradius=radius*6;
    ctx = myGameArea.context; 

    this.stay=function(){
        ctx = myGameArea.context;
        ctx.save();
        ctx.fillStyle=color;
        ctx.beginPath();
        ctx.arc(this.x, this.y,this.radius, 0, 2 * Math.PI);
        ctx.stroke();
        ctx.beginPath();
        ctx.arc(this.x,this.y,this.outerradius,0,2*Math.PI);
        ctx.stroke();
    }
    this.attract=function(i){
            if(planet[i].r>this.radius)
            {
             planet[i].acclx=(blackhole.mass*(blackhole.x-planet[i].x))/Math.pow(planet[i].r,3);
             planet[i].accly=(blackhole.mass*(blackhole.y-planet[i].y))/Math.pow(planet[i].r,3);
             planet[i].speedx+=planet[i].acclx; 
             planet[i].speedy+=planet[i].accly;
             planet[i].x+=planet[i].speedx;
             planet[i].y+=planet[i].speedy;
             ctx.save();
             ctx.fillStyle = color;
             ctx.beginPath();
             ctx.arc(planet[i].x, planet[i].y, planet[i].radius, 0, 2 * Math.PI);
             //ctx.stroke();
            }
            else
            {
              max=1;
              min=-1;
              planet[i].x=Math.floor((Math.random() *1000) + 1);
              planet[i].y=Math.floor((Math.random() *900) + 1);
              planet[i].acclx=0.001;
              planet[i].accly=0.0001;
              planet[i].dirx=Math.floor(Math.random() * (+max - +min)) + +min; 
              planet[i].diry=Math.floor(Math.random() * (+max - +min)) + +min;
              if(planet[i].dirx==0 && planet[i].diry==0)
              {
                  planet[i].dirx=-1;
                  planet[i].diry=1;
              }
              planet[i].speedx=Math.sqrt(Math.sqrt(Math.pow(this.x-planet[i].x,2)+Math.pow(this.y-planet[i].y,2))*planet[i].acclx)*planet[i].dirx;
              planet[i].speedy=Math.sqrt(Math.sqrt(Math.pow(this.x-planet[i].x,2)+Math.pow(this.y-planet[i].y,2))*planet[i].accly)*planet[i].diry;
            }
        }
    }
function Planet(x,y,radius,color,mass){
    this.x = x; 
    this.y = y;   
    this.acclx=0.001;
    this.accly=0.0001;
    this.mass=mass;
    this.r=0.0;
    max=1;
    min=-1;
    this.radius=radius;
    this.dirx=Math.floor(Math.random() * (+max - +min)) + +min; 
    this.diry=Math.floor(Math.random() * (+max - +min)) + +min;

    if(this.dirx==0 && this.diry==0)
    {
        this.dirx=-1;
        this.diry=1
    }
    this.speedx=Math.sqrt(Math.sqrt(Math.pow(blackhole.x-this.x,2)+Math.pow(blackhole.y-this.y,2))*this.acclx)*this.dirx;
    this.speedy=Math.sqrt(Math.sqrt(Math.pow(blackhole.x-this.x,2)+Math.pow(blackhole.y-this.y,2))*this.accly)*this.diry; 
     
    this.move=function(){
            
            if(this.x>canvasWidth)
               this.x=0;
            else if(this.x<0)
                this.x=canvasWidth;
            else if(this.y<0)
                this.y=canvasHieght;
            else if(this.y>canvasWidth)
                this.y=0;     
            this.x+=this.speedx;
            this.y+=this.speedy;
            ctx.save();
            ctx.fillStyle = color;
            ctx.beginPath();
            ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI);
           // ctx.stroke();
    }
    this.checkcollision=function(i)
    {
      for(let k=0;k<numofplanets;k++)
      {
        if(k==i)
          continue;
        if(this.x+this.radius+planet[k].radius>planet[k].x && this.x<planet[k].x+this.radius+planet[k].radius && this.y+this.radius+planet[k].radius>planet[k].y && this.y<planet[k].y+planet[k].radius+this.radius)
        {
         /* this.speedx=this.speedx-((2*planet[k].mass/this.mass+planet[k].mass)*(Math.sqrt(Math.pow(this.speedx-planet[k].speedx,2)+Math.pow(this.x-planet[k].x,2))))*(this.x-planet[k].x);
          this.speedy=this.speedy-((2*planet[k].mass/this.mass+planet[k].mass)*(Math.sqrt(Math.pow(this.speedy-planet[k].speedy,2)+Math.pow(this.y-planet[k].y,2))))*(this.y-planet[k].y);
          planet[k].speedx=planet[k].speedx-((2*this.mass/this.mass+planet[k].mass)*(Math.sqrt(Math.pow(planet[k].speedx-this.speedx,2)+Math.pow(planet[k].x-this.x,2))))*(planet[k].x-this.x);
          planet[k].speedy=planet[k].speedy-((2*this.mass/this.mass+planet[k].mass)*(Math.sqrt(Math.pow(planet[k].speedy-this.speedy,2)+Math.pow(planet[k].y-this.y,2))))*(planet[k].y-this.y);*/
          var tempx=this.speedx;
          var tempy=this.speedy;
          this.speedx=planet[k].speedx;
          this.speedy=planet[k].speedy;
          planet[k].speedx=tempx;
          planet[k].speedy=tempy;
        }  
      }
    }
}
function getcordinates(event)
{
  mousex=event.clientX;
  mousey=event.clientY;
}
function updateCanvas()
{
  myGameArea.clear();
  blackhole.stay();
  for(let i=0;i<numofplanets;i++)
  {
    planet[i].r=Math.sqrt(Math.pow(blackhole.x-planet[i].x,2)+Math.pow(blackhole.y-planet[i].y,2));
    planet[i].move();
    planet[i].checkcollision(i);
    
    if(planet[i].r<blackhole.outerradius)
        blackhole.attract(i);  
    ctx.stroke();  
      
  }
}
</script>

\$\endgroup\$
1
\$\begingroup\$

"i am doing some heavy computation"
from what I've seen from your code, 15 planets is not heavy (even tho there are some nested for's to check collisions). The FPS drop must be something related to Javascript memory leak or something.

I'm not quite sure what may be causing it, but, after reading the code and inspecting the behaviour on Dev Tools, it might be the way you wrote your code.

You are using a deprecated syntax that, if not wrote properly, might cause Javascript to be very unoptimized.
The way you should do it is using ES6 syntax. I rewrote your code using ES6 syntax and now it runs smoother. I tried not to change it too much from what you did, there are somethings that can still be optimized, but it will help you for now, I guess.

"Why does it runs smoother just by changing the syntax" you might ask. The answer is: ES6's Sugar Syntax tells Javascript to work differently with objects, in a more optimized way, implementing prototypes for methods and optimizing memory usage in general.

Pro tip: Do some research on ES6 and also on Chrome Dev Tools.

<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<style>
canvas {
border:1px solid #d3d3d3;
background-color: #f1f1f1;
width: 100%;
}
body
{
overflow-x: hidden;
overflow-y: hidden;
margin:0px 0px 0px 0px;
}
</style>
</head>
<body onload="startGame()">
</body>
<script>
  class Game {
constructor() {
  // game area
  this.canvas = document.createElement("canvas");
  this.context = this.canvas.getContext("2d");
  this.frameNo = 0;
  this.canvas.width = window.innerWidth;
  this.canvas.height = window.innerHeight;
  document.body.insertBefore(this.canvas, document.body.childNodes[0]);

  // general game props
  this.canvasWidth = window.innerWidth;
  this.canvasHieght = window.innerHeight;
  this.blackhole = new BlackHole(this.canvasWidth / 2, this.canvasHieght / 2, 30, "black", 70, this.context);
  this.planets = [];
  this.mousex = 0;
  this.mousey = 0;
  this.numberOfPlanets = 15;

  this.startGame();

  setInterval(this.update.bind(this), 1);
  // if you plan player interaction on the future, use this
  // window.requestAnimationFrame(this.update.bind(this));
}

startGame() {
  for (let i = 0; i < this.numberOfPlanets; i++) {
    const planet = new Planet(Math.floor((Math.random() * 300) + 1), Math.floor((Math.random() * 300) + 1), 10, "blue", 20, this.context, this.blackhole, this.canvas.width, this.canvas.height);
    this.planets.push(planet);
  }
}

clearCanvas() {
  this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
}

update() {
  this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
  this.blackhole.stay();
  for (let i = 0; i < this.numberOfPlanets; i++) {
    this.planets[i].r = Math.sqrt(Math.pow(this.blackhole.x - this.planets[i].x, 2) + Math.pow(this.blackhole.y - this.planets[i].y, 2));
    this.planets[i].move();
    this.checkPlanetsCollision(this.planets[i]);

    if (this.planets[i].r < this.blackhole.outerradius)
      this.blackhole.attract(this.planets[i]);
    this.context.stroke();
  }
  // window.requestAnimationFrame(this.update.bind(this));
}

checkPlanetsCollision(planet) {
  for (let k = 0; k < this.numberOfPlanets; k++) {
    if (k == planet.id)
      continue;
    if (planet.x + planet.radius + this.planets[k].radius > this.planets[k].x && planet.x < this.planets[k].x + planet.radius + this.planets[k].radius && planet.y + planet.radius + this.planets[k].radius > this.planets[k].y && planet.y < this.planets[k].y + this.planets[k].radius + planet.radius) {
      var tempx = planet.speedx;
      var tempy = planet.speedy;
      planet.speedx = this.planets[k].speedx;
      planet.speedy = this.planets[k].speedy;
      this.planets[k].speedx = tempx;
      this.planets[k].speedy = tempy;
    }
  }
}
  }

  class BlackHole {
  constructor(x, y, radius, color, mass, context) {
    this.x = x;
    this.y = y;
    this.color = color;
    this.mass = mass;
    this.radius = radius;
    this.outerradius = radius * 6;
    this.ctx = context;
  }

  stay() {
      this.ctx.save();
      this.ctx.fillStyle = this.color;
      this.ctx.beginPath();
      this.ctx.arc(this.x, this.y,this.radius, 0, 2 * Math.PI);
      this.ctx.stroke();
      this.ctx.beginPath();
      this.ctx.arc(this.x,this.y,this.outerradius,0,2*Math.PI);
      this.ctx.stroke();
  }

  attract(planet){
    if(planet.r>this.radius)
    {
      planet.acclx=(this.mass*(this.x-planet.x))/Math.pow(planet.r,3);
      planet.accly=(this.mass*(this.y-planet.y))/Math.pow(planet.r,3);
      planet.speedx+=planet.acclx; 
      planet.speedy+=planet.accly;
      planet.x+=planet.speedx;
      planet.y+=planet.speedy;
      this.ctx.save();
      this.ctx.fillStyle = this.color;
      this.ctx.beginPath();
      this.ctx.arc(planet.x, planet.y, planet.radius, 0, 2 * Math.PI);
      //ctx.stroke();
    }
    else
    {
      planet.x=Math.floor((Math.random() *1000) + 1);
      planet.y=Math.floor((Math.random() *900) + 1);
      planet.acclx=0.001;
      planet.accly=0.0001;
      planet.dirx=Math.floor(Math.random() * (+planet.max - +planet.min)) + +planet.min; 
      planet.diry=Math.floor(Math.random() * (+planet.max - +planet.min)) + +planet.min;
      if(planet.dirx==0 && planet.diry==0)
      {
          planet.dirx=-1;
          planet.diry=1;
      }
      planet.speedx=Math.sqrt(Math.sqrt(Math.pow(this.x-planet.x,2)+Math.pow(this.y-planet.y,2))*planet.acclx)*planet.dirx;
      planet.speedy=Math.sqrt(Math.sqrt(Math.pow(this.x-planet.x,2)+Math.pow(this.y-planet.y,2))*planet.accly)*planet.diry;
    }
  }
}

  class Planet{
constructor(x, y, radius, color, mass, context, blackhole, canvasWidth, canvasHieght) {
  this.canvasWidth = canvasWidth;
  this.canvasHieght = canvasHieght;
  this.x = x;
  this.y = y;
  this.acclx = 0.001;
  this.accly = 0.0001;
  this.mass = mass;
  this.r = 0.0;
  this.ctx = context;
  this.max = 1;
  this.min = -1;
  this.radius = radius;
  this.dirx = Math.floor(Math.random() * (+this.max - +this.min)) + +this.min;
  this.diry = Math.floor(Math.random() * (+this.max - +this.min)) + +this.min;
  if (this.dirx == 0 && this.diry == 0) {
    this.dirx = -1;
    this.diry = 1
  }
  this.speedx = Math.sqrt(Math.sqrt(Math.pow(blackhole.x - this.x, 2) + Math.pow(blackhole.y - this.y, 2)) * this.acclx) * this.dirx;
  this.speedy = Math.sqrt(Math.sqrt(Math.pow(blackhole.x - this.x, 2) + Math.pow(blackhole.y - this.y, 2)) * this.accly) * this.diry; 
}

move() {
  if(this.x> this.canvasWidth)
    this.x=0;
  else if(this.x<0)
      this.x= this.canvasWidth;
  else if(this.y<0)
      this.y= this.canvasHieght;
  else if(this.y> this.canvasWidth)
      this.y=0;     
  this.x+=this.speedx;
  this.y+=this.speedy;
  this.ctx.save();
  this.ctx.fillStyle = this.color;
  this.ctx.beginPath();
  this.ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI);
  // ctx.stroke();
}
  }

  function startGame() {
const game = new Game();
  }
</script>

\$\endgroup\$
  • 1
    \$\begingroup\$ I'll add that the reason this happens is because every time OP creates a new object with the current syntax, a new function is created for every method (e.g. this.stay for BlackHole). This leads to unnecessary memory usage. This can be avoided without using the ES6 classes (which are really just syntactic sugar for prototype, nothing more). You need to learn how prototypes work. A great place to start is here \$\endgroup\$ – Bálint Jul 17 at 15:59
  • \$\begingroup\$ Great addition, thanks! All I know is that ES6 has sugar syntax for prototype implementation, but I know very little about prototyping itself. \$\endgroup\$ – Diego Penha Jul 17 at 16:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.