在我写作的游戏中,我的表现一直存在很大问题。出于某种原因,在游戏过程中,游戏口吃并且似乎要么绘制相同的框架两次,要么只是跳过该框架的绘图。正如你在图像中看到的那样,出于某种原因,当这个jank / stutter发生时,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.height / 14;
rightSideSpriteGame2.x = c.width * .75;
rightSideSpriteGame2.y = -65;
rightSideSpriteGame2.vy = c.height / gameVelocity;
movingGameObjectsArray.push(rightSideSpriteGame2);
}
if (randomGameNumber === 3) {
//the blue game object
var leftSideSpriteGame3 = Object.create(mainObject);
leftSideSpriteGame3.sourceX = 0;
leftSideSpriteGame3.sourceY = 128;
leftSideSpriteGame3.sourceWidth = 103;
leftSideSpriteGame3.sourceHeight = 64;
leftSideSpriteGame3.width = c.width * .25;
leftSideSpriteGame3.height = c.height / 14;
leftSideSpriteGame3.x = 0
leftSideSpriteGame3.y = -65;
leftSideSpriteGame3.vy = c.height / gameVelocity;
movingGameObjectsArray.push(leftSideSpriteGame3);
var rightSideSpriteGame3 = Object.create(mainObject);
rightSideSpriteGame3.sourceX = 207;
rightSideSpriteGame3.sourceY = 128;
rightSideSpriteGame3.sourceWidth = 207;
rightSideSpriteGame3.sourceHeight = 64;
rightSideSpriteGame3.width = c.width * .5;
rightSideSpriteGame3.height = c.height / 14;
rightSideSpriteGame3.x = c.width * .5;
rightSideSpriteGame3.y = -65;
rightSideSpriteGame3.vy = c.height / gameVelocity;
movingGameObjectsArray.push(rightSideSpriteGame3);
}
if (randomGameNumber === 4) {
//the pink game object
var rightSideSpriteGame4 = Object.create(mainObject);
rightSideSpriteGame4.sourceX = 104;
rightSideSpriteGame4.sourceY = 192;
rightSideSpriteGame4.sourceWidth = 310;
rightSideSpriteGame4.sourceHeight = 64;
rightSideSpriteGame4.width = c.width * .75;
rightSideSpriteGame4.height = c.height / 14;
rightSideSpriteGame4.x = c.width * .25;
rightSideSpriteGame4.y = -65;
rightSideSpriteGame4.vy = c.height / gameVelocity;
movingGameObjectsArray.push(rightSideSpriteGame4);
}
}
}
function moveAllRectangles() {
interval = interval + 1;
if (interval % 60 === 0 || interval === 1) {
var randomNumber = Math.floor((Math.random() * 4) + 1);
if (randomNumber === 1) {
var seg1 = Object.create(segment1);
seg1.width = c.width * .75;
seg1.height = c.height / 14;
seg1.y = -c.height / 14;
seg1.vy = c.height / halfVelocity;
movingMenuObjectsArray.push(seg1);
}
if (randomNumber === 2) {
var seg2 = Object.create(segment2);
seg2.width = c.width * .5;
seg2.height = c.height / 14;
seg2.y = -c.height / 14;
seg2.vy = c.height / halfVelocity;
movingMenuObjectsArray.push(seg2);
var segL = Object.create(segment3);
segL.width = c.width * .25;
segL.height = c.height / 14;
segL.x = c.width * .75;
segL.y = -c.height / 14;
segL.vy = c.height / halfVelocity;
movingMenuObjectsArray.push(segL);
}
if (randomNumber === 3) {
var seg3 = Object.create(segment4);
seg3.width = c.width * .25;
seg3.height = c.height / 14;
seg3.y = -c.height / 14;
seg3.vy = c.height / halfVelocity;
movingMenuObjectsArray.push(seg3);
var seg3R = Object.create(segment5);
seg3R.width = c.width * .5;
seg3R.height = c.height / 14;
seg3R.x = c.width * .5;
seg3R.y = -c.height / 14;
seg3R.vy = c.height / halfVelocity;
movingMenuObjectsArray.push(seg3R);
}
if (randomNumber === 4) {
var seg4 = Object.create(segment6);
seg4.width = c.width * .75;
seg4.height = c.height / 14;
seg4.x = c.width * .25;
seg4.y = -c.height / 14;
seg4.vy = c.height / halfVelocity;
movingMenuObjectsArray.push(seg4);
}
}
if (movingMenuObjectsArray.length !== 0) {
for (var i = 0; i < movingMenuObjectsArray.length; i++) {
var sprite = movingMenuObjectsArray[i];
sprite.y += sprite.vy;
ds.drawImage(
menuObjects,
sprite.sourceX, sprite.sourceY,
sprite.sourceWidth, sprite.sourceHeight,
sprite.x, sprite.y,
sprite.width, sprite.height
);
if (sprite.y > c.height) {
var removeThis = sprite;
}
}
}
removeTheMenuObject(removeThis, movingMenuObjectsArray);
}
function removeTheMenuObject(objectToRemove, array) {
var i = array.indexOf(objectToRemove);
if (i !== -1) {
array.splice(i, 1);
}
}
function renderGameObjects() {
if (gameObjectsArray.length !== 0) {
ds.drawImage(
image,
0, 0,
414, 736,
0, 0,
414, 736
);
}
}
}());
答案 0 :(得分:4)
无需优化
我刚看好代码并在小提琴上对其进行了分析。好消息是您不需要优化代码,运行速度约为最大值的5%
在您发布的时间线图片中,您可以看到您的代码在大多数时间内几乎没有任何内容。优化总是很好,但对于你的应用程序,将iddle time从95%增加到96%是不值得的。
最佳加载V重载
我已经包含了一个图像,该图像显示了时间轴上每秒60fps的最佳加载效果,以及过载帧率如何只能管理每秒30帧并且帧速率从30-20跳跃因为做得太多了。
与OP的相比
[ 2
查看其他地方
当你比较它们时,你可以看到游戏中几乎没有任何事情发生,大部分帧都是空闲时间,你的代码可以接近1000 Fps并且还有空闲时间。您的帧速率问题在其他地方,无论是占用CPU时间还是您正在使用占用带宽和RAM的东西。
如果你一直打开开发工具,你会做很多可能产生重大影响的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%
永远不要在游戏循环中使用new
foo = []
,obj = {}
,obj = Object.create(blah)
,尤其是最后一个。您不仅要为已有RAM的对象请求更多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。
请勿使用Getters and Setters
我从未理解为什么Javascript编码器会这样做。 Getters和Setter适用于您无法直接访问的私有财产。它们也确保在设置时通过审查数据来保证类的状态不会受到损害。它是在获取时为数据类型提供抽象。它们适用于面向对象的语言,Javascript不是也不可能。
当值与getter调用一样可访问时,没有理由使用getter /你所做的就是增加CPU检索getter引用的工作,然后将其推送到调用堆栈,调用函数,然后获取值引用,将其移动到return referance,弹出调用堆栈并返回到流程,获取返回参考并将其移动到我们想要的变量。
只需获取值引用,将其移动到您想要的变量即可快得多。完成。 Javascript没有私有属性。拥有隐藏属性的唯一方法是通过闭包,您应该只关闭在其范围之外未使用的数据。不要使用它们,特别是不要在游戏中使用它们。
还有很多可以在你的游戏中进行优化(很多),但我认为你的双手完全找不到你的画面不一致的原因,你的游戏只有5%的1/60帧使用率时间,所以你可以忽略我在世界上所有时间所说的一切。
希望我的声音听起来不刺耳。但是,当我看到热情的新程序员从不熟悉如何使用Javascript的旧C ++ / C#Java程序员中汲取不良习惯时,它就会出现。