1 //为实现各种现代浏览器的requestAnimationFrame()方法,创建一段简单的跨浏览器保障代码(polyfill),以实现流畅、高效的动画。由保罗•艾里什(Paul Irish)编写,网址为 http://bit.ly/req_anim_frame。 2 window.requestAnimationFrame = (function(){ 3 return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function (callback){ 4 window.setTimeout(callback, 1000 / 60); 5 }; 6 })(); 7 8 // Define the game logic module which keeps track of the game state, the players's score, 9 // the number of lives remaining, handles collisions between the player's character and 10 // other obstacles and ensures the game graphics are drawn onto the <canvas> at the 11 // right moment. This module contains the brains behind the game play and instructs other 12 // code modules to do the heavy lifting through the use of the observer design pattern. 13 //定义游戏的逻辑模块。此模块保持追踪以下内容,包括有游戏状态、玩家的分数、剩余的生命条数。此模块还处理玩家角色和其他物体碰撞。此模块还确保游戏图形可以在正确的时刻绘制到<canvas>上。 14 (function(Frogger) { 15 16 // Define a variable to hold the current player's score 17 //定一个变量来记录玩家当前的分数。 18 var _score = 0, 19 20 // Define and initialize a variable to hold the high score achieved in the game 21 //定义并初始化一个变量来记录游戏中所获得的最高分。 22 _highScore = 1000, 23 24 // Define the number of lives the player has remaining before the game is over 25 //定义游戏结束前玩家还剩余的生命条数。 26 _lives = 5, 27 28 // Define the number of milliseconds the player has to get their character to 29 // the goal (60 seconds). If they take too long, they will lose a life 30 //定义玩家要将他们的角色送达目标位置的毫秒数(60秒)。如果玩家耗用时间过长,则他们将失去一条生命。 31 _timeTotal = 60000, 32 33 // Define a variable to store the current time remaining for the player to reach 34 // the goal 35 //定义一个变量来保存玩家要到达目标位置的当前剩余时间。 36 _timeRemaining = _timeTotal, 37 38 // Define the refresh rate of the graphics on the <canvas> element (one draw every 39 // 33⅓ milliseconds = 30 frames per second). Attempting to redraw too frequently 40 // can cause the browser to slow down so choose this value carefully to maintain a 41 // good balance between fluid animation and smooth playability 42 //定义<canvas>元素上的图形的刷新时间(每33⅓毫秒绘制一次 = 每秒30帧)。频率太快的重新绘制会引起浏览器响应缓慢。因此,选择使用这个值可以在流畅的动画与利索的可玩性之间取得平衡。 43 _refreshRate = 33.333, 44 45 // Define a variable to store the number of times the player's character has 46 // reached the goal 47 //定义一个变量来保存玩家的角色已经到达目标位置的次数。 48 _timesAtGoal = 0, 49 50 // Define a variable to indicate the number of times the player's character needs 51 // to reach the goal for the game to be won 52 //定义一个变量来记录玩家的角色为取得胜利还须到达目标位置的次数。 53 _maxTimesAtGoal = 5, 54 55 // Define a Boolean variable to indicate whether the player's movement is currently 56 // frozen in place 57 //定义一个布尔变量来记录玩家当前的移动状态是否被冻结在原地。 58 _isPlayerFrozen = false, 59 60 // Define a variable to store the last time the game loop ran - this helps keep 61 // the animation running smoothly at the defined refresh rate 62 //定义一个变量来保存上一次游戏主循环运行的时间——这有助于使动画以所定义的刷新频率下流通运行。 63 _lastTimeGameLoopRan = (new Date()).getTime(); 64 65 // Define a function to be called to count down the time remaining for the player to 66 // reach the goal without forfeiting a life 67 //定义一个函数进行调用,用来对玩家到达目标而不至于失去一条生命的剩余时间进行倒计时。 68 function countDown() { 69 if (_timeRemaining > 0) { 70 71 // This function will be called as frequently as the _refreshRate variable 72 // dictates so we reduce the number of milliseconds remaining by the 73 // _refreshRate value for accurate timing 74 //此函数将会根据_refreshRate变量值的频率进行调用。这样,我们就可以用剩余的毫秒数减去_refreshRate变量的值来实现精确的倒计时。 75 _timeRemaining -= _refreshRate; 76 77 // Publish the fact that the remaining time has changed, passing along the 78 // new time remaining as a percentage - which will help when we come to display 79 // the remaining time on the game board itself 80 //发出通知,剩余时间已经发生改变,把新的剩余时间以百分比形式传入——有助于我们在游戏面板上显式剩余时间。 81 Frogger.observer.publish("time-remaining-change", _timeRemaining / _timeTotal); 82 } else { 83 84 // If the remaining time reaches zero, we take one of the player's remaining 85 // lives 86 //如果剩余时间到达零,则将玩家的剩余生命条数减一。 87 loseLife(); 88 } 89 } 90 91 // Define a function to be called when all the player's lives have gone and the game 92 // is declared over 93 //定义一个函数,当玩家所有的生命已经失去时调用此函数,并宣布游戏结束。 94 function gameOver() { 95 96 // Pause the player's movements as they are no longer in the game 97 //暂停玩家的移动,因为他们已经不能再继续进行游戏了。 98 freezePlayer(); 99 100 // Inform other code modules in this application that the game is over 101 //通知这个应用程序中的其他代码模块,游戏已经结束。 102 Frogger.observer.publish("game-over"); 103 } 104 105 // Define a function to be called when the player has reached the goal 106 //定义一个函数,当玩家到达最终目标时调用此函数。 107 function gameWon() { 108 109 // Inform other code modules that the game has been won 110 //通知其他代码模块,游戏取得胜利。 111 Frogger.observer.publish("game-won"); 112 } 113 114 // Define a function to be called when the player loses a life 115 //定义一个函数,当玩家失去一条生命时调用此函数。 116 function loseLife() { 117 118 // Decrease the number of lives the player has remaining 119 //玩家的剩余生命条数减一。 120 _lives--; 121 122 // Pause the player's movements 123 //暂停玩家的移动。 124 freezePlayer(); 125 126 // Inform other code modules that the player has lost a life 127 //通知其他代码模块,玩家已经失去一条生命。 128 Frogger.observer.publish("player-lost-life"); 129 130 if (_lives === 0) { 131 132 // Declare the game to be over if the player has no lives remaining 133 //如果玩家没有剩余生命,则宣布游戏结束。 134 gameOver(); 135 } else { 136 137 // If there are lives remaining, wait 2000 milliseconds (2 seconds) before 138 // resetting the player's character and other obstacles to their initial 139 // positions on the game board 140 //如果还有剩余生命,则等待2000毫秒(2秒),再把玩家角色和其他物体重新设置至他们各自在游戏面板中的初始位置。 141 setTimeout(reset, 2000); 142 } 143 } 144 145 // Define a function to be called when the player's character is required to be frozen 146 // in place, such as when the game is over or when the player has lost a life 147 //定义一个函数,当玩家角色需要被冻结在原地时进行调用。如游戏结束时,或当玩家失去一条生命时。 148 function freezePlayer() { 149 150 // Set the local variable to indicate the frozen state 151 //将局部变量_isPlayerFrozen设为true来表示冻结状态。 152 _isPlayerFrozen = true; 153 154 // Inform other code modules - including that which controls the player's 155 // character - that the player is now be frozen 156 //通知其他代码模块——包括控制玩家角色的模块——玩家现在被冻结了。 157 Frogger.observer.publish("player-freeze"); 158 } 159 160 // Define a function to be called when the player's character is free to move after 161 // being previously frozen in place 162 //定义一个函数,当玩家的角色从先前的冻结在原地状态恢复至自由移动状态时,调用此函数。 163 function unfreezePlayer() { 164 165 // Set the local variable to indicate the new state 166 //将局部变量_isPlayerFrozen设为false来表示新状态。 167 _isPlayerFrozen = false; 168 169 // Inform other code modules that the player's character is now free to move around 170 // the game board 171 //通知其他代码模块,玩家的角色现在可以在游戏面板上自由移动。 172 Frogger.observer.publish("player-unfreeze"); 173 } 174 175 // Define a function to increase the player's score by a specific amount and update 176 // the high score accordingly 177 //定义一个函数来以特定的分值增加玩家的分数,并相应地更新最高分数记录。 178 function increaseScore(increaseBy) { 179 180 // Increase the score by the supplied amount (or by 0 if no value is provided) 181 //用参数所提供的分值加增加分数(如果没有分值提供,则以0作为增加分值)。 182 _score += increaseBy || 0; 183 184 // Inform other code modules that the player's score has changed, passing along 185 // the new score 186 //通知其他代码模块,玩家的分数已经发生改变,把新的分数传入。 187 Frogger.observer.publish("score-change", _score); 188 189 // If the player's new score beats the current high score then update the high 190 // score to reflect the player's new score and inform other code modules of a 191 // change to the high score, passing along the new high score value 192 //如果玩家的新分数高于当前的最高分数记录,则更新最高分数记录的值来对应出玩家的新分数,并通知其他代码模块最高分记录发生了改变,把新的最高分值传入。 193 if (_score > _highScore) { 194 _highScore = _score; 195 Frogger.observer.publish("high-score-change", _highScore); 196 } 197 } 198 199 // Define a function to execute once the player reaches the designated goal 200 //定义一个函数,一旦玩家到达指定的目标位置后,执行此函数。 201 function playerAtGoal() { 202 203 // When the player reaches the goal, increase their score by 1000 points 204 //当玩家到达目标位置,将玩家的分数增加1000分。 205 increaseScore(1000); 206 207 // Increment the value indicating the total number of times the player's character 208 // has reached the goal 209 //增加表示玩家的角色已经到达目标位置总次数的值。 210 _timesAtGoal++; 211 212 // Freeze the player's character movement temporarily to acknowledge they have 213 // reached the goal 214 //临时冻结玩家角色的移动来表示确认玩家已经到达目标位置。 215 freezePlayer(); 216 217 if (_timesAtGoal < _maxTimesAtGoal) { 218 219 // The player must enter the goal a total of 5 times, as indicated by the 220 // _maxTimesAtGoal value. If the player has not reached the goal this many 221 // times yet, then reset the player's character position and obstacles on the 222 // game board after a delay of 2000 milliseconds (2 seconds) 223 //玩家必须进入目标位置5次,如_maxTimesAtGoal的值所示。如果玩家还没有完成这个数量,则等待2000毫秒(2秒),在游戏面板上重新设置玩家角色位置以及各个其他物体。 224 setTimeout(reset, 2000); 225 } else { 226 227 // If the player has reached the goal 5 times, the game has been won! 228 //如果玩家已经到达目标位置5次,则游戏胜利。 229 gameWon(); 230 } 231 } 232 233 // Define a function to execute when the player moves their character on the game 234 // board, increasing their score by 20 points when they do 235 //定义一个函数,当玩家把角色在游戏面板上移动时,执行此函数。当玩家移动角色时,分数增加20分。 236 function playerMoved() { 237 increaseScore(20); 238 } 239 240 // Define a function to be called when the game board needs to be reset, such as when 241 // the player loses a life 242 //定义一个函数,当游戏面板需要重新设置时进行调用。如当玩家失去一条生命时。 243 function reset() { 244 245 // Reset the variable storing the current time remaining to its initial value 246 //把保存当前剩余时间的变量重设为它的初始值。 247 _timeRemaining = _timeTotal; 248 249 // Release the player's character if it has been frozen in place 250 //如果玩家的角色已经被冻结在原地,则解除冻结。 251 unfreezePlayer(); 252 253 // Inform other code modules to reset themselves to their initial conditions 254 //通知其他代码模块,使它们本身重设至各自的初始状态。 255 Frogger.observer.publish("reset"); 256 } 257 258 // The game loop executes on an interval at a rate dictated by value of the 259 // _refreshRate variable (once every 50 milliseconds), in which the game board is 260 // redrawn with the character and obstacles drawn at their relevant positions on 261 // the board and any collisions between the player's character and any obstacles 262 // are detected 263 //游戏主循环以特定的频率按某时间间隔执行,该频率由变量_refreshRate的值指定(每50毫秒一次【注:这里应是每33.333毫秒一次】), 264 function gameLoop() { 265 266 // Calculate how many milliseconds have passed since the last time the game loop 267 // was called 268 //计算自从上一次游戏主循环调用以来,所过去的毫秒数。 269 var currentTime = (new Date()).getTime(), 270 timeDifference = currentTime - _lastTimeGameLoopRan; 271 272 // Execute this function again when the next animation frame is ready for use by 273 // the browser - keeps the game loop looping 274 //当下一动画帧准备好可供浏览器使用时执行此函数——保持游戏主循环持续进行。 275 window.requestAnimationFrame(gameLoop); 276 277 // If the number of milliseconds passed exceeds the defined refresh rate, draw 278 // the obstacles in the updated position on the game board and check for collisions 279 //如果已经过去的毫秒时间超过了所定义的刷新频率时间,则在游戏面板上将各物体绘制在更新后的位置,并检测是否存在碰撞。 280 if (timeDifference >= _refreshRate) { 281 282 // Clear the <canvas> element's drawing surface - erases everything on the 283 // game board so we can redraw the player's character and obstacles in their 284 // new positions 285 //清空<canvas>元素的绘制表面——擦除在游戏上的所有内容以便我们能将玩家的角色和各物体绘制在各自的新位置上。 286 Frogger.drawingSurface.clearRect(0, 0, Frogger.drawingSurfaceWidth, Frogger.drawingSurfaceHeight); 287 288 if (!_isPlayerFrozen) { 289 290 // As long as the player's character is not frozen in place, ensure the 291 // timer is counting down, putting pressure on the player to reach the 292 // goal in time 293 //只要玩家的角色没有被冻结在原地,就要确保进行倒计时,给玩家施加压力使其能在限时内到达目标地点。 294 countDown(); 295 296 // Inform other code modules to check the player has not collided with an 297 // obstacle on the game board 298 //通知其他代码模块来检查玩家是否撞上游戏面板上的某个物体。 299 Frogger.observer.publish("check-collisions"); 300 } 301 302 // Now on our empty canvas we draw our game board and the obstacles upon it in 303 // their respective positions 304 //现在,我们在空白的canvas上绘制游戏面板,以及把各个物体绘制到各自位置上。 305 Frogger.observer.publish("render-base-layer"); 306 307 // After the game board and obstacles, we draw the player's character so that 308 // it is always on top of anything else on the <canvas> drawing surface 309 //绘制完游戏面板和各个物体后,我们才绘制玩家的角色。这样,在<canvas>的绘制平面中,玩家角色就实现了置顶的效果。 310 Frogger.observer.publish("render-character"); 311 312 // Store the current time for later comparisons to keep the frame rate smooth 313 //保存当前时间用于稍后进行对比,以保持帧频流畅。 314 _lastTimeGameLoopRan = currentTime; 315 } 316 } 317 318 // Define a function to kick-start the application and run the game loop, which renders 319 // each frame of the game graphics and checks for collisions between the player's 320 // character and any obstacles on the game board 321 //定义一个函数来启动应用程序并运行游戏主循环。游戏主循环会渲染每一帧的游戏图像,并检测玩家角色与游戏面板中的其他物体之间的碰撞。 322 function start() { 323 324 // Inform other code modules of the initial state of the game's high score 325 //把游戏最高分数的初始值通知其他代码模块。 326 Frogger.observer.publish("high-score-change", _highScore); 327 328 // Start the game loop running 329 //使游戏主循环开始运行。 330 gameLoop(); 331 } 332 333 // Execute the start() function to kick off the game loop once the "game-load" event 334 // is fired. We'll trigger this event after we've configured the rest of our code 335 // modules for the game 336 //一旦“game-load”事件发生,执行start()函数来启动游戏主循环。在我们为游戏完成其余的代码模块配置后,我们将触发该事件。 337 Frogger.observer.subscribe("game-load", start); 338 339 // Execute the playerAtGoal() function when another code module informs us that the 340 // player has reached the goal 341 //当其他代码模块通知我们玩家已经到达目标位置后,执行playerAtGoal()函数。 342 Frogger.observer.subscribe("player-at-goal", playerAtGoal); 343 344 // Execute the playerMoved() function when we have been informed that the player has 345 // moved their character 346 //当我们接到通知说玩家已经移动他们的角色时,执行playerMoved()函数。 347 Frogger.observer.subscribe("player-moved", playerMoved); 348 349 // Execute the loseLife() function when we are informed by another code base that the 350 // player's character has collided with an obstacle on the game board 351 //当我们被其他代码通知说玩家的角色和游戏面板中的某个物体发生碰撞时,执行loseLife()函数。 352 Frogger.observer.subscribe("collision", loseLife); 353 354 // Pass the global Frogger variable into the module so it can be accessed locally, 355 // improving performance and making its dependency clear 356 //把全局Frogger变量传入模块中,这样它就可以局部变量方式被访问,有助于提高性能。 357 }(Frogger));