game_manager.js 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. function GameManager(size, InputManager, Actuator, StorageManager) {
  2. this.size = size; // Size of the grid
  3. this.inputManager = new InputManager;
  4. this.storageManager = new StorageManager;
  5. this.actuator = new Actuator;
  6. this.startTiles = 2;
  7. this.inputManager.on("move", this.move.bind(this));
  8. this.inputManager.on("restart", this.restart.bind(this));
  9. this.inputManager.on("keepPlaying", this.keepPlaying.bind(this));
  10. this.setup();
  11. }
  12. // Restart the game重新开始游戏
  13. GameManager.prototype.restart = function () {
  14. this.storageManager.clearGameState();
  15. this.actuator.continueGame(); // Clear the game won/lost message
  16. this.setup();
  17. };
  18. // document.onkeydown = function(event) {
  19. // var e = event || window.event || arguments.callee.caller.arguments[0];
  20. // if (e && e.keyCode == 27) { // 按 Esc
  21. //
  22. // //要做的事情
  23. // }
  24. //
  25. // if (e && e.keyCode == 13) { // enter 键
  26. // //要做的事情
  27. // }
  28. // };
  29. // Keep playing after winning (allows going over 2048)
  30. // 获胜后继续
  31. GameManager.prototype.keepPlaying = function () {
  32. this.keepPlaying = true;
  33. this.actuator.continueGame(); // Clear the game won/lost message清除游戏中获胜/丢失的消息
  34. };
  35. // Return true if the game is lost, or has won and the user hasn't kept playing
  36. // 如果游戏输了,或者已经赢了,并且用户没有继续玩下去,请返回true
  37. GameManager.prototype.isGameTerminated = function () {
  38. return this.over || (this.won && !this.keepPlaying);
  39. };
  40. // Set up the game
  41. // 设置游戏
  42. GameManager.prototype.setup = function () {
  43. var previousState = this.storageManager.getGameState();
  44. // Reload the game from a previous game if present
  45. // 如果存在,则从以前的游戏中重新加载游戏。
  46. if (previousState) {
  47. this.grid = new Grid(previousState.grid.size,
  48. previousState.grid.cells); // Reload grid
  49. // 再装填栅格
  50. this.score = previousState.score;
  51. this.over = previousState.over;
  52. this.won = previousState.won;
  53. this.keepPlaying = previousState.keepPlaying;
  54. } else {
  55. this.grid = new Grid(this.size);
  56. this.score = 0;
  57. this.over = false;
  58. this.won = false;
  59. this.keepPlaying = false;
  60. // Add the initial tiles
  61. // 添加初始化
  62. this.addStartTiles();
  63. }
  64. // Update the actuator
  65. // 更新执行器
  66. this.actuate();
  67. };
  68. // Set up the initial tiles to start the game with
  69. // 设置参数以开始游戏
  70. GameManager.prototype.addStartTiles = function () {
  71. for (var i = 0; i < this.startTiles; i++) {
  72. this.addRandomTile();
  73. }
  74. };
  75. // Adds a tile in a random position
  76. // 将目标添加到任意位置
  77. GameManager.prototype.addRandomTile = function () {
  78. if (this.grid.cellsAvailable()) {
  79. var value = Math.random() < 0.9 ? 2 : 4;
  80. var tile = new Tile(this.grid.randomAvailableCell(), value);
  81. this.grid.insertTile(tile);
  82. }
  83. };
  84. // Sends the updated grid to the actuator
  85. // 将更新的网格发送给执行器。
  86. GameManager.prototype.actuate = function () {
  87. if (this.storageManager.getBestScore() < this.score) {
  88. this.storageManager.setBestScore(this.score);
  89. }
  90. // Clear the state when the game is over (game over only, not win)
  91. // 游戏结束后清除状态
  92. if (this.over) {
  93. this.storageManager.clearGameState();
  94. } else {
  95. this.storageManager.setGameState(this.serialize());
  96. }
  97. this.actuator.actuate(this.grid, {
  98. score: this.score,
  99. over: this.over,
  100. won: this.won,
  101. bestScore: this.storageManager.getBestScore(),
  102. terminated: this.isGameTerminated()
  103. });
  104. };
  105. // Represent the current game as an object
  106. // 将当前游戏表示为对象
  107. GameManager.prototype.serialize = function () {
  108. return {
  109. grid: this.grid.serialize(),
  110. score: this.score,
  111. over: this.over,
  112. won: this.won,
  113. keepPlaying: this.keepPlaying
  114. };
  115. };
  116. // Save all tile positions and remove merger info
  117. // 保存所有tile位置并删除合并信息
  118. GameManager.prototype.prepareTiles = function () {
  119. this.grid.eachCell(function (x, y, tile) {
  120. if (tile) {
  121. tile.mergedFrom = null;
  122. tile.savePosition();
  123. }
  124. });
  125. };
  126. // Move a tile and its representation
  127. // 移动tile及其表示
  128. GameManager.prototype.moveTile = function (tile, cell) {
  129. this.grid.cells[tile.x][tile.y] = null;
  130. this.grid.cells[cell.x][cell.y] = tile;
  131. tile.updatePosition(cell);
  132. };
  133. // Move tiles on the grid in the specified direction
  134. // 按指定的方向移动网格上的元素
  135. GameManager.prototype.move = function (direction) {
  136. // 0: up, 1: right, 2: down, 3: left
  137. var self = this;
  138. if (this.isGameTerminated()) return; // Don't do anything if the game's over
  139. // 如果游戏结束了,什么都不要做
  140. var cell, tile;
  141. var vector = this.getVector(direction);
  142. var traversals = this.buildTraversals(vector);
  143. var moved = false;
  144. // Save the current tile positions and remove merger information 保存当前的元素位置并删除合并信息。
  145. this.prepareTiles();
  146. // Traverse the grid in the right direction and move tiles
  147. // 沿着正确的方向遍历网格并移动元素
  148. traversals.x.forEach(function (x) {
  149. traversals.y.forEach(function (y) {
  150. cell = { x: x, y: y };
  151. tile = self.grid.cellContent(cell);
  152. if (tile) {
  153. var positions = self.findFarthestPosition(cell, vector);
  154. var next = self.grid.cellContent(positions.next);
  155. // Only one merger per row traversal?每行遍历只有一次合并?
  156. if (next && next.value === tile.value && !next.mergedFrom) {
  157. var merged = new Tile(positions.next, tile.value * 2);
  158. merged.mergedFrom = [tile, next];
  159. self.grid.insertTile(merged);
  160. self.grid.removeTile(tile);
  161. // Converge the two tiles' positions
  162. // 收敛两个元素的位置
  163. tile.updatePosition(positions.next);
  164. // Update the score
  165. // 会聚两个元素的位置-更新分数
  166. self.score += merged.value;
  167. // The mighty 2048 tile
  168. if (merged.value === 2048) self.won = true;
  169. } else {
  170. self.moveTile(tile, positions.farthest);
  171. }
  172. if (!self.positionsEqual(cell, tile)) {
  173. moved = true; // The tile moved from its original cell!
  174. // 元素从原来的网格里移动了!
  175. }
  176. }
  177. });
  178. });
  179. if (moved) {
  180. this.addRandomTile();
  181. if (!this.movesAvailable()) {
  182. this.over = true; // 你输啦!
  183. }
  184. this.actuate();
  185. }
  186. };
  187. // Get the vector representing the chosen direction
  188. // 获取表示所选方向的向量
  189. GameManager.prototype.getVector = function (direction) {
  190. // Vectors representing tile movement
  191. // 表示平铺运动的矢量
  192. var map = {
  193. 0: { x: 0, y: -1 }, // Up
  194. 1: { x: 1, y: 0 }, // Right
  195. 2: { x: 0, y: 1 }, // Down
  196. 3: { x: -1, y: 0 } // Left
  197. };
  198. return map[direction];
  199. };
  200. // Build a list of positions to traverse in the right order
  201. // 按正确的顺序建立要遍历的位置列表。
  202. GameManager.prototype.buildTraversals = function (vector) {
  203. var traversals = { x: [], y: [] };
  204. for (var pos = 0; pos < this.size; pos++) {
  205. traversals.x.push(pos);
  206. traversals.y.push(pos);
  207. }
  208. // Always traverse from the farthest cell in the chosen direction 总是沿着选择的方向从最远的网格中走过。
  209. if (vector.x === 1) traversals.x = traversals.x.reverse();
  210. if (vector.y === 1) traversals.y = traversals.y.reverse();
  211. return traversals;
  212. };
  213. GameManager.prototype.findFarthestPosition = function (cell, vector) {
  214. var previous;
  215. // Progress towards the vector direction until an obstacle is found 向矢量方向前进,直到发现障碍
  216. do {
  217. previous = cell;
  218. cell = { x: previous.x + vector.x, y: previous.y + vector.y };
  219. } while (this.grid.withinBounds(cell) &&
  220. this.grid.cellAvailable(cell));
  221. return {
  222. farthest: previous,
  223. next: cell // Used to check if a merge is required用于检查是否需要合并。
  224. };
  225. };
  226. GameManager.prototype.movesAvailable = function () {
  227. return this.grid.cellsAvailable() || this.tileMatchesAvailable();
  228. };
  229. // Check for available matches between tiles (more expensive check)检查平铺之间的可用匹配
  230. GameManager.prototype.tileMatchesAvailable = function () {
  231. var self = this;
  232. var tile;
  233. for (var x = 0; x < this.size; x++) {
  234. for (var y = 0; y < this.size; y++) {
  235. tile = this.grid.cellContent({ x: x, y: y });
  236. if (tile) {
  237. for (var direction = 0; direction < 4; direction++) {
  238. var vector = self.getVector(direction);
  239. var cell = { x: x + vector.x, y: y + vector.y };
  240. var other = self.grid.cellContent(cell);
  241. if (other && other.value === tile.value) {
  242. return true; // These two tiles can be merged这两个元素可以合并。
  243. }
  244. }
  245. }
  246. }
  247. }
  248. return false;
  249. };
  250. GameManager.prototype.positionsEqual = function (first, second) {
  251. return first.x === second.x && first.y === second.y;
  252. };