ng2048源码阅读

ng2048源码阅读

Tutorial: http://www.ng-newsletter.com/posts/building-2048-in-angularjs.html
Github: https://github.com/fullstackio/ng2048.git

本地不部署项目一直卡在node-sass不成功的问题上,需要的css文件没办法生成,我的目的是弄清设计原理,学习ng的用法,所以直接用sass编译sass文件为css文件,丢到项目文件夹下跑起来。

sass编译css文件

npm install node-sass -g
sass D:\GitHub\ng2048\app\styles\main.scss D:\GitHub\ng2048\app\styles\main.css

启动项目

这个项目是grunt构建的, 使用--force忽略sass编译的错误继续执行

grunt serve --force

这个列子有些小复杂,看看能学到啥

1. 数据传递,指令的使用,ng-if的使用

grid的使用,通过ng-model传递到指令内部的独立scope

<!-- 显示格子 -->
<div grid ng-model='ctrl.game' class="row"></div>

Grid中包含tile,tile指令也是通过ng-model传递到指令内部的独立scope,注意这里的ng-repeat迭代的对象的传递

<div id="game-{{ ngModel.gameSize }}">
  <div class="grid-container">
    <div class="grid-cell" ng-repeat="cell in ngModel.grid track by $index"></div>
  </div>

  <div class="tile-container">
    <div tile 
          ng-model='tile'
          ng-repeat='tile in ngModel.tiles track by $id(tile.id || $index)'></div>
  </div>
</div>

tile的模板

<div ng-if='ngModel' class="tile position-{{ ngModel.x }}-{{ ngModel.y }} tile-{{ ngModel.value }}" 
  ng-class="{ 'tile-merged': ngModel.merged}">
  <div class="tile-inner">
    {{ ngModel.value }}
  </div>
</div>

2. 通用的游戏键盘控制服务

服务init的时候这个keydown什么也不做,通过on订阅事件,将自己的处理程序加到服务的keyEventHandlers列表中完成事件的定制。这个服务就通用的了,你只需要写好自己的控制程序,然后通过on订阅就可以了。这也算是一种设计技巧了。

'use strict';

angular.module('Keyboard', [])
.service('KeyboardService', function($document) {

  var UP    = 'up',
      RIGHT = 'right',
      DOWN  = 'down',
      LEFT  = 'left';

  var keyboardMap = {
    37: LEFT,
    38: UP,
    39: RIGHT,
    40: DOWN
  };

  this.init = function() {
    var self = this;
    this.keyEventHandlers = [];
    $document.bind('keydown', function(evt) {
      var key = keyboardMap[evt.which];

      if (key) {
        // An interesting key was pressed
        evt.preventDefault();
        self._handleKeyEvent(key, evt);
      }
    });
  };

  this.on = function(cb) {
    this.keyEventHandlers.push(cb);
  };

  this._handleKeyEvent = function(key, evt) {
    var callbacks = this.keyEventHandlers;
    if (!callbacks) {
      return;
    }

    evt.preventDefault();

    if (callbacks) {
      for (var x = 0; x < callbacks.length; x++) {
        var cb = callbacks[x];
        cb(key, evt);
      }
    }
  };

});
//初始化
KeyboardService.init();
//订阅事件
KeyboardService.on(function(key) {
    self.game.move(key);
});

3. 设计思路和实现思路

代码看完了,游戏的思路和我设想的差不多,但是在实现上跟我想的有些不一样,大致也就是两层循环挨个的处理格子,但是实现上感觉还是有些技巧的。

3.1 根据方向确定坐标的偏移量
    var vectors = {
      'left': { x: -1, y: 0 },
      'right': { x: 1, y: 0 },
      'up': { x: 0, y: -1 },
      'down': { x: 0, y: 1 }
    };
3.2 根据方向确定两层循环的索引序列,[0,1,2,3]还是[3,2,1,0]
    this.traversalDirections = function(key) {
      var vector = vectors[key];
      var positions = {x: [], y: []};
      for (var x = 0; x < this.size; x++) {
        positions.x.push(x);
        positions.y.push(x);
      }

      if (vector.x > 0) {
        positions.x = positions.x.reverse();
      }
      if (vector.y > 0) {
        positions.y = positions.y.reverse();
      }

      return positions;
    };
3.3 一次性拿到可以直接移动到的位置和紧跟的进行碰撞的位置
    this.calculateNextPosition = function(cell, key) {
      var vector = vectors[key];
      var previous;

      do {
        previous = cell;
        cell = {
          x: previous.x + vector.x,
          y: previous.y + vector.y
        };
      } while (this.withinGrid(cell) && this.cellAvailable(cell));
      //没有越界,并且cell是空的

      return {
        newPosition: previous,
        next: this.getCellAt(cell)
      };
    };
posted @ 2016-11-12 17:53  CooMark  阅读(354)  评论(0编辑  收藏  举报