2017-02-23 45 views
1

我做了一个简单的游戏。目标是通过避开激光器保持活力。但是,每当玩家穿过激光警告时,它就会擦除激光,使其看起来很奇怪。我已经尝试重新绘制警告,但每次发生这种情况都不起作用并使其变得更加缓慢。在我的简单游戏中,玩家总是擦除激光警告,而我似乎无法修复它。

<!DOCTYPE html> 
<html> 
<head> 
<title> 
    Drift 2 
</title> 
</head> 
<body> 
<center> 
<h1>Drift 2</h1> 
<h2>by Milesman34</h2> 
<canvas id="canvas" width="400" height="400"></canvas> 
<strong><p id="score">Score</p></strong> 
</center> 
<script src="https://code.jquery.com/jquery-2.1.0.js"></script> 
<script> 
    //MARK: Set up the canvas + canvas variables and draws the border + background 
    var canvas = document.getElementById("canvas"); 
    var ctx = canvas.getContext("2d"); 

    var width = canvas.width; 
    var height = canvas.height; 

    var blockSize = 20; 
    var widthInBlocks = width/blockSize; 
    var heightInBlocks = height/blockSize; 

    ctx.fillStyle = "rgb(225, 225, 225)"; 
    ctx.fillRect(0, 0, width, height); 
    //MARK: Defines arrays and variables 
    var shooterArray = []; 
    var score = 0; 
    //MARK: Defines functions 
    function getRandomFromInterval(interval) { 
     return Math.floor(Math.random() * interval); 
    }; 
    //MARK: Defines game over function 
    var gameEnd = 0; 
    function gameOver() { 
     setTimeout (function() { 
      gameEnd = 1; 
      clearInterval (scoreEffects); 
      clearInterval (fireEffects); 
      ctx.clearRect(blockSize, blockSize, width - (blockSize * 2), height - (blockSize * 2)) 
      ctx.fillStyle = "rgb(225, 225, 225)"; 
      ctx.fillRect(blockSize, blockSize, width - (blockSize * 2), height - (blockSize * 2)); 
      ctx.font = "60px Courier"; 
      ctx.fillStyle = "Black"; 
      ctx.textAlign = "center"; 
      ctx.textBaseline = "middle"; 
      ctx.fillText("Game Over", width/2, height/2); 
     }, 149); 
    }; 
    //MARK: Defines the player 
    function Player (x, y) { 
     this.x = x; 
     this.y = y; 
    }; 
    //MARK: Defines the function that draws the player 
    Player.prototype.draw = function() { 
     ctx.fillStyle = "rgb(185, 185, 185)"; 
     ctx.fillRect(this.x, this.y, blockSize, blockSize); 
    }; 
    var player = new Player(width/2, height/2); 
    player.draw(); 
    //MARK: Defines the functions that move the player 
    Player.prototype.moveLeft = function() { 
     ctx.clearRect(this.x, this.y, blockSize, blockSize); 
     this.x = this.x - 20; 
     ctx.fillStyle = "rgb(225, 225, 225)"; 
     ctx.fillRect(this.x + 20, this.y, blockSize, blockSize); 
    }; 
    Player.prototype.moveRight = function() { 
     ctx.clearRect(this.x, this.y, blockSize, blockSize); 
     this.x = this.x + 20; 
     ctx.fillStyle = "rgb(225, 225, 225)"; 
     ctx.fillRect(this.x - 20, this.y, blockSize, blockSize); 
    }; 
    Player.prototype.moveUp = function() { 
     ctx.clearRect(this.x, this.y, blockSize, blockSize); 
     this.y = this.y - 20; 
     ctx.fillStyle = "rgb(225, 225, 225)"; 
     ctx.fillRect(this.x, this.y + 20, blockSize, blockSize); 
    }; 
    Player.prototype.moveDown = function() { 
     ctx.clearRect(this.x, this.y, blockSize, blockSize); 
     this.y = this.y + 20; 
     ctx.fillStyle = "rgb(225, 225, 225)"; 
     ctx.fillRect(this.x, this.y - 20, blockSize, blockSize); 
    }; 
    Player.prototype.checkWallCollision = function() { 
     if (this.x === 0 || this.x === width - 20 || this.y === 0 || this.y === height - 20) { 
      gameOver(); 
     }; 
    }; 
    //MARK: Defines the Shooter 
    function Shooter (x, y, direction) { 
     this.x = x; 
     this.y = y; 
     if (["left", "right", "up", "down"].indexOf(direction) != -1) { 
      this.direction = direction; 
     }; 
     shooterArray.push(this); 
    }; 
    //MARK: Defines the function that draws the Shooter 
    Shooter.prototype.draw = function() { 
     ctx.fillStyle = "rgb(185, 185, 185)"; 
     ctx.fillRect(this.x, this.y, blockSize, blockSize); 
    }; 
    //MARK: Defines the function that fires the Shooter 
    var timeoutID = null; 
    function fireLeftRight(y) { 
     if (gameEnd === 0) { 
      ctx.fillStyle = "Red"; 
      ctx.fillRect(blockSize, y, width - (blockSize * 2), blockSize); 
      if (player.y === y) { 
       gameOver(); 
      }; 
     }; 
    }; 
    function fireLeftRightWarn(y) { 
     ctx.fillStyle = "Red"; 
     for (i = 1;i < widthInBlocks - 1;i++) { 
      ctx.fillRect(i * blockSize + (blockSize/4), y + (blockSize/4 * 1.5), blockSize/2, blockSize/4); 
     }; 
     timeoutID2 = setTimeout (function() { 
      clearTimeout (timeoutID); 
      timeoutID = setTimeout (fireLeftRight(y), 100); 
     }, 600); 
    }; 
    function fireUpDown(x) { 
     if (gameEnd === 0) { 
      ctx.fillStyle = "Red"; 
      ctx.fillRect(x, blockSize, blockSize, height - (blockSize * 2)); 
      if (player.x === x) { 
       gameOver(); 
      }; 
     }; 
    }; 
    function fireUpDownWarn(x) { 
     ctx.fillStyle = "Red"; 
     for (i = 1;i < heightInBlocks - 1;i++) { 
      ctx.fillRect(x + (blockSize/4 * 1.5), i * blockSize + (blockSize/4), blockSize/4, blockSize/2); 
     }; 
     timeoutID2 = setTimeout (function() { 
     clearTimeout (timeoutID); 
     timeoutID = setTimeout (fireUpDown(x)) 
     }, 600); 
    }; 
    Shooter.prototype.fire = function() { 
     if (this.direction === "left" || this.direction === "right") { 
      timeoutID = setTimeout (fireLeftRightWarn(this.y), 1); 
     } else { 
      timeoutID = setTimeout (fireUpDownWarn(this.x), 1) 
     }; 
    }; 
    //MARK: Creates the required shooters 
    for (i = 1;i < heightInBlocks - 1;i++) { 
     new Shooter(0, i * blockSize, "right"); 
    }; 
    for (i = 1;i < heightInBlocks - 1;i++) { 
     new Shooter(width - blockSize, i * blockSize, "left"); 
    }; 
    for (i = 1;i < widthInBlocks - 1;i++) { 
     new Shooter(i * blockSize, 0, "down"); 
    }; 
    for (i = 1;i < widthInBlocks - 1;i++) { 
     new Shooter(i * blockSize, height - blockSize, "up") 
    }; 
    for (i = 0;i < shooterArray.length;i++) { 
     shooterArray[i].draw(); 
    }; 
    ctx.fillStyle = "rgb(185, 185, 185)"; 
    ctx.fillRect(0, 0, blockSize, blockSize); 
    ctx.fillRect(width - blockSize, 0, blockSize, blockSize); 
    ctx.fillRect(0, height - blockSize, blockSize, blockSize); 
    ctx.fillRect(width - blockSize, height - blockSize, blockSize, blockSize); 
    //MARK: Draws the score 
    function drawScore() { 
     $("#score").text("Score: " + Math.floor(score)); 
    }; 
    //MARK: Convert keycodes to directions 
    var directions = { 
     37: "left", 
     38: "up", 
     39: "right", 
     40: "down" 
    }; 
    //MARK: This is the interval loop 
    var scoreEffects = setInterval (function() { 
     score += 0.1; 
     drawScore(); 
     player.draw(); 
     player.checkWallCollision(); 
    }, 100); 
    $("body").keyup(function (event) { 
      if (gameEnd != 1) { 
       var moveDir = directions[event.keyCode]; 
       if (moveDir === "left") { 
        player.moveLeft(); 
       } else if (moveDir === "right") { 
        player.moveRight(); 
       } else if (moveDir === "up") { 
        player.moveUp(); 
       } else if (moveDir === "down") { 
        player.moveDown(); 
       }; 
      }; 
     }); 
    var fireEffects = setInterval (function() { 
     ctx.clearRect(blockSize, blockSize, width - (blockSize * 2), height - (blockSize * 2)) 
     ctx.fillStyle = "rgb(225, 225, 225)"; 
     ctx.fillRect(blockSize, blockSize, width - (blockSize * 2), height - (blockSize * 2)); 
     ctx.fillStyle = "rgb(185, 185, 185)"; 
     ctx.fillRect(0, 0, blockSize, blockSize); 
     ctx.fillRect(width - blockSize, 0, blockSize, blockSize); 
     ctx.fillRect(0, height - blockSize, blockSize, blockSize); 
     ctx.fillRect(width - blockSize, height - blockSize, blockSize, blockSize); 
     for (i = 0;i < shooterArray.length;i++) { 
      if (getRandomFromInterval(30) === 0) { 
       shooterArray[i].fire(); 
      }; 
     }; 
    }, 750); 
</script> 

+0

目前还不清楚,在[JSFiddle](https://jsfiddle.net/2keb9930/)中查看过这个功能是什么。 – leigero

+0

为什么不在一个单独的画布层上绘制玩家。有2个帆布彼此顶部。将播放器画布放在顶部并使其具有清晰的背景。 'fillStyle =“rgba(0,0,0,0)”;' 然后你不需要担心玩家的动作会清除激光警告的绘图。 – bobjoe

回答

1

在这里工作项目:https://jsfiddle.net/ay7kp7yb/1/

我认为最好的办法来解决这个问题是与经典的模式工作。这样游戏中发生的所有事件都需要链接到主循环(请参阅:Game Loop)。

类似:

while (true) 
{ 
    processInputs(); 
    updateObjects(); 
    render(); 
} 

在你的代码中的一些事件与的setTimeout/setInterval的独立发生的,它变得非常复杂, “对齐” 事件。

我建议你使用两种组合技巧,第一种是将所有事件集中在一个循环中(主游戏循环),第二种是创建一组状态来控制游戏。

游戏状态:

// game states 
var STATE_TITLE_SCREEN = 0; 
var STATE_GAME_STARTING = 1; 
var STATE_RUNNING = 2; 
var STATE_PLAYER_DYING = 3; 
var STATE_GAME_OVER_SCREEN = 4; 

// current state 
var gameState = STATE_TITLE_SCREEN; 

// time of the current state 
var gameStateTime = 0; 

// change the state and reset the time of last state 
function setGameState(state) { 
    gameState = state; 
    gameStateTime = 0; 
} 

主回路:

var minIteractionTime = 10; 
var mainGameLoop = setInterval (function() { 

    switch (gameState) { 

     case STATE_TITLE_SCREEN: 
      // Title screen (on press space bar, change gameState to STATE_GAME_STARTING) 
     break; 

     case STATE_GAME_STARTING: 
      // Starting game, reset variables, countdown to start, etc... 
      score = 0.0; 

      // run the game after 5s 
      if (gameStateTime>5000) 
       setGameState(STATE_RUNNING); 

     break; 

     case STATE_RUNNING: 
      score += (0.1/100.0); // 0.1 points after 100 miliseconds 

      // CLEAR THE SCREEN HERE 

      drawScore(); 
      player.draw(); // draw the player ONLY here 
      laserWarning.draw(); // Draws the warnings AFTER draw the player 
      laserbeam.burn(player); // try to burn the player 
     break; 



     case STATE_PLAYER_DYING: 
      // player dying animation.. 

      // after 5s, change show the game over screen 
      if (gameStateTime>5000) 
       setGameState(STATE_GAME_OVER_SCREEN); 
     break; 

     case STATE_GAME_OVER_SCREEN: 
      // draw the game over screen here 

      // after 5s returns to the title screen 
      if (gameStateTime>5000) 
       setGameState(STATE_TITLE_SCREEN); 
     break; 
    } 

    // add the loop time in the gameStateTime variable 
    gameStateTime += minIteractionTime; 

}, minIteractionTime); 

LaserWarning类:

LaserWarning.prototype.draw = function() { 
    this.warningTime += minIteractionTime; 

    if (this.warningTime>100) { 
     // draw the warning only after firsts 100ms 
    } 

    if (this.warningTime>=750) { // warning time 
     // timer reset 
     this.warningTime = 0; 
    } 

} 

的激光光束类:

Laserbeam.prototype.burn = function (player) { 
    this.warmingUpTime += minIteractionTime; 

    if (this.warmingUpTime>=750) { // same time of warning 

     // draw the laser 

     // check if burns the player 
     if (player.checkWallCollision()) { 
      setGameState(STATE_PLAYER_DYING); 
     } 
    } 
} 

输入iteractions:

var gameKeys = { 
    37: "left", 
    38: "up", 
    39: "right", 
    40: "down", 
    32: "space" 
}; 

// for all iteractions, check the current state 
$("body").keyup(function (event) { 

    var lastKey = gameKeys[event.keyCode]; 

    switch (gameState) { 
     case STATE_RUNNING: 
      if (lastKey === "left") { 
       player.moveLeft(); 
      } else if (lastKey === "right") { 
       player.moveRight(); 
      } else if (lastKey === "up") { 
       player.moveUp(); 
      } else if (lastKey === "down") { 
       player.moveDown(); 
      }; 
     break; 

     case STATE_TITLE_SCREEN: 
      if (lastKey=="space") 
       setGameState(STATE_GAME_STARTING); 
     break; 
    } 
}); 

您应该只移动播放器时,并在新的位置不重绘调整定位变量,这将在player.draw发生();,在STATE_RUNNING中,在绘制警告之前。

如果用户在少于10ms(minIteractionTime)内按下右箭头两次,玩家将只会被绘制一次!如果您需要更加精确,只需减少minIteractionTime的值即可。

变量minIteractionTime控制帧率。

+0

我试过这样做。但是,它现在在进入GAME_RUNNING状态时会冻结。代码: http:// pastebin。com/tmCDA7jj – milesman34

+0

你好@ melesman34,我做了一些改变,现在我相信它工作正常。我留下了很好的评论,以便了解我所做的更改。 [链接](https://jsfiddle.net/ay7kp7yb/1/) – dreadnought