我频繁地被问及到的一个问题之一,就是什么时候使用Angular框架是一个糟糕的选择。我的默认答复是编写游戏的时候,尽管Angular有它自己的 事件循环处理 ($digest循环) ,并且游戏通常需要很多底层DOM操作.如果说有Angular能支持很多类型的游戏,那这个说法可不准确。即使游戏需要大量的DOM操作,这可能会用到 angular框架处理静态部分,如记录最高分和游戏菜单。 如果你和我一样迷上流行的2048 游戏. 游戏的目标是用相同的值相加拼出值为2048的方块。 在今天这篇博文中,我们会用AngularJS从头到尾地创建一个副本, 并解释创建app的全过程。由于这个app相对复杂,所以我也打算用这篇文章来描述如何创建复杂的AngularJS应用。 这是我们要创建的 demo . 现在开始吧!
第一步:规划app第一步我们要做的,就是给要创建的app做高层设计。无论是山寨别人的app,还是自己从零做起,这一步都与app的规模无关。 再来看看这个游戏,我们发现在游戏板的顶端有一堆瓦片。每个瓦片自身都可以作为一个位置,用来放置其他有编号的瓦片。我们可以根据这个事实,把任务移动瓦片的任务交给CSS3来处理,而不是依靠JavaScript,它需要知道移动瓦片的位置。当游戏面板上有一个瓦片,我们只需要简单地确保它放在顶部合适的位置即可。 使用CSS3来布局,能带给我们CSS动画效果,同时也默认使用AngularJS行为来跟踪游戏板的状态,瓦片和游戏逻辑。 因为我们只有一个单页面(single page),我们还需要一个单控制器(single controller)来管理页面。
因为应用的生命周期内只有一个游戏板,我们在GridService服务中的一个单一实例里包含所有的网格逻辑。由于服务是单例模式对象,所以这是一个存储网格的恰当位置。我们使用GridService来处理瓦片替换,移动,管理网格。 而把游戏的逻辑和处理放到一个叫做GameManager的服务中。它将负责游戏的状态,处理移动,维护分数(当前分数和最高分) 最后,我们需要一个允许我们管理键盘的组件。我们需要一个叫做KeyboardService的服务。在这篇博文中,实现了应用对桌面的处理,我们也可以复用这个服务来管理触摸操作让它在移动设备上运转。 创建app为了创建app,我们先创建一个基本的 app (使用 yeoman angular 生成器生成app的结构, 这一步不是必须的. 我们只把它作为切入点,之后就迅速地从它的结构上分开。).创建一个app目录用来放置整个应用。把test/目录作为app/目录的同级目录.
因为在应用中我们用了yeomanin工具, 我们首先要确保它已经安装好了. Yeoman安装时基于NodeJS和npm.安装NodeJS不是这篇教程所要讲的,但你可以参看NodeJS.org 站点. 装完npm后,我们就可以安装yeoman工具yo和angular生成器(它由yo调用来创建Angular app):
安装后,我们可以使用yeoman工具生成我们的应用,如下:
该工具会询问一些请求。我们都选yes即可,除了要选择angular-cookies作为依赖外,我们不需要任何其他的依赖了。
我们的angular 模块我们将创建scripts/app.js文件来放置我们的应用。现在就开始创建应用吧:
模块结构布局angular应用使用的结构现在是根据函数推荐的,而不是类型。这就是说,不用把组件分成控制器,服务,指令等,就可以在函数基础上定义我们的模块结构。例如,在应用中定义一个Game模块和一个Keyboard模块。 模块结构清晰地为我们分离出匹配文件结构的职能域。这不仅方便我们创建大型,灵活性强的angular应用,也方便我们共享app中的函数。 最后,我们搭建测试环境适应文件目录结构。 视图应用中最易切入的地方非视图莫属了。审视视图自身,我们发现只有一个view/template.在这个应用中,不需要多视图,所以我们创建单一的<div>元素,用来放置应用的内容。 在我们的主文件app/index.html中,我们需要包含所有的依赖项(包括angular.js自身和JS文件,即scripts/app.js),如下:
有了app/index.html文件集,我们需要在应用视图层面上,详细地处理app/views/main.html中的视图。当需要在应用中导入一个新 打开app/views/main.html,我们要替换所有的游戏指定的视图。使用controllerAs语法,我们可以在$scope中清楚地知道我们期待在哪里查询数据,哪个控制器负责哪个组件。
在视图中,我们要显示以下一些项目:
游戏静态头可以这样来完成:
注意到,当在视图中引用currentScore和highScroe时,我们也引用了GameController.controllerAs语法使得我们可以显示地引用我们感兴趣的控制器。 GameController现在我们有了一个合理的项目结构,现在来创建GameController来放置我们要在视图中显示的值。在app/script/app.js中,我们可以在主模块twentyfourtyeight.App中创建控制器:
在视图中,我们引用了一个game对象,它将在GameController中设置。该game对象将引用主game对象。我们在一个新模块中创建这个主游戏模块,用来放置游戏中所有的引用。
因为这个模块还没有创建,app不会再浏览器中加载它。在控制器中,我们可以添加GameManager依赖
别忘了,我们正创建一个模块级别的依赖,它是应用中不同的部分,所以要确保它在应用中正确地加载,我们需要将它列为angular模块的一个依赖。为使Game模块成为twentyfourtyeightApp的依赖,我们在定义该模块的数组中列举它。 我们整个的app/script/app.js文件应该看起来像这样:
Game既然我们有视图间部分相互连接了,那么就可以开始编写游戏背后的逻辑了。为创建一个新游戏模块,我们在app/scripts/目录中把我们的模块创建为app/scripts/game/game.js:
Game模块将提供一个单核心组件:GameManager. 我们将来完成GameManager,使它能处理游戏的状态,用户可以移动的不同方法,记录分数以及决定游戏何时结束和用户是否打破最高分以及用户是否输局了。 |