文章翻译自 Building the 2048 game in AngularJS, 整个译文带有自己的理解与想法,如果译得不好或有不对之处还请指点和见谅
第一步:规划程序
我们要做的第一步就是高层次(认真仔细有逻辑)的设计我们将要做的应用。我们如果这样做,无论应用程序有多大或者我们克隆另外一个应用,或者从头开始创建,都可以很轻松,容易。
先来看看这个游戏,我们可以看到有很多瓷砖在游戏板上面。每一个瓷砖本身作为一个位置被一个标有数字的瓷砖来放置。根据上面的情况,我们可以把放置瓷砖的职责交给css3,而不是依靠需要知道去哪放置瓷砖的javascript。当我们有一个瓷砖在面板上面的时候,我们基本可以确定瓷砖被摆放在一个合适的位置上面。
使用css3让我们有能力摆脱(解决)css的动画工作,同时,我们使用默认的AngularJS行为去跟踪面板和瓷砖的状态,游戏逻辑。
因为我们只有一个页面,我们仅仅只需要一个控制器去管理页面。
因为在APP运行期间,只有一个游戏面板,然后,我们将包含所有的网格逻辑在一个单一的实例GridService服务中。因为服务是一个单例对象,这样正好适合存储网格。我们会使用GridService服务去解决放置瓷砖,移动瓷砖,遍历网格找到瓷砖可能位置和管理网格的所有问题。
我们将使用另外一个叫做GameManager的服务来存储游戏逻辑和处理内部服务。GameManager服务将负责管理游戏的统计,解决移动,记录成绩(包括当前游戏数据和最高分数)。
最后,我们需要一个让我们控制键盘的组件。为了解决这个,我们将使用一个叫做KeyboardService的服务(我们只需要一个处理器在这个APP中)。在这篇文章中,我们将会在APP中完成对桌面的处理,但是我们可以重复使用这个相同的服务去解决移动设备的触摸事件。
构建app
为了构建我们的应用,我们创建了一个基本APP(我们使用叫做yeoman的angular生成器来生成APP的结构,但是这个并不是必要的我们。我们只把它作为一个起点,但很快偏离它的结构)。我们直接创建覆盖整个结构的APP。我们会把test/目录作为app /的同级目录。
接下来的介绍是用yeoman工具安装项目。如果你更喜欢手动安装,你可以跳过安装依赖,然后直接跳到下一节。
因为在我们的应用中使用了yeoman,我们一开始需要确认yeoman是否安装。Yeoman依赖于nodejs和npm是否安装了。安装nodejs超出了这篇教程的范围之外,但是有一个伟大的指导在NodeJS.org上面。
npm安装好了之后,我们可以安装yeoman工具yo和angular产生器(生成器将会被yo工具使用去创建我们的Angular app)。
当上面这些安装好了之后,我们可以按照下面的方法用yeoman工具创建我们的应用
安装的时候,工具将会问我们一些问题,我们对所有问题都选择是,仅仅除了选择angular-cookies作为依赖,因为我们不需要任何其他提供的依赖。
需要注意的是,使用Angular产生器,它会期望你已经安装了compass gem 和ruby环境。通过找打完整来源的方法来避免使用ruby和compass。
下面是手动方法通过bower来构建目录。(自己写的)
首先通过nodejs安装bower,也可以去bower上面自己查看。
然后安装所需要的依赖包
最终目录如下
我们的angular模块
我们将会在scripts目录创建app.js文件来控制我们的应用。让我们创建我们应用的开始代码
第二部:模块结构
这个angular app的推荐的布局结构是通过功能,而不是类型。那就意味着,我们将在功能的基础上定义我们的模块,而不是通过控制器,服务,指令等等来拆分我们的组件。比如,我么吧将定义一个Game模块和Keyboard模块在我们的应用中。
这种模块结构给了我们和文件结构匹配的完全分离的职责。这样做不仅能帮助我们构建更大更复杂的应用,同时也能帮助我们在我们的应用程序之前实现函数共享。
视图
最容易开始构建我们应用的地方就是视图。我们先看看视图,我们只有一个视图(模板)。我们不需要复杂的视图在我们应用中,所以我们将创建一个单独的div元素来包含我们的应用内容。
在我们app/index.html
文件中,我们需要包含所有我们的依赖(包括angular.js自己作为我们的js文件,暂时,这是简单的scripts/app.js),像这样
随意去制作一个更加复杂版本的多视图游戏,如果这也样做了,请在下面留言。我们将会很高兴看到你制作的东西。
随着我们的app/index.html
文件已经设置好,我们只需要为应用程序级视图详述出视图在我们的app/views/main.html
文件中。我们仅仅只需要修改index.html
文件,并且我们需要在应用程序中引入一个新的来源。
打开我们的app/views/main.html
文件,我们将放置我们所有的游戏专用视图。使用controller as
语法,我们将能够明确哪里我们期望找到数据在$scope
,并且明确哪个控制器负责处理哪个组件。
controller as
是在版本1.2中推出的相对较新的语法 。当解决有多个控制器在页面上同时它允许我们具体的了解哪里我们期待功能和数据可以被定义的问题时很有用。
在我们的视图中,最低限度,我们想去展现一些东西:
- 游戏的静态头部
- 当前游戏的分数和本地用户的最高分数
- 游戏面板
静态的头部可以这么简单:
注意,在视图中,当我们引用currentScore
和highScore
时,我们也要引用GameController
。controller as
语法允许我们准确应用我们感兴趣的控制器。
第三步,GameController
现在我们有了一个合理的项目结构,让我们创建GameController
来展现我们在视图中看到的值。在app/scripts/app.js
里面,我们可以创建在主要模块twentyfourtyeightApp
上的控制器:
在视图中,我们引用设置在GameController
上的一个game
对象。game
对象将会引用主要的游戏对象。我们将在一个新的模块创建这个游戏主要对象,我们将创建一个保存游戏的所有引用。
因为这个模块目前还没有被创建,应用程序不会在浏览器中加载。在控制器内,我们可以添加GameManager
依赖:
记住,我们正在创建一个模块化水平依赖与此不同的我们的应用程序的一部分,所以为了确认它在我们的app中是否加载,我们需要去将其列为我们angular模块的一个依赖。为了让Game
模块作为我们twentyfourtyeightApp
的依赖,我们将其放在我们模块定义的数组中。
我们全部的app/scripts/app.js
文件应该像这样:
游戏
现在我们的视图部分连接到视图,我们可以开始构建游戏本身背后的逻辑。为了去创建一个新的游戏模块,让我们在app/scripts/
目录中的app/scripts/game/game.js
创建我们的模块。
当创建模块,我们喜欢将它们写在以模块命名的自己的目录中。我们将在一个以模块命名的文件中,实现模块的初始化。例如,我们正在创建一个
game
模块,所以我们将在app/scripts/game
里面名为game.js
的文件中创建我们的游戏模块。这种方法在产品中提供了可以拓展的和条理分明。
Game
模块将会提供一个核心模块:GameManager
。
我们写GameManager
是为了负责保持这种状态的游戏,用户可以做的各种运动,追踪分数,同时确定什么时候游戏结束,以及用户是否打败游戏或者被游戏打败。