我一直在为我正在写的游戏带来性能问题。出于某种原因,在游戏过程中,游戏口吃并且似乎要么绘制相同的框架两次,要么只是跳过该框架的绘图。正如你在图像中看到的那样,出于某种原因,当这个jank / stutter发生时,FPS下降到30并且帧时间是两倍。这些口吃非常频繁,每1-5秒发生一次。我测试了内存泄漏没什么异常,测试垃圾收集,没什么不寻常的。调用移动/渲染游戏对象的三个函数是moveAllGameRectangles(); renderTheGameObjects(); checkForCollisonsRectangles(); (以该顺序)。它们在update()函数中使用requestAnimationFrame调用。这是我创建游戏的第一次尝试,所以任何帮助都会很棒。 Image
(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
);
}
}
}());