如何在以下HTML 5画布游戏中减少延迟?

时间:2014-01-24 13:24:46

标签: javascript performance canvas html5-canvas

我正在尝试使用HTML 5 canvas和JavaScript进行游戏编码。我的游戏还没有完成,但我已经遇到了一些突出的滞后问题。游戏可以在这里找到:http://survival.sthost.net/gunsvsships.html 此外,任何其他有关改进功能的建议都会有所帮助。这是js:

$(document).ready(function(){

var canvas = $("#canvas")[0];
var ctx = canvas.getContext("2d");
var w = $("#canvas").width();
var h = $("#canvas").height();
var cw = 10;

var title = new Image();
var startButton = new Image();
var startButtonSelected1 = new Image();
var startButtonSelected2 = new Image();
var startButtonSelected3 = new Image();
var background = new Image();
var ship = new Image();
var dead_ship = new Image();
var retical = new Image();
var bullet = new Image();
var slug = new Image();
var gunLabel1 = new Image();
var gunLabel2 = new Image();
var gunLabel3 = new Image();
var gunLabel4 = new Image();
var gunLabel5 = new Image();
var gunLabel1Trans = new Image();
var gunLabel2Trans = new Image();
var gunLabel3Trans = new Image();
var currentStartButton = startButton;
var startButtonIncrementor = 0;
var startButtonTimeNow = new Date().getTime();
var startButtonTimeThen = new Date().getTime();
var drawStart = true;
var gun = "Sniper"
var credits = 0;
var dead = 0;
var dead2 = 0;
var dead3 = 0;
var newSpeed = 5;
var bulletSpeed = 8;
var shipX = 0;
var ship2X = 1000;
var ship3X = 0;
var shipY = 250;
var ship2Y = 300;
var ship3Y = 250;
var bulletX = 500;
var bulletY = 550;
var slugAX = 500;
var slugBX = 500;
var slugCX = 500;
var slugAY = 550;
var slugBY = 550;
var slugCY = 550;
var gunType = -1;
var reticalX = 0;
var reticalY = 0;
var reticalX2 = -1;
var reticalY2 = -1;
var drawShipA = true;
var drawShipB = true;
var drawShipC = true;
var drawBullet = false;
var drawSlugs = false;
var now, then = new Date().getTime(), delta;
var sniperSound = new Audio("sniper.wav");
var shotgunSound = new Audio("shotgun.wav");

title.src = "images/title1.png";
startButton.src = "images/start_button.png";
startButtonSelected1.src = "images/start_button_selected1.png";
startButtonSelected2.src = "images/start_button_selected2.png";
startButtonSelected3.src = "images/start_button_selected3.png";
background.src = "images/background.png";
ship.src = "images/ship.png";
dead_ship.src = "images/crashed_ship.png";
retical.src = "images/retical.png";
bullet.src = "images/bullet.png";
slug.src = "images/slug.png";
gunLabel1.src = "images/1.png"; 
gunLabel2.src = "images/2.png"; 
gunLabel3.src = "images/3.png"; 
gunLabel4.src = "images/4.png"; 
gunLabel5.src = "images/5.png"; 
gunLabel1Trans.src = "images/1_trans.png";
gunLabel2Trans.src = "images/2_trans.png";
gunLabel3Trans.src = "images/3_trans.png";

sniperSound.volume = 0.1;

function getRandomInt(min, max) {
    return Math.floor(Math.random() * (max - min + 1) + min);
}

function findPos(obj) {
    var curleft = 0, curtop = 0;
    if (obj.offsetParent) {
        do {
            curleft += obj.offsetLeft;
            curtop += obj.offsetTop;
        } while (obj = obj.offsetParent);
        return { x: curleft, y: curtop };
    }
    return undefined;
}


$('#canvas').mousemove(function(e) {
    var pos = findPos(this);
    reticalX = e.pageX - pos.x;
    reticalY = e.pageY - pos.y;
});

$('#canvas').click(function(e) {
    var pos = findPos(this);
    reticalX2 = e.pageX - pos.x - 40;
    reticalY2 = e.pageY - pos.y - 40;
    if (gunType == 0){
        if ((Math.abs(reticalX2 - (shipX - 35)) <= 50) && (Math.abs(reticalY2 - (shipY + 35)) <= 50)){
            dead = 1;
            credits += 100}
        if ((Math.abs(reticalX2 - (ship2X - 10)) <= 50) && (Math.abs(reticalY2 - (ship2Y + 10)) <= 50)){
            dead2 = 1;
            credits += 100}
        if ((Math.abs(reticalX2 - (ship3X - 10)) <= 50) && (Math.abs(reticalY2 - (ship3Y + 10)) <= 50)){
            dead3 = 1;
            credits += 100}}
    else if (gunType == 1 && drawBullet == false){
        drawBullet = true;
        bulletX = reticalX2;
        sniperSound.play();}
    else if (gunType == 2 && drawSlugs == false){
        drawSlugs = true;
        slugAX = reticalX2;
        slugBX = reticalX2;
        slugCX = reticalX2;
        shotgunSound.play();}        
    else if (gunType == -1)
        if ((reticalX >= 370 && reticalY >= 220) && (reticalX < 520 && reticalY < 300)) {
            drawStart = false;
            gunType = 0;}
});

$(document).keydown(function(e) {
    if (drawStart == false) {
        if (drawBullet == false && drawSlugs == false) {
            switch(e.keyCode) {
                case 49:
                    gunType = 0;
                    gun = "Sniper";
                    break;
                case 50:
                    gunType = 1;
                    gun = "Revolver";
                    break;
                case 51:
                    gunType = 2;
                    gun = "Shotgun";
                    break;}
    }}            

});


function update() {

    if ((reticalX >= 370 && reticalY >= 220) && (reticalX < 520 && reticalY < 280)) {
        if ((startButtonTimeNow - startButtonTimeThen) < 500) {
            currentStartButton = startButtonSelected1;
            startButtonTimeNow = new Date().getTime()}
        else if ((startButtonTimeNow - startButtonTimeThen) < 1000) {
            currentStartButton = startButtonSelected2;
            startButtonTimeNow = new Date().getTime();}
        else if ((startButtonTimeNow - startButtonTimeThen) < 1500) {
            currentStartButton = startButtonSelected3;
            startButtonTimeNow = new Date().getTime();}
        else if ((startButtonTimeNow - startButtonTimeThen) < 2000) {
            startButtonTimeNow, startButtonTimeThen = new Date().getTime(), new Date().getTime();}
        else {startButtonTimeNow, startButtonTimeThen = new Date().getTime(), new Date().getTime();}
    }        

    else {currentStartButton = startButton;}

    if (shipY >= -70 && dead == 0){
        drawShipA = true;
        shipY -= newSpeed;
        shipX += newSpeed;
    }
    else if (dead == 1 && shipY < 600){
        shipY += newSpeed;
        drawShipA = false;
    }            
    else {   
        shipY = getRandomInt(400, 425);
        shipX = getRandomInt(100, 900);
        dead = 0;
        drawShipA = true;
    }

    if (ship2Y >= -70 && dead2 == 0){
        drawShipB = true;
        ship2Y -= newSpeed;
        ship2X -= newSpeed;
    }
    else if (dead2 == 1 && ship2Y < 600){
        ship2Y += newSpeed;
        drawShipB = false;
    }
    else {   
        ship2Y = getRandomInt(400, 425);
        ship2X = getRandomInt(100, 900);
        dead2 = 0;
        drawShipB = true;;
    }

    if (ship3Y >= -70 && dead3 == 0){
        drawShipC = true;
        ship3Y -= newSpeed;
        ship3X -= newSpeed;
    }
    else if (dead3 == 1 && ship3Y < 600){
        ship3Y += newSpeed;
        drawShipC = false;
    }
    else {   
        ship3Y = getRandomInt(400, 425);
        ship3X = getRandomInt(100, 900);
        dead3 = 0;
        drawShipC = true;
    }

    if ((dead + dead2 + dead3 >= 2) && (((Math.abs(shipX - ship2X) + Math.abs(shipY - ship2Y)) + (Math.abs(shipX - ship3X) + Math.abs(shipY - ship3Y)) + (Math.abs(ship3X - ship2X) + Math.abs(ship3Y - ship2Y))) < 1500)) {credits += 50}

    if (drawBullet == true){  
        bulletY -= bulletSpeed;
        if ((Math.abs(bulletX - (shipX + 20)) <= 40) && (Math.abs(bulletY - (shipY + 10)) <= 50)){
            dead = 1;
            credits += 100}
        if ((Math.abs(bulletX - (ship2X + 20)) <= 40) && (Math.abs(bulletY - (ship2Y + 10)) <= 50)){
            dead2 = 1;
            credits += 100}
        if ((Math.abs(bulletX - (ship3X + 20)) <= 40) && (Math.abs(bulletY - (ship3Y + 10)) <= 50)){
            dead3 = 1;
            credits += 100}
        if (bulletY <= -130){
            drawBullet = false;
            bulletY = 550;
        } }

    if (drawSlugs == true){
        slugAY -= bulletSpeed;
        bulletSpeed = bulletSpeed * 2;
        bulletSpeed = (bulletSpeed - (bulletSpeed % 3))/3
        slugBX -= bulletSpeed;
        slugBY -= bulletSpeed * 2;
        slugCX += bulletSpeed;
        slugCY -= bulletSpeed * 2;
        if ((Math.abs(slugAX - (shipX + 10)) <= 40) && (Math.abs(slugAY - (shipY + 10)) <= 50)){
            dead = 1;
            credits += 100}
        if ((Math.abs(slugAX - (ship2X + 10)) <= 40) && (Math.abs(slugAY - (ship2Y + 10)) <= 50)){
            dead2 = 1;
            credits += 100}
        if ((Math.abs(slugAX - (ship3X + 10)) <= 40) && (Math.abs(slugAY - (ship3Y + 10)) <= 50)){
            dead3 = 1;
            credits += 100}   
         if ((Math.abs(slugBX - (shipX + 10)) <= 40) && (Math.abs(slugBY - (shipY + 10)) <= 50)){
            dead = 1;
            credits += 100}
        if ((Math.abs(slugBX - (ship2X + 10)) <= 40) && (Math.abs(slugBY - (ship2Y + 10)) <= 50)){
            dead2 = 1;
            credits += 100}
        if ((Math.abs(slugBX - (ship3X + 10)) <= 40) && (Math.abs(slugBY - (ship3Y + 10)) <= 50)){
            dead3 = 1;
            credits += 100}             
         if ((Math.abs(slugCX - (shipX + 10)) <= 40) && (Math.abs(slugCY - (shipY + 10)) <= 50)){
            dead = 1;
            credits += 100}
        if ((Math.abs(slugCX - (ship2X + 10)) <= 40) && (Math.abs(slugCY - (ship2Y + 10)) <= 50)){
            dead2 = 1;
            credits += 100}
        if ((Math.abs(slugCX - (ship3X + 10)) <= 40) && (Math.abs(slugCY - (ship3Y + 10)) <= 50)){
            dead3 = 1;
            credits += 100} 
        if (slugAY <= -150){
            drawSlugs = false;
            slugAY = 550;
            slugBY = 550;
            slugCY = 550;}}

}


function paint()
{
    now = new Date().getTime();
    delta = now - then;

    ctx.drawImage(background,0,0);

    if (drawShipA == true){ctx.drawImage(ship, shipX, shipY, 70, 70);}
    else {ctx.drawImage(dead_ship, shipX, shipY, 70, 70);}

    if (drawShipB == true){ctx.drawImage(ship, ship2X, ship2Y, 70, 70);}
    else {ctx.drawImage(dead_ship, ship2X, ship2Y, 70, 70);}        

    if (drawShipC == true){ctx.drawImage(ship, ship3X, ship3Y, 70, 70);}
    else {ctx.drawImage(dead_ship, ship3X, ship3Y, 70, 70);}            

    if (drawBullet == true){
        ctx.drawImage(bullet, bulletX + 30, bulletY, 10,10);}

    if (drawSlugs == true){
        ctx.drawImage(slug, slugAX + 20, slugAY, 30, 30);
        ctx.drawImage(slug, slugBX + 20, slugBY, 30, 30);
        ctx.drawImage(slug, slugCX + 20, slugCY, 30, 30);        
    }

    bulletSpeed = calcSpeed(delta, 6);
    newSpeed = calcSpeed(delta, 3);    
    then = now;
    ctx.drawImage(retical, reticalX - 40, reticalY - 40, 80, 80);
    ctx.font = "30px sans-serif";
    ctx.strokeStyle = "rgba(255, 165, 0, 1)"
    ctx.strokeText("$" + credits, 15, 30);
    ctx.strokeStyle = "rgba(255, 165, 0, .8)"
    ctx.strokeText(gun, 450, 500);
    ctx.drawImage(gunLabel1, 445, 510, 35, 35);
    ctx.drawImage(gunLabel2, 470, 510, 35, 35);
    ctx.drawImage(gunLabel3, 495, 510, 35, 35);
    switch(gunType) {
        case 0:
            ctx.drawImage(gunLabel1Trans, 445, 510, 35, 35);
            break;
        case 1:
            ctx.drawImage(gunLabel2Trans, 470, 510, 35, 35);
            break;
        case 2:
            ctx.drawImage(gunLabel3Trans, 495, 510, 35, 35);
            break;
            }
    if (drawStart == true) {
        ctx.drawImage(background, 0, 0);
        ctx.drawImage(title, 270, 90, 400, 100);
        ctx.drawImage(currentStartButton, 370, 220, 150, 60);
        ctx.drawImage(retical, reticalX - 40, reticalY - 40, 80, 80);}  
    ctx.strokeRect(0,0,w,background.height);
    }

var init = function()

{
    requestAnimationFrame(init);
    update();
    paint();
}   

var calcSpeed = function(del, speed) {
    return (speed * 60 * del) / 1000;
}

init();})

1 个答案:

答案 0 :(得分:3)

问题是你的游戏速度与你的帧速率有关:

var init = function()

{
    requestAnimationFrame(init);
    update();
    paint();
}   

当每秒的帧数下降时,每秒的更新次数也会减少。结果,当帧速率下降时游戏减慢,当帧速率上升时游戏速度加快。要解决此问题,请按固定间隔执行update-loop:

 window.setInterval(update, 10);

这样,游戏逻辑将始终每秒更新100次,无论渲染现在多快或多慢。

执行此操作时,您可能会注意到一个问题:绘图发生时无法控制。在调用paint期间,对update的调用可能会发生。结果它将绘制一个游戏状态,即半逻辑帧n和半逻辑帧n + 1。这可能会导致一些奇怪的图形故障。我通常像这样构建我的游戏循环(伪代码):

 calculated_time_in_ms = getCurrentTimeInMs();

 while(game_is_running) {
     paint();
     while(calculated_time_in_ms < getCurrentTimeInMs()) {
          update();
          calculated_time_in_ms += 10;
     }
 }

这样维护游戏逻辑帧率始终优先于渲染,但是当图形引擎足够快时,它能够比游戏逻辑更新更频繁地绘制。