当前位置:  开发笔记 > 编程语言 > 正文

画布游戏帧速率下降(30fps),也随机出现卡顿/颠簸

如何解决《画布游戏帧速率下降(30fps),也随机出现卡顿/颠簸》经验,为你挑选了1个好方法。

我编写的游戏的性能一直存在很大的问题。出于某种原因,游戏时口吃,似乎要么绘制同一帧两次,要么跳过该帧的绘制。正如您在图像中看到的那样,由于某种原因,当发生这种“抽动/停顿”时,FPS下降到30,帧时间是原来的两倍。这些口吃非常频繁,每1-5秒发生一次。我已经测试了内存泄漏,没有异常,测试了垃圾回收,没有异常。在update()函数中使用requestAnimationFrame调用它们。这是一个jsfiddle我放在一起。只需右键单击输出,转到时间轴和配置文件20秒钟。您会看到FPS随机下降到30。在小提琴上,大多数代码来自我添加的外部文件,因此对该问题并不重要。我在579行添加了一行,以显示重要代码的起始位置。导致此问题的两个函数是moveAllGameRectangles();。renderTheGameObjects(); 在线上,小提琴上是1180和1098。如果您可以看一下这两个功能,并告诉我是否看到可以改善性能的任何东西,那就太好了。我出于测试目的删除了碰撞检测。我几个月来一直遇到这个问题,所以任何帮助都将非常巨大!

(function() {
var c = document.querySelector("canvas");
var ds = c.getContext("2d");

c.width = window.innerWidth;
c.height = window.innerHeight;

var theMaxWidth = c.width / 4.5;

if (c.height === 1743 || c.height === 1744 || c.height === 1740) {
    var gameVelocity = 60;
} else {
    var gameVelocity = 70;
}

var so = Math.round(c.height / gameVelocity + 65);

var halfVelocity = gameVelocity * 4;
var halfVelocityModulus = gameVelocity * 2;

var modulusNumber = 17;

var OPENING = 0
var LOADING = 1
var BUILD_MENU = 2
var BUILD_MAP = 3
var PLAYING = 4
var LOST = 5
var SETTINGS = 6

assetsLoaded = [];
cargoAnimation = [];
gameObjectsArray = [];
menuObjectsArray = [];
movingMenuObjectsArray = [];
assetsToLoad = [];
whatToMove = [];
gameObjectsPlayingArray = [];
movingGameObjectsArray = [];
lossObjectsArray = [];
settingsObjectArray = [];
messages = [];
theMaxArray = [];
messagesHighScore = [];
settingsTextArray = [];
loadTheFontBeforeArray = [];

randomNumber = 0;
randomGameNumber = 0;

var touchAllowed = false;
var collisionOrNot = false;
var gameRunning = true;
var loaded = false;
var settingsItem1Checked = false;

var practiceModeOn = false;
var interval = null;
var gameInterval = null;

assetsLoaded = 0;
doneLoading = false;

var gameState = OPENING;

var cargo = new Image();
cargo.src = "../www/img/cargo.png";
cargoAnimation.push(cargo);

var menuObjects = new Image();
menuObjects.src = "../www/img/loadingScreenSpriteSheet.png";
assetsToLoad.push(menuObjects);

var gameObjects = new Image();
gameObjects.src = "../www/img/gamePlayingSprites.png";
assetsToLoad.push(gameObjects);


function loadHandler() {
    assetsLoaded++;
    if (assetsLoaded === assetsToLoad.length) {
        touchAllowed = true;
        gameState = BUILD_MENU;
    }
}



window.setTimeout(function() {
    window.addEventListener("touchstart", touchUpHandler, false);
});
c.addEventListener("touchstart", touchdownhandler, false);
window.setTimeout(function() {
    gameState = LOADING;
});

// backgroundSquare menu
var bSquare = Object.create(mainObject);
bSquare.sourceX = 0;
bSquare.sourceY = 0;
bSquare.sourceWidth = 256;
bSquare.sourceHeight = 264;
bSquare.width = c.width / 2 + c.width / 10;
bSquare.height = c.height / 3;
bSquare.x = c.width / 5;
bSquare.y = c.height / 2 - c.height / 6;
menuObjectsArray.push(bSquare);
lossObjectsArray.push(bSquare);

// loss menu
var lSquare = Object.create(mainObject);
lSquare.sourceX = 0;
lSquare.sourceY = 360;
lSquare.sourceWidth = 228;
lSquare.sourceHeight = 92;
lSquare.width = c.width / 2 - c.width / 35;
lSquare.height = c.height / 9;
lSquare.x = c.width / 3 - c.width / 14;
lSquare.y = c.height / 3 + c.height / 25;
lossObjectsArray.push(lSquare);

// play button
var play = Object.create(mainObject);
play.sourceX = 256;
play.sourceY = 0;
play.sourceWidth = 66;
play.sourceHeight = 64;
play.width = c.width / 5;
play.height = c.height / 9;
play.x = c.width / 3 - c.width / 14;
play.y = c.height / 2 + c.height / 100;
menuObjectsArray.push(play);
lossObjectsArray.push(play);

// settingsButton
var settings = Object.create(mainObject);
settings.sourceX = 322;
settings.sourceY = 0;
settings.sourceWidth = 66;
settings.sourceHeight = 64;
settings.width = c.width / 5;
settings.height = c.height / 9;
settings.x = c.width / 3 + c.width / 5 + c.width / 150;
settings.y = c.height / 2 + c.height / 100;
menuObjectsArray.push(settings);
lossObjectsArray.push(settings);

// logo
var logo = Object.create(mainObject);
logo.sourceX = 0;
logo.sourceY = 265;
logo.sourceWidth = 228;
logo.sourceHeight = 92;
logo.width = c.width / 2 - c.width / 35;
logo.height = c.height / 9;
logo.x = c.width / 3 - c.width / 14;
logo.y = c.height / 3 + c.height / 25;
menuObjectsArray.push(logo);


// firstButton
var button1 = Object.create(mainObject);
button1.sourceX = 0;
button1.sourceY = 257;
button1.sourceWidth = 103;
button1.sourceHeight = 120;
button1.width = c.width / 4;
button1.height = c.height / 4;
button1.x = 0;
button1.y = c.height - c.height / 6;
gameObjectsPlayingArray.push(button1);

// secondButton
var button2 = Object.create(mainObject);
button2.sourceX = 103;
button2.sourceY = 257;
button2.sourceWidth = 103;
button2.sourceHeight = 120;
button2.width = c.width / 4;
button2.height = c.height / 4;
button2.x = c.width / 4;
button2.y = c.height - c.height / 6;
gameObjectsPlayingArray.push(button2);

// thirdButton
var button3 = Object.create(mainObject);
button3.sourceX = 207;
button3.sourceY = 257;
button3.sourceWidth = 103;
button3.sourceHeight = 120;
button3.width = c.width / 4;
button3.height = c.height / 4;
button3.x = c.width / 4 + c.width / 4;
button3.y = c.height - c.height / 6;
gameObjectsPlayingArray.push(button3);

// fourButton
var button4 = Object.create(mainObject);
button4.sourceX = 309;
button4.sourceY = 257;
button4.sourceWidth = 103;
button4.sourceHeight = 120;
button4.width = c.width / 4;
button4.height = c.height / 4;
button4.x = c.width / 4 + c.width / 4 + c.width / 4;
button4.y = c.height - c.height / 6;
gameObjectsPlayingArray.push(button4);

// theGameCharacter
var theGameCharacter = Object.create(mainObject);
theGameCharacter.sourceX = 0;
theGameCharacter.sourceY = 380;
theGameCharacter.sourceWidth = 60;
theGameCharacter.sourceHeight = 60;
theGameCharacter.width = c.width / 6;
theGameCharacter.height = c.height / 11;
theGameCharacter.x = c.width / 24;
theGameCharacter.y = c.height * .75 - c.height / 15;
gameObjectsPlayingArray.push(theGameCharacter);


var settingsMenu = Object.create(mainObject);
settingsMenu.sourceX = 256;
settingsMenu.sourceY = 64;
settingsMenu.sourceWidth = 66;
settingsMenu.sourceHeight = 64;
settingsMenu.width = c.width / 6;
settingsMenu.height = c.height / 11;
settingsMenu.x = c.width / 20;
settingsMenu.y = c.height / 35;
settingsObjectArray.push(settingsMenu);

var settingsMenuNonChecked = Object.create(mainObject);
settingsMenuNonChecked.sourceX = 256;
settingsMenuNonChecked.sourceY = 180;
settingsMenuNonChecked.sourceWidth = 182;
settingsMenuNonChecked.sourceHeight = 50;
settingsMenuNonChecked.width = c.width / 2;
settingsMenuNonChecked.height = c.height / 13;
settingsMenuNonChecked.x = c.width / 10;
settingsMenuNonChecked.y = c.height / 5;
settingsObjectArray.push(settingsMenuNonChecked);

timerMessage = Object.create(messageObject);
timerMessage.x = c.width / 2;
timerMessage.y = c.height / 10;
timerMessage.font = getFont();
timerMessage.fillStyle = "#3000ff";
timerMessage.visible = true;
messages.push(timerMessage);

timerMessageHighScore = Object.create(messageObject);
timerMessageHighScore.x = c.width / 2;
timerMessageHighScore.y = c.height / 5;
timerMessageHighScore.font = getFont();
timerMessageHighScore.fillStyle = "#3000ff";
timerMessageHighScore.visible = true;
messagesHighScore.push(timerMessageHighScore);

settingsText = Object.create(messageObject);
settingsText.x = c.width / 1.6;
settingsText.y = c.height / 4.7;
settingsText.font = getSmallerFont();
settingsText.fillStyle = "#3000ff";
settingsText.visible = true;
settingsText.text = "Practice Mode"
settingsTextArray.push(settingsText);

loadTheFontBefore = Object.create(messageObject);
loadTheFontBefore.font = getSmallerFont();
loadTheFontBefore.fillStyle = "#3000ff";
loadTheFontBefore.x = -c.width;
loadTheFontBefore.y = -c.height;
loadTheFontBefore.visible = false;
loadTheFontBeforeArray.push(loadTheFontBefore);


function getFont() {
    var size = c.width / 20 * 2
    return (size | 0) + 'px neuropolitical rg';
}

function getSmallerFont() {
    var size = c.width / 30 * 2
    return (size | 0) + 'px neuropolitical rg';
}


update();

function update() {

    ds.clearRect(0, 0, c.width, c.height);


    //console.log(cargoAnimation.length, gameObjectsArray.length, menuObjectsArray.length, movingMenuObjectsArray.length, assetsToLoad.length, whatToMove.length, gameObjectsPlayingArray.length, movingGameObjectsArray.length, lossObjectsArray.length, settingsObjectArray.length, messages.length)

    req = requestAnimationFrame(update, c);

    switch (gameState) {

        case LOADING:
            loadHandler();
            break;

        case BUILD_MENU:
            moveAllRectangles();
            renderMenuObjects();
            beforeLoadTheFont();
            break;

        case BUILD_MAP:
            moveAllGameRectangles();
            renderTheGameObjects();
            checkForCollisonsRectangles();

            break;

        case PLAYING:
            break;

        case SETTINGS:
            renderSettingsObjects();
            renderSettingText();
            break;
    }
}


function checkForCollisonsRectangles() {
    for (var i = 0; i < movingGameObjectsArray.length; i++) {

        var collisionOrNot = hitTestRectangle(theGameCharacter, movingGameObjectsArray[i]);

        if (collisionOrNot && movingGameObjectsArray[0].y > theGameCharacter.y + theGameCharacter.height - c.height / 80) {
            collisionOrNot === false;
            return;
        }

        if (collisionOrNot) {
            stoptimer();
            resettimer();
            touchAllowed = true;
            window.cancelAnimationFrame(req);
            displayRestartMenu();
            gameRunning = false;
            logHighScore();
            showHighScore();
            return;
        }
    }
}

function logHighScore() {
    console.log(practiceModeOn)
    if (practiceModeOn === false) {
        theMaxArray.push(timerMessage.text);
        Array.max = function(theMaxArray) {
            return Math.max.apply(Math, theMaxArray);
        };
        var maximum = Array.max(theMaxArray);
        timerMessageHighScore.text = maximum
    }
}

function showHighScore() {
    console.log(practiceModeOn)
    if (practiceModeOn === false) {
        if (messagesHighScore.length !== 0) {
            for (var i = 0; i < messagesHighScore.length; i++) {
                var message = messagesHighScore[i];
                if (message.visible) {
                    ds.font = message.font;
                    ds.fillStyle = message.fillStyle;
                    ds.textBaseline = message.textBaseline;
                    ds.textAlign = 'center';
                    ds.fillText(message.text, message.x, message.y);
                }
            }
        }
    }
}

function renderSettingText() {
    if (settingsTextArray.length !== 0) {
        for (var i = 0; i < settingsTextArray.length; i++) {
            var message = settingsTextArray[i];
            if (message.visible) {
                ds.font = message.font;
                ds.fillStyle = message.fillStyle;
                ds.textBaseline = message.textBaseline;
                ds.textAlign = 'center';
                ds.fillText(message.text, message.x, message.y);
            }
        }
    }
}

function beforeLoadTheFont() {
    if (loaded === true) {
        return;
    }
    if (loadTheFontBeforeArray.length !== 0) {
        for (var i = 0; i < loadTheFontBeforeArray.length; i++) {
            var message = loadTheFontBeforeArray[i];
            ds.font = message.font;
            ds.fillStyle = message.fillStyle;
            ds.textBaseline = message.textBaseline;
            ds.textAlign = 'center';
            ds.fillText(message.text, message.x, message.y);
        }
    }
    loaded = true;
    console.log("runn")
}

function displayRestartMenu() {
    if (lossObjectsArray.length !== 0) {
        for (var i = 0; i < lossObjectsArray.length; i++) {
            var sprite = lossObjectsArray[i];
            ds.drawImage(
                menuObjects,
                sprite.sourceX, sprite.sourceY,
                sprite.sourceWidth, sprite.sourceHeight,
                sprite.x, sprite.y,
                sprite.width, sprite.height
            );
        }
    }
}

function touchUpHandler() {

    touchX = event.targetTouches[0].pageX - c.offsetLeft;
    touchY = event.targetTouches[0].pageY - c.offsetTop;

    if (hitTestPoint(touchX, touchY, play)) {


        if (gameState === BUILD_MENU) {
            movingMenuObjectsArray = [];
            startstoptimer();
            gameState = BUILD_MAP;
            gameRunning = true;
            touchAllowed = false;
        } else {
            if (touchAllowed === true) {
                movingMenuObjectsArray = [];
                startstoptimer();
                movingGameObjectsArray = [];
                update();
                gameState = BUILD_MAP;
                gameRunning = true;
                touchAllowed = false;
            }


        }
    }

    if (hitTestPoint(touchX, touchY, settings)) {
        if (touchAllowed === true) {
            gameState = SETTINGS;
            if (!gameRunning) {
                update();
                movingGameObjectsArray = [];
            }
            gameRunning = true;
        }
    }


    if (hitTestPoint(touchX, touchY, settings)) {
        if (touchAllowed === true) {
            gameState = SETTINGS;
            if (!gameRunning) {
                update();
                movingGameObjectsArray = [];
            }
            gameRunning = true;
        }
    }

    if (hitTestPoint(touchX, touchY, settingsMenu)) {
        if (gameState === SETTINGS) {
            gameState = BUILD_MENU;
        }
    }

    if (hitTestPoint(touchX, touchY, settingsMenuNonChecked)) {
        console.log(settingsItem1Checked)
        if (settingsItem1Checked === true) {
            console.log("doing")
            if (c.height === 1743 || c.height === 1744 || c.height === 1740) {
                gameVelocity = 60;
            } else {
                gameVelocity = 70;
            }
            modulusNumber = 17;
            settingsMenuNonChecked.sourceY = 180;
            settingsItem1Checked = false;
            practiceModeOn = false;
            return;
        }
        if (settingsItem1Checked === false) {
            console.log("doing")
            if (c.height === 1743 || c.height === 1744 || c.height === 1740) {
                gameVelocity = 120;
            } else {
                gameVelocity = 140;
            }
            modulusNumber = 30;
            settingsMenuNonChecked.sourceY = 128;
            settingsItem1Checked = true;
            practiceModeOn = true;
            return;
        }

    }



}



function touchdownhandler() {

    touchX = event.targetTouches[0].pageX - c.offsetLeft;
    touchY = event.targetTouches[0].pageY - c.offsetTop;

    if (hitTestPoint(touchX, touchY, button1)) {

        theGameCharacter.x = c.width / 23;

    }

    if (hitTestPoint(touchX, touchY, button2)) {

        theGameCharacter.x = c.width * .25 + c.width / 23;

    }

    if (hitTestPoint(touchX, touchY, button3)) {

        theGameCharacter.x = c.width * .5 + c.width / 23;

    }

    if (hitTestPoint(touchX, touchY, button4)) {

        theGameCharacter.x = c.width * .75 + c.width / 23;

    }

}

function renderTheGameObjects() {

    if (movingGameObjectsArray.length !== 0) {
        for (var i = 0; i < movingGameObjectsArray.length; i++) {
            var sprite = movingGameObjectsArray[i];
            sprite.y += sprite.vy;
            ds.drawImage(
                gameObjects,
                sprite.sourceX, sprite.sourceY,
                sprite.sourceWidth, sprite.sourceHeight,
                sprite.x, sprite.y,
                sprite.width, sprite.height
            );
            if (sprite.y > c.height - c.height / 6) {
                var removeThis = sprite;
            }
        }
    }

    removeTheMenuObject(removeThis, movingGameObjectsArray);

    if (messages.length !== 0) {
        for (var i = 0; i < messages.length; i++) {
            var message = messages[i];
            if (message.visible) {
                ds.font = message.font;
                ds.fillStyle = message.fillStyle;
                ds.textBaseline = message.textBaseline;
                ds.textAlign = 'center';
                ds.fillText(message.text, message.x, message.y);
            }
        }
    }
    if (gameObjectsPlayingArray.length !== 0) {
        for (var i = 0; i < gameObjectsPlayingArray.length; i++) {
            var sprite = gameObjectsPlayingArray[i];
            ds.drawImage(
                gameObjects,
                sprite.sourceX, sprite.sourceY,
                sprite.sourceWidth, sprite.sourceHeight,
                sprite.x, sprite.y,
                sprite.width, sprite.height
            );
        }
    }
}


function hitTestPoint(pointX, pointY, sprite) {
    return pointX > sprite.left() && pointX < sprite.right() && pointY > sprite.top() && pointY < sprite.bottom();
}

function renderMenuObjects() {
    if (menuObjectsArray.length !== 0) {
        for (var i = 0; i < menuObjectsArray.length; i++) {
            var sprite = menuObjectsArray[i];
            ds.drawImage(
                menuObjects,
                sprite.sourceX, sprite.sourceY,
                sprite.sourceWidth, sprite.sourceHeight,
                sprite.x, sprite.y,
                sprite.width, sprite.height
            );
        }
    }
}

function renderSettingsObjects() {
    if (settingsObjectArray.length !== 0) {
        for (var i = 0; i < settingsObjectArray.length; i++) {
            var sprite = settingsObjectArray[i];
            ds.drawImage(
                menuObjects,
                sprite.sourceX, sprite.sourceY,
                sprite.sourceWidth, sprite.sourceHeight,
                sprite.x, sprite.y,
                sprite.width, sprite.height
            );
        }
    }
}

function moveAllGameRectangles() {
    gameInterval = gameInterval + 1;
    if (gameInterval % modulusNumber === 0 || gameInterval === 1) {
        var randomGameNumber = Math.floor((Math.random() * 4) + 1);
        if (randomGameNumber === 1) {

            //the green game object
            var leftSideSpriteGame1 = Object.create(mainObject);
            leftSideSpriteGame1.sourceX = 0;
            leftSideSpriteGame1.sourceY = 0;
            leftSideSpriteGame1.sourceWidth = 310;
            leftSideSpriteGame1.sourceHeight = 64;
            leftSideSpriteGame1.width = c.width * .75;
            leftSideSpriteGame1.height = c.height / 14;
            leftSideSpriteGame1.x = 0;
            leftSideSpriteGame1.y = -65;
            leftSideSpriteGame1.vy = c.height / gameVelocity;
            movingGameObjectsArray.push(leftSideSpriteGame1);

        }

        if (randomGameNumber === 2) {

            //the red game object
            var leftSideSpriteGame2 = Object.create(mainObject);
            leftSideSpriteGame2.sourceX = 0;
            leftSideSpriteGame2.sourceY = 64;
            leftSideSpriteGame2.sourceWidth = 207;
            leftSideSpriteGame2.sourceHeight = 64;
            leftSideSpriteGame2.width = c.width * .5;
            leftSideSpriteGame2.height = c.height / 14;
            leftSideSpriteGame2.x = 0
            leftSideSpriteGame2.y = -65;
            leftSideSpriteGame2.vy = c.height / gameVelocity;
            movingGameObjectsArray.push(leftSideSpriteGame2);

            var rightSideSpriteGame2 = Object.create(mainObject);
            rightSideSpriteGame2.sourceX = 310;
            rightSideSpriteGame2.sourceY = 64;
            rightSideSpriteGame2.sourceWidth = 103;
            rightSideSpriteGame2.sourceHeight = 64;
            rightSideSpriteGame2.width = c.width * .25;
            rightSideSpriteGame2.height = c.

Blindman67.. 5

无需优化

我刚刚看了一下代码,并在小提琴上进行了介绍。好消息是,您无需优化代码,您的运行速度约为最大值的5%。

在您发布的时间轴图像中,您可以看到您的代码大多数时候几乎没有执行任何操作。优化总是好的,但是对于您的应用程序,将中间时间从95%增加到96%并不值得。

最佳负载V过载

我包括一张图像,该图像显示时间轴上每秒60fps的最佳负载应该是什么样子,然后过载的帧率看起来就可以每秒管理30帧,并且由于存在30帧而使帧率从30跳到20太多的事情要做。

与OP的相比

[2

看其他地方

当您比较它们时,您可以看到您的游戏中几乎没有任何东西发生,大多数帧是空闲时间。您的代码可以达到接近1000 Fps的速度,并且仍有空闲时间。帧速率的问题出在其他地方,要么是占用CPU时间,要么是共享GPU占用了带宽和RAM。

如果您始终打开开发工具,那么您所做的很多console.log事情都会产生很大的影响。console.log当您不再需要调用时,将其保留在代码之外。

在设备上没有更多信息的情况下,您不能在其上运行该设备,以及该设备在运行哪些其他应用程序,我无法为您提供明确的解决方案。关闭尽可能多的应用程序和服务,直到发现改进为止。它最有可能只是导致它的一个不良应用。

现在的坏消息

您说您不关心GC。好吧,如果您想扩展游戏,就应该非常担心。使用CPU事件探查器,您的游戏会生成足够多的不需要的RAM,以使GC占总处理时间的2-5%(不包括空闲时间)。您很幸运,您还有余时间,但GC会注意并是导致帧速率不一致的主要原因之一。相比之下,具有良好V坏代码(100,000行代码)的代码如果出现,将永远不会使GC的峰值超过0.1%。

每次运行一些创建Array或的代码Object时,或者在使用new Something()Object.create(foo)创建必须清理的内容时,都应考虑这种方式。如果这样做,那么每一帧都会在RAM中不断产生垃圾。结果是GC的负载恒定,随着代码复杂度的增加,负载只会变得更糟。游戏循环的GC绝对不要超过0.5%,如果做得好,它应该保持在0.1%以下

切勿在游戏循环中使用新游戏

同为foo = []obj = {}obj = Object.create(blah)特别是最后一个。您不仅要为已经具有RAM的对象请求更多的内存,而且Object.create是创建javascript具有的新对象的最慢方法。情况变得更糟,不仅是创建对象的最慢方式,而且创建了访问和使用最慢的对象。忘记您甚至知道该功能已经存在。

错误的内存使用示例。

// creates an object 
function GameThing(a1,a2,a4,a4){
    this.a1 = a1;
    this.a2 = a2;
    this.a3 = a3;
    this.a4 = a4;
    this.data = [1,2,3,4,5,6,7,8,9,10]; // create a new array in game loop BAD
}
function rand(){return Math.random()};);
var gameThing;
function mainLoop(){
    // using new in the main loop BAD
    gameThing = new GameThing(rand(),rand(),rand(),rand()); // dumps the old RAM 
                                                            // for GC and uses some
                                                            // more for the next loop.
}

应该怎么做

// creates an object 
function GameThing(a1,a2,a4,a4){
    this.a1 = a1;
    this.a2 = a2;
    this.a3 = a3;
    this.a4 = a4;
    this.data = [1,2,3,4,5,6,7,8,9,10]; 
}
function reuseGameThing(thing,a1,a2,a3,a4);
    thing.a1 = a1;
    thing.a2 = a2;
    thing.a3 = a3;
    thing.a4 = a4;
    thing.data[1] = 0;  // don't create a new array. The memory has be set
    thing.data[2] = 1;  // aside for it already, why dump it to reset the values
    thing.data[3] = 2;  // just overwrite the old values. The time save 
    thing.data[4] = 3;  // from needless GC hits is well worth he little
    ...                 // extra time for set each item
    ...
    thing.data[9] = 10;
}

function rand(){return Math.random()};);
// create the object ONCE only. It create a ram buffer for your data
// that should not need to be deleted for the life time of the current game state
var gameThing = new GameThing(rand(),rand(),rand(),rand());
function mainLoop(){
    // Instead of dumping the old reuse its memory
    reuseGameThing(gameThing,rand(),rand(),rand(),rand()); 
    // so much quicker as there is no need to find and assign new RAM and
    // GC will not be called 
}

不必担心内存泄漏。您必须非常努力地做到这一点,并且将涉及DOM。您只需触摸DOM。

不要使用Getter和Setters

我从来不明白为什么Javascript编码器会这样做。Getter和Setter用于您没有直接访问权限的私有属性。它们也可以通过设置时检查数据来确保永远不会损害类的状态。它是在获取数据类型时提供抽象。它们用于面向对象的语言,而Javascript并非如此,也永远不可能如此。

当值与getter调用一样可访问时,没有理由使用getter /您所做的就是增加CPU的工作,以获取getter引用,然后将其推入调用堆栈,调用函数,然后获取值引用,将其移至返回引用,弹出调用堆栈并返回流,获取返回引用并将其移至我们想要的变量中。

只需获取值引用,然后将其移至您想要的变量中,便可以更快地完成。Javascript没有私有属性。拥有隐藏属性的唯一方法是通过闭包,并且只应关闭未在其范围之外使用的数据。不要使用它们,尤其是不要在游戏中使用它们。

您的游戏(很多)中还有很多可以优化的东西,但我认为您会全力以赴寻找导致帧不一致的原因,而且您的游戏仅使用了1/60帧时间的5%,因此您就像您在世界上的所有时间一样,可以忽略我所说的一切。

希望我听起来不刺耳。但是,当我看到热心的新程序员从不懂Java语言的旧C ++ / C#Java编码器中吸取了坏习惯时,这便引起了我的注意。



1> Blindman67..:

无需优化

我刚刚看了一下代码,并在小提琴上进行了介绍。好消息是,您无需优化代码,您的运行速度约为最大值的5%。

在您发布的时间轴图像中,您可以看到您的代码大多数时候几乎没有执行任何操作。优化总是好的,但是对于您的应用程序,将中间时间从95%增加到96%并不值得。

最佳负载V过载

我包括一张图像,该图像显示时间轴上每秒60fps的最佳负载应该是什么样子,然后过载的帧率看起来就可以每秒管理30帧,并且由于存在30帧而使帧率从30跳到20太多的事情要做。

与OP的相比

[2

看其他地方

当您比较它们时,您可以看到您的游戏中几乎没有任何东西发生,大多数帧是空闲时间。您的代码可以达到接近1000 Fps的速度,并且仍有空闲时间。帧速率的问题出在其他地方,要么是占用CPU时间,要么是共享GPU占用了带宽和RAM。

如果您始终打开开发工具,那么您所做的很多console.log事情都会产生很大的影响。console.log当您不再需要调用时,将其保留在代码之外。

在设备上没有更多信息的情况下,您不能在其上运行该设备,以及该设备在运行哪些其他应用程序,我无法为您提供明确的解决方案。关闭尽可能多的应用程序和服务,直到发现改进为止。它最有可能只是导致它的一个不良应用。

现在的坏消息

您说您不关心GC。好吧,如果您想扩展游戏,就应该非常担心。使用CPU事件探查器,您的游戏会生成足够多的不需要的RAM,以使GC占总处理时间的2-5%(不包括空闲时间)。您很幸运,您还有余时间,但GC会注意并是导致帧速率不一致的主要原因之一。相比之下,具有良好V坏代码(100,000行代码)的代码如果出现,将永远不会使GC的峰值超过0.1%。

每次运行一些创建Array或的代码Object时,或者在使用new Something()Object.create(foo)创建必须清理的内容时,都应考虑这种方式。如果这样做,那么每一帧都会在RAM中不断产生垃圾。结果是GC的负载恒定,随着代码复杂度的增加,负载只会变得更糟。游戏循环的GC绝对不要超过0.5%,如果做得好,它应该保持在0.1%以下

切勿在游戏循环中使用新游戏

同为foo = []obj = {}obj = Object.create(blah)特别是最后一个。您不仅要为已经具有RAM的对象请求更多的内存,而且Object.create是创建javascript具有的新对象的最慢方法。情况变得更糟,不仅是创建对象的最慢方式,而且创建了访问和使用最慢的对象。忘记您甚至知道该功能已经存在。

错误的内存使用示例。

// creates an object 
function GameThing(a1,a2,a4,a4){
    this.a1 = a1;
    this.a2 = a2;
    this.a3 = a3;
    this.a4 = a4;
    this.data = [1,2,3,4,5,6,7,8,9,10]; // create a new array in game loop BAD
}
function rand(){return Math.random()};);
var gameThing;
function mainLoop(){
    // using new in the main loop BAD
    gameThing = new GameThing(rand(),rand(),rand(),rand()); // dumps the old RAM 
                                                            // for GC and uses some
                                                            // more for the next loop.
}

应该怎么做

// creates an object 
function GameThing(a1,a2,a4,a4){
    this.a1 = a1;
    this.a2 = a2;
    this.a3 = a3;
    this.a4 = a4;
    this.data = [1,2,3,4,5,6,7,8,9,10]; 
}
function reuseGameThing(thing,a1,a2,a3,a4);
    thing.a1 = a1;
    thing.a2 = a2;
    thing.a3 = a3;
    thing.a4 = a4;
    thing.data[1] = 0;  // don't create a new array. The memory has be set
    thing.data[2] = 1;  // aside for it already, why dump it to reset the values
    thing.data[3] = 2;  // just overwrite the old values. The time save 
    thing.data[4] = 3;  // from needless GC hits is well worth he little
    ...                 // extra time for set each item
    ...
    thing.data[9] = 10;
}

function rand(){return Math.random()};);
// create the object ONCE only. It create a ram buffer for your data
// that should not need to be deleted for the life time of the current game state
var gameThing = new GameThing(rand(),rand(),rand(),rand());
function mainLoop(){
    // Instead of dumping the old reuse its memory
    reuseGameThing(gameThing,rand(),rand(),rand(),rand()); 
    // so much quicker as there is no need to find and assign new RAM and
    // GC will not be called 
}

不必担心内存泄漏。您必须非常努力地做到这一点,并且将涉及DOM。您只需触摸DOM。

不要使用Getter和Setters

我从来不明白为什么Javascript编码器会这样做。Getter和Setter用于您没有直接访问权限的私有属性。它们也可以通过设置时检查数据来确保永远不会损害类的状态。它是在获取数据类型时提供抽象。它们用于面向对象的语言,而Javascript并非如此,也永远不可能如此。

当值与getter调用一样可访问时,没有理由使用getter /您所做的就是增加CPU的工作,以获取getter引用,然后将其推入调用堆栈,调用函数,然后获取值引用,将其移至返回引用,弹出调用堆栈并返回流,获取返回引用并将其移至我们想要的变量中。

只需获取值引用,然后将其移至您想要的变量中,便可以更快地完成。Javascript没有私有属性。拥有隐藏属性的唯一方法是通过闭包,并且只应关闭未在其范围之外使用的数据。不要使用它们,尤其是不要在游戏中使用它们。

您的游戏(很多)中还有很多可以优化的东西,但我认为您会全力以赴寻找导致帧不一致的原因,而且您的游戏仅使用了1/60帧时间的5%,因此您就像您在世界上的所有时间一样,可以忽略我所说的一切。

希望我听起来不刺耳。但是,当我看到热心的新程序员从不懂Java语言的旧C ++ / C#Java编码器中吸取了坏习惯时,这便引起了我的注意。

推荐阅读
重庆制造漫画社
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有