分享更有价值
被信任是一种快乐

如何利用AngularJS开发2048游戏

文章页正文上

这期内容当中小编将会给大家带来有关如何利用AngularJS开发2048游戏,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。我频繁地被问及到的一个问题之一,就是什么时候使用Angular框架是一个糟糕的选择。我的默认答复是编写游戏的时候,尽管Angular有它自己的事件循环处理($digest循环) ,并且游戏通常需要很多底层DOM操作.如果说有Angular能支持很多类型的游戏,那这个说法可不准确。即使游戏需要大量的DOM操作,这可能会用到angular框架处理静态部分,如记录最高分和游戏菜单。如果你和我一样迷上流行的2048游戏. 游戏的目标是用相同的值相加拼出值为2048的方块。我们会用AngularJS从头到尾地创建一个副本, 并解释创建app的全过程。由于这个app相对复杂,所以我也打算用这篇文章来描述如何创建复杂的AngularJS应用。这是我们要创建的demo.现在开始吧!TL;DR: 这个app的源代码也可下载,文章尾部有该app在github上的链接.第一步我们要做的,就是给要创建的app做高层设计。无论是山寨别人的app,还是自己从零做起,这一步都与app的规模无关。再来看看这个游戏,我们发现在游戏板的顶端有一堆瓦片。每个瓦片自身都可以作为一个位置,用来放置其他有编号的瓦片。我们可以根据这个事实,把任务移动瓦片的任务交给CSS3来处理,而不是依靠JavaScript,它需要知道移动瓦片的位置。当游戏面板上有一个瓦片,我们只需要简单地确保它放在顶部合适的位置即可。使用CSS3来布局,能带给我们CSS动画效果,同时也默认使用AngularJS行为来跟踪游戏板的状态,瓦片和游戏逻辑。因为我们只有一个单页面(single page),我们还需要一个单控制器(single controller)来管理页面。
因为应用的生命周期内只有一个游戏板,我们在GridService服务中的一个单一实例里包含所有的网格逻辑。由于服务是单例模式对象,所以这是一个存储网格的恰当位置。我们使用GridService来处理瓦片替换,移动,管理网格。而把游戏的逻辑和处理放到一个叫做GameManager的服务中。它将负责游戏的状态,处理移动,维护分数(当前分数和最高分)最后,我们需要一个允许我们管理键盘的组件。我们需要一个叫做KeyboardService的服务。在这篇博文中,实现了应用对桌面的处理,我们也可以复用这个服务来管理触摸操作让它在移动设备上运转。为了创建app,我们先创建一个基本的 app (使用yeoman angular 生成器生成app的结构, 这一步不是必须的. 我们只把它作为切入点,之后就迅速地从它的结构上分开。).创建一个app目录用来放置整个应用。把test/目录作为app/目录的同级目录.The following instructions are for setting up the project using the yeoman tool. If you prefer to do it manually, you can skip installing the dependencies and move on to the next section.因为在应用中我们用了yeomanin工具, 我们首先要确保它已经安装好了. Yeoman安装时基于NodeJS和npm.安装NodeJS不是这篇教程所要讲的,但你可以参看NodeJS.org 站点.装完npm后,我们就可以安装yeoman工具yo和angular生成器(它由yo调用来创建Angular app):安装后,我们可以使用yeoman工具生成我们的应用,如下:该工具会询问一些请求。我们都选yes即可,除了要选择angular-cookies作为依赖外,我们不需要任何其他的依赖了。Note that using the Angular generator, it will expect you have the compass gem installed along with a ruby environment. See the complete source for a way to get away without using ruby and compass below.我们将创建scripts/app.js文件来放置我们的应用。现在就开始创建应用吧:布局angular应用使用的结构现在是根据函数推荐的,而不是类型。这就是说,不用把组件分成控制器,服务,指令等,就可以在函数基础上定义我们的模块结构。例如,在应用中定义一个Game模块和一个Keyboard模块。模块结构清晰地为我们分离出匹配文件结构的职能域。这不仅方便我们创建大型,灵活性强的angular应用,也方便我们共享app中的函数。最后,我们搭建测试环境适应文件目录结构。应用中最易切入的地方非视图莫属了。审视视图自身,我们发现只有一个view/template.在这个应用中,不需要多视图,所以我们创建单一的在我们的主文件app/index.html中,我们需要包含所有的依赖项(包括angular.js自身和JS文件,即scripts/app.js),如下:Feel free to make a more complex version of the game with multiple views – please leave a comment below if you do. We’d love to see what you create.有了app/index.html文件集,我们需要在应用视图层面上,详细地处理app/views/main.html中的视图。当需要在应用中导入一个新
资源时,我们需要修改index.html文件。打开app/views/main.html,我们要替换所有的游戏指定的视图。使用controllerAs语法,我们可以在$scope中清楚地知道我们期待在哪里查询数据,哪个控制器负责哪个组件。ThecontrollerAssyntax is a relatively new syntax that comes with version 1.2. It is useful when dealing with many controllers on the page as it allows us to be specific about the controllers where we expect functions and data to be defined.在视图中,我们要显示以下一些项目:游戏静态头当前游戏分数和本地用户最高分游戏板游戏静态头可以这样来完成:注意到,当在视图中引用currentScore和highScroe时,我们也引用了GameController.controllerAs语法使得我们可以显示地引用我们感兴趣的控制器。现在我们有了一个合理的项目结构,现在来创建GameController来放置我们要在视图中显示的值。在app/script/app.js中,我们可以在主模块twentyfourtyeight.App中创建控制器:在视图中,我们引用了一个game对象,它将在GameController中设置。该game对象将引用主game对象。我们在一个新模块中创建这个主游戏模块,用来放置游戏中所有的引用。
因为这个模块还没有创建,app不会再浏览器中加载它。在控制器中,我们可以添加GameManager依赖别忘了,我们正创建一个模块级别的依赖,它是应用中不同的部分,所以要确保它在应用中正确地加载,我们需要将它列为angular模块的一个依赖。为使Game模块成为twentyfourtyeightApp的依赖,我们在定义该模块的数组中列举它。我们整个的app/script/app.js文件应该看起来像这样:既然我们有视图间部分相互连接了,那么就可以开始编写游戏背后的逻辑了。为创建一个新游戏模块,我们在app/scripts/目录中把我们的模块创建为app/scripts/game/game.js:When building modules, we like to write them in their own directory named after the module. We’ll implement the module initialization in a file by the name of the module. For instance, we’re building a game module, so we’ll build our game module inside theapp/scripts/gamedirectory in a file namedgame.js. This methodology has provided to be scalable and logical in production.Game模块将提供一个单核心组件:GameManager.我们将来完成GameManager,使它能处理游戏的状态,用户可以移动的不同方法,记录分数以及决定游戏何时结束和用户是否打破最高分以及用户是否输局了。开始开发应用时,我们喜欢为我们用到的方法编写stub方法,并编写测试代码然后填入要实现的地方。For the purposes of this article, we’ll run through this process for this module. When we write the next several modules, we’ll only mention the core components we should be testing.我们知道GameManager将支持以下特性:建立新游戏处理游戏循环/移动操作更新分数跟踪游戏是否结束有了这些特性,我们可以创建GameManager服务的基本大纲,我们就可以对它进行测试代码的编写:基本的功能实现完后,就来编写测试代码,使它定义GameManager需要支持的功能.开始实现测试前,需要使用karma驱动测试。如果你对karma不熟悉,就把它当做一个测试runner,它允许我们在终端和代码中舒适高效地进行前台的自动化测试。
要使用Karma,我们需要确保它已安装正确。使用Karma,我们要依赖NodeJS,因为它可以作为一个npm包。运行以下代码,安装Karma:The-gflag tells npm to install the package globally. Without this flag, the package would only be installed locally in the current working directory.如果你使用了yeoman angular生成器,你可以跳过下一部分。要使用 karma, 我们需要编写一个配置文件。尽管我们不会深入讨论怎样配置Karma(猛戳这里ng-book ,查看配置Karma的详细选项), 但是关键的部分还是要知道的,即设置Karma使它在测试中加载所有我们感兴趣的文件。要创建一个配置文件,我们可以使用karma init命令来创建一个基本的版本.该命令会询问一些请求并创建karma.conf.js文件。从这里起,我们将改变两个配置选项:files数组和要打开的autoWatch:建立完这个配置文件,我们可以随时运行测试(它写在test/unit/目录下)
为运行测试,我们运行karma start命令,如下所示:既然karma安装和配置好了,我们就可以开始为GameManager编写基本的测试。因为我们还不知道应用的全部功能,我们只能进行有限的测试Often times, we find that our API changes as we develop the application, so rather than introduce a lot of work ahead of time that we’ll likely change, we set up our tests to test basic functionality and fill them in deeper as we uncover the eventual API.第一份测试的较好的备选方案,是它可以告诉我们有没有可能向左移动。为测试是否可以向左移动,我们简单地写一个我们需要调用的stub方法,它测试应用逻辑的行为并返回true/false.我们将穿件一个文件—test/unit/game/game_spec.js,并开始创建我们的测试上下文:In this test, we’re using Jasmine syntax.同其他单元测试一样,我们需要创建GameManager对象的实例。我们可以沿袭常规(当测试服务时),把它注入到我们测试中:有了这个gameManager的实例,我们可以开始编写movesAvailable()期望的功能.我们将定义movesAvailable()函数,它用来验证是否有剩下可用的方块以及验证有没有可能的合并。因为它是游戏是否结束的条件,我们把这个方法放到GameManager,但是在GridService中实现大部分功能,GridService将在下一步创建。要看游戏板上是否有方块移动,我们看两个条件:游戏板上有可用的位置有可匹配的位置有了这两个条件,我们就可以编写测试代码来看是否满足这两个条件。最基本的想法就是我们写出测试代码,然后满足一个条件,它可以用来观察单元测试在环境下的表现。由于依赖GridService来报告游戏板的条件,所以我们要在GameManager中改变条件来看逻辑是否正确。要mock我们的GridService,我们通过重写默认的Angular行为来“提供”我们的mock后的服务,而不是真正的服务,所以我们可以在服务中建立可控的条件用mocked方法创建一个fake对象,然后通过$p免费云主机、域名rovide服务处理它们并告诉Angular这些fake对象是真正的对象。现在我们可以使用这个fake _gridService实例来建立我们的条件。
我们要确保当有可用的方块时,movesAvailable()函数返回true.现在就在GridService中mock anyCellsAvailable()方法。我们希望这个方法在GridService中报告是否有可用方块。既然基本原理弄清楚了,我们就可以设定第二个条件的期望值了。如果有可用的搭配,我们就要确保movesAvailable()函数返回true.同时我们确保对话返回true时,要是没有可用的网格或搭配,就没有可用的移动。另两个测试确保如下过程:我们已经奠定基础了,现在在实现期望的功能前可以编写测试样例了。Although we aren’t going to continue with TDD in this post, for the sake of overall completion, we suggest you should continue with it. Check out the full source code below for more tests.现在我们来实现movesAvailable函数. 我们已经测试代码可以运行, 并且明确了执行的条件, 这个函数实现起来就简单了.GameManager已经准备妥当, 我们接下来就要创建GridService来管理游戏板.回想一下我们用来描述游戏板的两个数组变量grid和tiles, 我们用这两个局部变量来设置GridService. 在app/scripts/grid/grid.js文件中, service的创建代码如下:当我们想创建一个新游戏, 数组用null元素初始化. grid数组只包含在游戏板上用来放置方块的固定数量的Dom元素, 因此grid可以理解为静态的.相比而言, tiles数组用来存放游戏中正在使用的瓦片, 则相对是动态变化的. 下来我们在页面上创建grid, 看看如何通过使用这些变量来控制grid和瓦片的布局.回到app/views/main.html中,我们需要开始布局网格。因为它是动态的,加上我们要把我们的逻辑处理放在网格内,我们仅仅只要把逻辑放到它自己的指令内。使用指令,将清空主模板和在指令中的封装的功能,同时主控制器也被清空。
在app/index.html中,我们把网格指令放到网格并在控制器中传递GameManager实例:编写这个指令,使它能包含在Grid模块中。在app/scripts/grid/目录下,我们创建一个grid_directives.js文件来放置grid指令。
在grid指令中,由于它的权限有限,不能封装视图,所以我们还需要一些变量。这个指令需要一个GameManager实例(或者,至少一个包含grid和tiles的模型),这样就可以根据指令的需要完成了一个自定义的指令。另外,我们不希望我们的指令干扰到页面或者页面中的GameManager实例,所以我们需要使用isolate来创建这个之类,用于限制它的使用范围。在指令的模板里面,我们使用两次ngRepeat来遍历展示grid和tiles数组,并且分别使用$index来跟踪遍历的结果。可以看到第一个ng-repeat是一个非常简单的遍历,将ngModel.grid遍历输出到一个class为grid-cell的div里面。在第二个ng-repeat里面,我们给每一个屏幕上的元素创建一个叫做tile的辅助的指令。这个tile指令将用于给每个tile元素创建直观的页面显示效果。后面我们再来创建这个tile指令…精明的读者可能会看到,我们只使用了一个一维数组来展示一个二维网格。当我们渲染视图的时候,我们只获取一列tiles,而不是一个格子。为了让他们变成网格,我们需要使用CSS。针对这个项目,我们使用SASS的一个现代变体:scss。scss不仅是一个更强大的CSS,我们将会以动态的方式来构建我们的CSS。这个app的视觉元素部分将使用CSS完成,包括动画以及布局和视觉元素(瓷砖的颜色等)。为了可以使用二维数组的方式创建面板,我们需要使用CSS3的transform关键字来将每个瓷砖放置在面板特定的位置上。CSS3 transform 属性向元素应用 2D 或 3D 转换。 该属性允许我们对元素(当然是可以动起来的元素)进行移动、倾斜、旋转、缩放,以及其它更多动作. 使用这个属性,我们可以简单地将方块放到游戏板上,然后给元素应用适当的transform属性。例如,下面这个示例,我们有一个40px宽和40px高的box类:如果我们应用一个translateX(300px)属性,我们将向左移动盒子300px,以下示例证明了这一点:使用这个转换属性,我们能够简单地通过给我们的方块应用一个CSS类标记在游戏板上移动它们。现在,微秒的地方就是我们怎样来构建我们动态的类,如此,当我们在页面上定点时,它们使用CSS类来对应一个合适的方格?这就是SCSS发挥威力的地方。我们将设置一些变量(比如一行我们想要几个方块),并且在这些变量周围构建我们的SCSS,使用一些数学方法来为我们做计算。让我们看一看这些变量,我们需要正确的计算它们的在游戏板上的位置:我们可以让SCSS帮我们动态的计算这三个变量的位置。首先,我们需要计算每一个方块的面积。这对SCSS变量来说是非常容易的:现在我们可以为#game这个容器设置合适的宽高。同样,我们在#game这个容器上设置位置参数,这/p>

相关推荐: 满足解决Docker容器网络下UDP协议的问题

这篇文章主要讲解了“满足解决Docker容器网络下UDP协议的问题”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“满足解决Docker容器网络下UDP协议的问题”吧!问题重现这个问题很容易重现,我的实验是在 ub…

文章页内容下
赞(0) 打赏
版权声明:本站采用知识共享、学习交流,不允许用于商业用途;文章由发布者自行承担一切责任,与本站无关。
文章页正文下
文章页评论上

云服务器、web空间可免费试用

宝塔面板主机、支持php,mysql等,SSL部署;安全高速企业专供99.999%稳定,另有高防主机、不限制内容等类型,具体可咨询QQ:360163164,Tel同微信:18905205712

主机选购导航云服务器试用

登录

找回密码

注册