我的游戏出现性能泄漏,我找不到

时间:2018-07-13 14:42:16

标签: javascript

我就此问题发表了一篇文章,但意识到它说得不太好,所以我删除了它,并为这篇文章提供了更多信息。因此,我将这款游戏进行了一段时间,它是一种游戏,其中一种游戏作为一种颜色,并收集其他颜色的球并获得积分。您可以在下面使用我的工作代码片段进行播放。一段时间后(例如30或45秒),游戏落后很多,并且无法很好地进行游戏。我检查了所有地方,但似乎找不到任何地方重复每一帧,这会减慢速度。有什么办法可以找到性能问题所在的地方?

以下是代码段:

let ctx = document.getElementById("can").getContext("2d");
let can = document.getElementById("can");
let width = can.width;
let height = can.height;
let disp = document.getElementById("dis");


ctx.fillStyle = "black";
ctx.rect(0, 0, can.width, can.height);
ctx.fill();
ctx.font = "30px ComicSans";
let colors = ["red", "green", "blue"];



let Player = function(){
  this.x = 250;
  this.y = 400;
  this.colornum = 0;
  this.xspeed = 10;
  this.yspeed = 10;
  this.pressRight = false;
  this.pressLeft = false;
  this.pressUp = false;
  this.pressDown = false;
  this.radius = 30;
  this.num = 0;


  this.draw = function(){

    ctx.save();
    ctx.beginPath();
    ctx.arc(this.x, this.y, this.radius, 0, 2*Math.PI, false);
    ctx.fillStyle = colors[this.colornum];
    ctx.fill();
    ctx.closePath();

    //Number Style
    ctx.beginPath();
    ctx.fillStyle = "Black";
    ctx.fillText(this.num, this.x - 15, this.y + 10);
    ctx.fill();
    ctx.restore();

  }

  this.updatePosition = function(){
    if(this.pressRight){
      if(this.x + this.radius < can.width){
        this.x += this.xspeed;
      }
    }else if(this.pressLeft){
      if(this.x - this.radius > 0){
        this.x -= this.xspeed;
      }
    }else if(this.pressUp){
      if(this.y - this.radius > 0){
        this.y -= this.yspeed;
      }
    }else if(this.pressDown){
      if(this.y + this.radius < can.height){
        this.y += this.yspeed;
      }
    }
  }

  this.update = function(){
    this.updatePosition();
    this.draw();
  }

}

let Entity = function(){
  this.x = Math.floor(Math.random() * 500);
  this.y = Math.floor(Math.random() * 500);
  this.colornum = Math.floor(Math.random() * 3);
  this.xspeed = Math.floor(Math.random() * 7);
  this.yspeed = Math.floor(Math.random() * 7);

  this.collide = function(){
    if(this.x > can.width || this.x < 0){
      this.xspeed = -this.xspeed;
    }
    if(this.y > can.height || this.y < 0){
      this.yspeed = -this.yspeed;
    }
  }

  this.updatePosition = function(){
    this.collide();
    this.x += this.xspeed;
    this.y += this.yspeed;
  }

  this.draw = function(){
    ctx.save();
    ctx.beginPath();
    ctx.arc(this.x, this.y, 20, 0, 2*Math.PI, false);
    ctx.fillStyle = colors[this.colornum];
    ctx.fill();
    ctx.closePath();

  }

  this.update = function(){
    this.updatePosition();
    this.draw();
  }
}

let player1 = new Player();

let enemylist = {};

for(let i = 0; i < 20; i++){
  let id = Math.floor(Math.random() * 100);
  enemylist[id] = new Entity();
}

function CollideDetect(x1, y1, x2, y2){
  if(Math.sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1)) < (player1.radius + 20)){
    return true;
  }else
  return false;
}

function update(){
  ctx.clearRect(0,0,width,height);
  ctx.fillStyle = "White";
  ctx.rect(0,0,800,600);
  ctx.fill();

  for(let key in enemylist){
    let collided = CollideDetect(enemylist[key].x, enemylist[key].y, player1.x, player1.y);
    if(collided && player1.colornum == enemylist[key].colornum){
      player1.radius += 5;
      player1.num += 1;
      delete enemylist[key];
    }else if(collided && player1.colornum != enemylist[key].colornum){
      if(player1.num <= 0 || player1.radius <5){
        delete enemylist[key];
      }else{
        player1.radius -= 5;
        player1.num -= 1;
        delete enemylist[key];
      }
    }
  }

  for(let key in enemylist){
    enemylist[key].update();
  }
  player1.update();

  if(Object.keys(enemylist).length == 0){
    disp.innerHTML = "Game Over";
  }else if(Object.keys(enemylist).length != 0){
    disp.innerHTML = "Start";
  }
}

let Restart = function(){
  if(Object.keys(enemylist).length == 0){
    player1.num = 0;
    player1.radius =30;
    for(let i = 0; i < 20; i++){
      let id = Math.floor(Math.random() * 10);
      enemylist[id] = new Entity();
    }
  }

}



document.onkeydown = function(event){
  if(event.keyCode == 68){
    player1.pressRight = true;
  }else if(event.keyCode == 65){
    player1.pressLeft = true;
  }else if(event.keyCode == 83){
    player1.pressDown = true;
  }else if(event.keyCode == 87){
    player1.pressUp = true;
  }else if(event.keyCode == 71){
    player1.colornum = 0;
  }else if(event.keyCode == 72){
    player1.colornum = 1;
  }else if(event.keyCode == 74){
    player1.colornum = 2;
  }
}

document.onkeyup = function(event){
  if(event.keyCode == 68){
    player1.pressRight = false;
  }else if(event.keyCode == 65){
    player1.pressLeft = false;
  }else if(event.keyCode == 83){
    player1.pressDown = false;
  }else if(event.keyCode == 87){
    player1.pressUp = false;
  }else if(event.keyCode == 87){

  }
}

let gameloop = function(){
  window.requestAnimationFrame(gameloop);
  update();
}

gameloop();
<!DOCTYPE html>
<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8">
    <title>Collision Game</title>
  </head>
  <body>

    <h1>Collision Game</h1>

    <button onclick="Restart()">Restart </button>

    <canvas id="can" height="600" width="800" style="border:1px solid black"></canvas>
    <p id="dis">Start</p>

    <script src="game.js"></script>


  </body>
</html>

2 个答案:

答案 0 :(得分:1)

在处理了一些代码之后,我发现了问题。

问题

您对disp.innerHTML的呼叫方式过多。垃圾收集器无法跟上更新dom的次数。

因此,如果我们采用这段代码并将其移出更新循环并移至其自己的setInterval(..., 500)中,则游戏将继续进行,并且dom可以跟上。

做到这一点:

if (Object.keys(enemylist).length == 0) {
  disp.innerHTML = "Game Over";
} else if (Object.keys(enemylist).length != 0) {
  disp.innerHTML = "Start";
}

成为这个:

setInterval(() = > {
  if (Object.keys(enemylist).length == 0) {
    disp.innerHTML = "Game Over";
  } else if (Object.keys(enemylist).length != 0) {
    disp.innerHTML = "Start";
  }
}, 500)

我是怎么发现的

在chrome开发工具中进行性能测试时,我看到有800多个节点,但是在查看元素时,页面中只有几个元素。但是,p元素进行了很多更新。因此,我们只是放慢了它的更新速度,并解决了这个问题,现在我们将节点保持在100个以下!

答案 1 :(得分:1)

删除ctx.save()ctx.restore()

在游戏中,它们基本上没有用,因为您需要清除画布并在每次更新时从头开始重新绘制所有内容。无论如何,我们不需要在下一帧中保存的任何信息。

保存到堆栈中的drawing state包括:

  • 当前转换矩阵。
  • 当前剪切区域。
  • 当前破折号列表。
  • 以下属性的当前值:
    • strokeStyle
    • fillStyle
    • globalAlpha
    • lineWidth
    • lineCap
    • lineJoin
    • miterLimit
    • lineDashOffset
    • shadowOffsetX
    • shadowOffsetY
    • shadowBlur
    • shadowColor
    • globalCompositeOperation
    • 字体
    • textAlign
    • textBaseline
    • 方向
    • imageSmoothingEnabled

在逐帧重画的游戏中,这些都不是有用的,这使得调用ctx.save()ctx.restore()毫无意义。这并不意味着您不能使用strokeStylefillStyle等。保存这些数据毫无意义。