【问题标题】:JavaScript canvas game is dropping FPS after some timeJavaScript 画布游戏在一段时间后下降 FPS
【发布时间】:2021-05-09 16:00:15
【问题描述】:

最近我一直在尝试制作 JS 画布项目,几天前我开始编写它。我创建了在画布上生成随机点并移动到它的敌人。我通过运行 createEnemy() 函数来创建敌人。它使用敌人的数据创建一个对象并将其存储在“敌人”数组中。一切正常,除了我的 FPS :( 一段时间后,它们真的很难掉下来。发生了什么,为什么?这是我的代码:

var c = document.getElementById("canv");
var context = c.getContext("2d");
var mouseX, mouseY;
const fpsTime = [];
var fps;
var enemies = []
var speed = 2
c.width = window.innerWidth;
c.height = window.innerHeight;
document.addEventListener("mousemove", e => { mouseX = e.pageX; mouseY = e.pageY;});

function getEnemies() {
    return enemies;
}

function drawCharacter(x, y) {
    context.clearRect(0, 0, c.width, c.height);
    context.fillStyle = 'red';
    context.fillRect(x, y,50,60);
    context.save();
    context.font = "30px Arial";
}


function getCurrentMouse() {
    return {"x": mouseX, "y": mouseY}
}

function drawPoint(x, y) {
    context.fillStyle = 'red';
    context.fillRect(x, y,10,10);
    context.save();
}

function createEnemy(name) {
    var enemy = {
        name: name,
        didCompletePoint: true,
        targetX: 0,
        targetY: 0,
        currentX: Math.floor(Math.random() * (+window.innerWidth + 1 - +0)) + +0,
        currentY: Math.floor(Math.random() * (+window.innerHeight + 1 - +0)) + +0,
        generatePoint: function() {
            this.targetX = Math.floor(Math.random() * (+ window.innerWidth + 1 - +0)) + +0
            this.targetY = Math.floor(Math.random() * (+ window.innerHeight + 1 - +0)) + +0
            return [this.targetX, this.targetY];
        },
        draw: function() {
            context.fillStyle = 'black';
            context.fillRect(this.currentX, this.currentY,60,60);
            context.save();
            drawPoint(this.targetX, this.targetY)
            context.font = "30px Arial";
        }
    };
    enemies.push(enemy)
    return enemy
}

var enemy = createEnemy("tak")
var enemy1 = createEnemy("tak")
var enemy2 = createEnemy("tak")
var enemy3 = createEnemy("tak")
var enemy5 = createEnemy("tak")


function drawFrame() {
    document.getElementById("fps").innerHTML = "FPS: " + fps;
    drawCharacter(getCurrentMouse().x, getCurrentMouse().y)
    getEnemies().forEach((en, index) => {
        if(en.didCompletePoint) {
            en.didCompletePoint = false;
            en.generatePoint()
        }else {
            if((en.targetX === en.currentX) && (en.targetY === en.currentY)) {
                en.didCompletePoint = true;
            }
            else {
                //vertical movement
                if (en.targetY > en.currentY){
                    en.currentY++
                }
                else if (en.targetY < en.currentY) {
                    en.currentY--
                }
                //side movement

                // going right
                if (en.targetX > en.currentX) {
                    en.currentX++
                }
                // going left
                else if (en.targetX < en.currentX) {
                    en.currentX--
                }
            }
        }
        en.draw()
    })
}

function startLoop() {
    window.requestAnimationFrame(() => {
        const p = performance.now();
        while (fpsTime.length > 0 && fpsTime[0] <= p - 1000) {
            fpsTime.shift();
        }
        fpsTime.push(p);
        fps = fpsTime.length;
        drawFrame()
        startLoop();
    });
}

startLoop();
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        html, body {
            width: 100%;
            height: 100%;
            margin: 0 !important;
            padding: 0 !important;
        }
    </style>
</head>
<body>
<p id="fps" style="font-size: 30px; font-family: 'Calibri Light', serif; position: absolute; right: 2%; top: 0%;"></p>
<canvas id="canv" style="margin: 0;"></canvas>
</body>
<script src="script.js"></script>
</html>

【问题讨论】:

  • 测试场景到底是什么?
  • 您好,您需要等待 FPS 下降多长时间?我已经在(几年前的)iPad 上运行了大约 5 分钟,它保持在 29 到 31 FPS 之间。你在什么上面运行它?啊,刚刚看到(更强大的)笔记本电脑上的 fps 大幅下降,但 CPU/gpu 或内存使用量并不大。为什么物体移动的速度取决于您可以达到的 fps?

标签: javascript performance canvas frame-rate


【解决方案1】:

首先我得说你走的很好。使用传统的 ecma 很好地填充 在面向画布的脚本中良好/舒适。

你在 context.save() 调用中犯了错误。 不断调用 save() 而不调用 restore() 给程序带来很大的痛苦 并导致内存泄漏。

canvas 2d 上下文必须以智能方式使用。

  • 切勿在没有目的的情况下使用上下文。
  • 有些设置更昂贵,例如 .font 或 shadows。

用于保存恢复

 // we have some complex setup initial already
 // but we need to change something
 ctx.save()
 ctx.fillStyle = "red";
 ctx.fillText ('use it' , 1, 1, 111, 111)
 ctx.restore()
 // Now back me to the old setup

差点忘了一个更大的错误: 我删除了 startLoop() 我把它放在 drawFrame 函数中的最后一次调用。 这样我们就得到了流畅的工作。

说明: 您调用 drawFrame() 并同时调用 startLoop func 如何再次调用 drawFrame....


My english is bad but i am glad if i help...
    drawFrame()
    startLoop();

var c = document.getElementById("canv");
var context = c.getContext("2d");
var mouseX, mouseY;
const fpsTime = [];
var fps;
var enemies = []
var speed = 2
c.width = window.innerWidth;
c.height = window.innerHeight;
document.addEventListener("mousemove", e => { mouseX = e.pageX; mouseY = e.pageY;});

function getEnemies() {
    return enemies;
}

function drawCharacter(x, y) {
    context.clearRect(0, 0, c.width, c.height);
    context.fillStyle = 'red';
    context.fillRect(x, y,50,60);
    // context.font = "30px Arial";
}


function getCurrentMouse() {
    return {"x": mouseX, "y": mouseY}
}

function drawPoint(x, y) {
    context.fillStyle = 'red';
    context.fillRect(x, y,10,10);
}

function createEnemy(name) {
    var enemy = {
        name: name,
        didCompletePoint: true,
        targetX: 0,
        targetY: 0,
        currentX: Math.floor(Math.random() * (+window.innerWidth + 1 - +0)) + +0,
        currentY: Math.floor(Math.random() * (+window.innerHeight + 1 - +0)) + +0,
        generatePoint: function() {
            this.targetX = Math.floor(Math.random() * (+ window.innerWidth + 1 - +0)) + +0
            this.targetY = Math.floor(Math.random() * (+ window.innerHeight + 1 - +0)) + +0
            return [this.targetX, this.targetY];
        },
        draw: function() {
            context.fillStyle = 'black';
            context.fillRect(this.currentX, this.currentY,60,60);
            drawPoint(this.targetX, this.targetY)
            context.font = "30px Arial";
        }
    };
    enemies.push(enemy)
    return enemy
}

var enemy = createEnemy("tak")
var enemy1 = createEnemy("tak")
var enemy2 = createEnemy("tak")
var enemy3 = createEnemy("tak")
var enemy5 = createEnemy("tak")

function drawFrame() {
    document.getElementById("fps").innerHTML = "FPS: " + fps;
    drawCharacter(getCurrentMouse().x, getCurrentMouse().y)
    getEnemies().forEach((en, index) => {
        if(en.didCompletePoint) {
            en.didCompletePoint = false;
            en.generatePoint()
        }else {
            if((en.targetX === en.currentX) && (en.targetY === en.currentY)) {
                en.didCompletePoint = true;
            }
            else {
                //vertical movement
                if (en.targetY > en.currentY){
                    en.currentY++;
                }
                else if (en.targetY < en.currentY) {
                    en.currentY--;
                }
                //side movement

                // going right
                if (en.targetX > en.currentX) {
                    en.currentX++;
                }
                // going left
                else if (en.targetX < en.currentX) {
                    en.currentX--;
                }
            }
        }
        en.draw();
    })
    startLoop();
}

function startLoop() {
    window.requestAnimationFrame(() => {
        const p = performance.now();
        while (fpsTime.length > 0 && fpsTime[0] <= p - 1000) {
            fpsTime.shift();
        }
        fpsTime.push(p);
        fps = fpsTime.length;
        drawFrame();
    });
}

startLoop();
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        html, body {
            width: 100%;
            height: 100%;
            margin: 0 !important;
            padding: 0 !important;
        }
    </style>
</head>
<body>
<p id="fps" style="font-size: 30px; font-family: 'Calibri Light', serif; position: absolute; right: 2%; top: 0%;"></p>
<canvas id="canv" style="margin: 0;"></canvas>
</body>
<script src="script.js"></script>
</html>

【讨论】:

  • 他们的 startLoop / draFrame 没问题,startLoop 只安排一个新的帧。真正的问题是保存恢复。
  • 当然,@Kaiido 先生,但有时我仍然更喜欢 setTimeout 与hackertimer 结合使用,因为我获得了强大的功能(网络服务 - 后台空闲)。
  • 请@Kaiido 我有一个问题:是requestAnimationFrame 强烈推荐吗?
  • 是的,绝对使用requestAnimationFrame 为屏幕上的某些内容设置动画。它仍然是我们必须与监视器显示速率同步的最佳调度程序,因此既要避免丢失帧会导致动画不流畅,又要避免绘制超出显示范围的内容。
猜你喜欢
  • 1970-01-01
  • 2020-02-04
  • 1970-01-01
  • 1970-01-01
  • 2018-11-30
  • 1970-01-01
  • 2023-03-07
  • 1970-01-01
  • 2015-09-30
相关资源
最近更新 更多