通过使用Axure制作「贪吃蛇」游戏,使自己更加熟练Axure中的各种函数及对一款产品开发时的结构梳理及逻辑分析。本文的受众对象为喜欢Axure或想学Axure的小白及达人,认为无用的人请点击「esc」按钮。
废话不多说,还是先上图吧。
http://e.g.easyiservice.com/71566H/#c=2
本文会通过第一人称视角,从确定制作游戏——设想游戏规则——游戏基本逻辑——游戏模块制作——游戏成品,一步步详尽地讲解如何实现,确保每一个功能及函数你都会看懂。(个别针对小白功能讲解其他人可以跳过)
每一次的开始
其实每次使用Axure工具时,对我们产品经理也是一个制作产品的过程,不管是画原型还是其他,我们首先都要想到5W1H:
- What(我们在做什么):一款简单的贪吃蛇小游戏,移动小蛇,通过小蛇吃到「肉」得分。
- Who(给谁做):喜欢使用Axure的人群。
- How(怎么做):如果要实现这个游戏,其中的每个功能、每个动作、每个函数怎么做。
- Why(为什么):为什么做这个产品,产品中的每个功能为什么这么实现,是不是有更好的方式能够实现这个功能或者动作?
- When(多久):针对目前来不及解决的问题或者没实现的想法,我们什么时候能够完善?之后多久能够补充和整理?
- Where(在哪发布?当然是woshipm.com):允许我这个where只是凑份子的(汗~)。
思路整理
我们做的游戏是「贪吃蛇」,大家应该都玩过吧,这是一个规则十分简单的游戏,首先我们在草稿中绘制出「小蛇」、「好吃的肉」、「边界」。
这应该是游戏中最基本的逻辑了,目前我们不去管怎么使小蛇动起来,或者如何让「肉」随机出现。通过这个简单的草稿页面,我们应该会分析出这个游戏有如下一些规则及注意事项(不分先后):
题外话:是的,做每件事情我们都需要使用头脑尽自己所能充分分析它所有的方面。如果我们不思考,直接百度『axure 贪吃蛇 源文件』,然后下载下来,这样又有何意义呢?
「贪吃蛇」虽然是款十分简单的游戏,但是如果我们通过自己的思考和努力实现它,是不是在获得知识的同时内心也会充满愉悦呢?
规则1:「蛇」只能上下左右移动单一方向移动,由此引申出「蛇」的大小、「蛇」每次移动的距离、「蛇」的移动速度等。
规则2:当「蛇」每次碰到「肉」时,「肉」消失,由此引申出每次碰到「肉」后,「肉」在其他位置随机出现,「蛇」身体增长单位长度,「蛇」碰到「肉」后移动方向及速度不变
规则3:游戏需要边界,「蛇」碰到边界时游戏失败
- 注意1:如何控制「蛇」移动的方向,通过键盘的上下左右还是怎样
- 注意2:如何控制「蛇」移动速度
- 注意3:如何使「肉」消失并随机位置出现
- 注意4:「蛇」身如何增长,引申当蛇改变方向时,如何正确显示。
梳理逻辑
目前我们先不需要知道如何通过具体函数处理相关运动,也不用去想如何设置相关功能动态面板等,我们可以先梳理出游戏的基本流程:
在这里希望大家先记住三个字:初始化。大家不妨回忆下,在你第一次安装完任何一款软件或者游戏后,打开的第一次都会出现初始化(可能是其他名字),我们虽然是一个十分简单的小游戏,但也一定不能免除这个步骤,目前初始化什么内容我们还不知道,但是我们要先确定第一个步骤是:初始化。
「黑色方框」部分是我在梳理逻辑过程中想到的:
- 为了调整「蛇」不同的移动速度,我们需要设置速度「等级」后再开始
- 为了体现游戏更加人性化,增加了一个「称号」功能
- 万一游戏时有事需要离开,但又不想重新开始,那只有使用「暂停」功能了
游戏最基本流程为:当点击「开始」时,游戏就正式开始了,我们移动小「蛇」,使蛇在吃到「肉」的过程中又不能碰到「边界」。
OK,我们的基本逻辑已经确定好了,你到这里也一定没问题吧,下面我们将会在Axure中,通过Axure的特性对每个逻辑详细展开实现。
终于开始了
先实现小球动起来吧。
我们现在对页面初始化的内容还无从下手,那么就从最基本的小「蛇」移动开始吧。在游戏过程中,只要「蛇」不碰到四壁,那么「蛇」就会一直以固定的速度运动,只不过可能会改变方向。聪明的你现在是否也想到了通过『循环』功能实现「蛇」的恒定移动呢?即每固定时间间隔,「蛇」移动固定的位移呢(位移,不论方向)?
目前大家普遍实现循环的方式就是通过「页面载入时」设置「循环切换动态面板状态」,然后对动态面板设置「状态改变时」实现循环的功能。例如设置「页面载入时」动态面板「A」向后循环,循环间隔1000毫秒,然后对动态面板「A」设置「状态改变时」切换B元素选中状态,那么我们就是实现了B元素每隔1秒变换一次显示状态。
此时需要注意:动态面板必须有2个或2个以上的状态才可以实现
原理相同,如果我们在动态面板「A」状态改变时,设置移动「蛇」x轴或y轴固定位移,那么我们就实现了「蛇」的移动了。但是这里面也衍生了3个问题:
- 如果通过循环间隔控制「蛇」的移动速度,那么这里面不能写函数,我们如何更改速度呢?
- 「蛇」在移动固定位移时,我们如何按下「→」时,「蛇」就向→移动固定位移呢?
- 「蛇」每次移动的位移是多少呢,1、10还是20合适?
不要头大,只要我们发现问题,就已经向解决问题前进了一步。如果我们想自定义「蛇」的移动速度,那么这里肯定会需要「变量」,在这里,我们新建一个全局变量「level」,通过「level」的不同取值是否能实现这个功能呢?
这里面最难的是最后一步,如何通过变量「level」控制「蛇」的移动呢?我们现在回到最开始,「蛇」的移动频率是通过动态面板的状态切换时间间隔决定的,而动态面板切换状态的间隔并不能通过变量赋值。那么我们必须通过其他途径同样获取到一个固定时间间隔变换的数值,通过这个数值实现「蛇」的移动。(以下设置level内容与我之前文章《Axure制作小游戏之「疯狂乒乓球1.0」,你能得多少分?》逻辑类似,如果了解可以跳过)
这个数值怎么得到呢?动态面板可以每隔最短1毫秒变换,是的,这里面我们发现是不是可以通过其他途径获得同样每隔毫秒为单位的数值呢?是的,时间函数!Axure中有丰富的函数可以使用,我们这里首先使用「Now.getTime()」函数,这个函数可以获得此刻的一个时间戳(最小单位毫秒),如果我们通过判断2个时间戳的固定差值,是不是就能得到之前想要的数值了呢?
在此我们设置页面载入时的文本「time_begin」== [[Now.getTime()]],然后每当动态面板状态变化时(间隔1ms),设置文本「time_now」 == [[Now.getTime()]],然后设置文本「time_bit」 == [[time_now-time_begin]],获得一个时间差值。
这个差值就和「level」联系起来了,当设置「level」==200时,如果我们设置只有当「time_bit」>=「level」时,「time_begin」重新赋值[[Now.getTime()]],那么我们就得到了一个200毫秒的可控制循环了。
是不是很神奇?我们得到了一个每level(200ms)的循环。在这里我设置了2个动态面板,第一个动态面板如前所述,第二个动态面板也是每1毫秒切换一次状态,在这里设置当time_bit>=level的判断条件即可。
这下明白了吗?每当time_bit==level时,证明时间运行了200毫秒,此时我们重新赋值time_begin,time_bit再次从0开始递增,同时我们设置「蛇」移动一个位移就可以了。
截图里面多了game_status的变量,别急,一会我们再讲。
现在可以控制小「蛇」方向了
下面我们该研究如何通过键盘控制「蛇」的移动方向了。这里面有两个问题:
- 检测键盘按键
- 控制「蛇」移动方向
在Axure中,可以直接通过「页面按键按下时」直接检测键盘按键,我们可以分别设置对应的上、下、左、右按键即可。
那么如何控制「蛇」的移动方向呢?在此我引入了2个方向全局变量:location_x与location_y,当小球横向移动时,location_y==0,location_x==1(向右)或-1(向左),当「蛇」竖向移动时,location_x==0,location_y==1(向下)或-1(向上)。
这样我们就实现了小「蛇」只能沿x轴或y轴单方向移动。
是时候解决移动多少位移的问题了
每次移动多少位移引申到以下几个问题:
- 整个游戏布局大小:本次设置的500*500
- 小「蛇」大小:本次设置20*20
- 每次移动位移
根据游戏布局及小「蛇」大小,我们可以设置小「蛇」每次移动位移为「蛇」本身大小,即20。这样整个游戏布局中,小「蛇」的移动属性我们就全部掌握了。
终于可以开”吃”了
先解决随机出现的问题吧。Axure中跟随机有关的函数只有一个Math.random()。所以我们只能从这里下手了。我们先设置「肉」的大小是20*20,那么它随机出现的位置范围是0<x<480,0<y<480。因为我们的小「蛇」每次移动都是以20为单位的,所以我们的「肉」出现的坐标也应该是以20为单位的整数。我们先获得20这个基准单位吧,[[Math.floor(Math.random()*24)*20]],这个函数我们得到的是首先获得0~24的整数,然后乘以20,即获得了以20为基本单位的数值了。
下一个问题就是小「蛇」碰到「肉」时,肉自动消失后又重新随机出现,出现问题解决了,如何解决「吃肉」这个判断呢?
在Axure中,有一项判断是「元件范围接触」,即当判断小「蛇」接触到「肉」时,肉重新随机刷新位置。
在这里,我们通过另外一个动态面板状态改变来实时判断这个接触过程。
难点处理
写到这里,基本功能和动作我们都基本实现了。但是细心的读者可能发现还有几个功能我没有实现。是的,在我制作过程中,我发现有些功能短期内无法处理,所以准备放弃或在2.0版本中实现,这也是我们平时产品开发时的常用套路,对吧?
- 小「蛇」吃到肉后长度自增:每当小「蛇」成功吃到肉后,小「蛇」长度恒定加20,宽度不变。
- 长方形小「蛇」转弯:如果小「蛇」是长方形时,转弯如何处理。
在我思考后发现,第一个自增问题其实是可以解决的,比如我们将小「蛇」分为蛇头和蛇身,蛇头固定20长度,每当吃到「肉」后,蛇身增加20长度,这个逻辑不难实现。但是因为同时延伸到后面的转弯问题,我发现自增也无法解决了。
如果小「蛇」是长方形,那么在改变小「蛇」方向的瞬间,如何控制小「蛇」移动呢?因为时间较短,目前我还没有想好完美实现的方法,希望在2.0版本中实现。如果有读者能提供更好的方法,欢迎随时回复。
间接实现
在1.0版本中无法解决,那么我们为了能将产品产出,也只能通过其他方式间接实现了。
小「蛇」不能自增,那么我们目前只让小「蛇」是一个20*20的恒定大小。
小「蛇」吃到肉后无法增加长度,那么我们就通过「分数」的方式,实现游戏的乐趣。
程序搭建
按照最开始的逻辑梳理,我们首先要为游戏界面设置一个动态面板,为了能在面板中呈现出不同的内容,我们需要给面板设置不同的状态:首页、设置、游戏中、游戏结束。
那么如何在状态中切换呢?这时候我们引入了一个全局变量「game_status」,通过判断「game_status」的值来进行各种操作。比如当值为「begin」时,「蛇」才会移动,当值为「over」时,面板状态切换到「游戏结束」等等。
游戏桌面构思完成,我们还缺少一个辅助的「信息栏」动态面板,通过这个面板我们可以实现鼠标控制上下左右移动、实时显示分数、暂停、显示玩家称呼等功能。
OK,一共需要显示2个动态面板(其他动态面板做循环功能,不显示),那么我们现在就可以进行初始化的工作了,初始化所有内容如下:
- 自动调节显示面板到页面中间显示
- 设置所有循环类动态面板每隔1毫秒自动切换状态
- 设置「time_begin」初始值==[[Now.getTime()]]
- 设置全局变量game_status==home,level==200,direction_x==1,direction_y==0,angle==20,score==0,score1==3,tools==pc
- 设置循环面板「bit_time」赋值「time_bit」和「time_now」
其中有「score」为分数全局变量,「score1」为游戏难度基准值,难度越高值越高,每当小「蛇」碰到「肉」时,「score」==「score」*「score1」+「score」,即每次增加固定分数值。
「tools」为「键盘按键」或「鼠标点击」的方式,当「tools」==pc时,玩家只可以使用键盘的上下左右改变方向,当「tools」==mouse时,玩家只可以使用鼠标点击「信息栏」中的上下左右按钮。
初始化之后游戏直接进入首页,此时game_status==home。在首页中我们可以点击「设置」或「开始」2个按钮,当点击「开始」时可以使用默认设置项进行游戏。设置项包括:
- 游戏难度:默认正常
- 游戏方式:默认键盘
- 称呼:默认无
我们配置点击「设置」按钮时,动态面板进入「设置」状态,在设置每项数据时,我们需要配置「切换游戏等级」时,将值传送给全局变量「level」,输入「称号」时,将值传送给信息栏中的称号,切换「游戏方式」时,将值传送给全局变量「tools」。
当点击「确定」时,再次将动态面板状态切换到「首页」。
OK,下面配置「开始」按键吧,点击「开始」需要完成以下内容:
- 设置小「蛇」位置到达(0,0)
- 等待1秒钟:防止进入后小「蛇」突然移动导致失败
- 设置「game_status」==begin
是不是有人发现截图里面有些内容不一样?没关系,我们在后面的优化部分再谈。
现在我们已经进入到游戏页面了,在这里我们需要配置以下几项内容:
- 循环动态面板「bit_snake」配置成当「game_status」==begin且「time_bit」>=level时,重新赋值「time_begin」并且移动小「蛇」(实现了在游戏中时每隔level毫秒,小蛇移动1个位移)
- 「页面按键按下时」,修改对应direction_x、direction_y的值
- 循环动态面板「bit_score」配置成当「game_status」==begin且小「蛇」接触到「肉」时,分数增加并且随机生成「肉」的位置
- 循环动态面板「bit_status_over」配置成小「蛇」的x、y值不在0~480时,「game_status」变为over。并切换动态面板桌面的状态为「游戏结束」
最后我们再配置下「游戏结束」状态。游戏结束时只可以点击「确定」按钮,当点击「确定」时,会像「页面初始化」一样将所有需要的值初始化赋值即可。
剩下的就是「信息栏」了。
右面的分数、难度、称号显示我们已经讲过了。当点击「暂停」动态面板时,全局变量「game_status」==pause,游戏桌面的所有内容也就自动暂停了,然后切换动态面板状态到「恢复」即可。
左面的按键则通过全局变量「tools」判断,只有「tools」==mouse时,按钮才可以点击,点击效果为改变direction_x、direction_y的值。
再次优化
是否终于松了一口气,以为完成了呢?就像我们产品在开发阶段的α测试一样,测试完会发现很多问题:
- 小「蛇」如果从(0,0)开始,大小又正好是20*20,容易造成游戏直接结束的问题,因为我们的判断是当小「蛇」接触0~480时即结束。
- 如果小「蛇」贴着「肉」,并没有路过时,也会出发吃掉「肉」的效果,其实是个bug
- 因为小「蛇」长度不能自增,可玩性太低
针对以上几点问题,我们制作了如下解决方案:
- 小「蛇」大小修改为19*19,初始位置为(1,1),避免了触发边界时的bug。
- 「肉」大小修改为15*15,避免了路过即得分的bug。
- 增加随机障碍「雄黄」,每次「肉」被吃掉后,桌面内会在随机位置出现一块随机大小的「雄黄」,当小「蛇」碰到「雄黄」时,游戏同样结束。
总结
终于可以写出这两个字了,在这篇写了5000字左右的文章中,我希望尽可能写的足够详细,对于用过Axure的人,无论掌握什么程度,按照我的思路都可以理解每一个步骤和过程。如果有人觉得过于繁琐,请你放心,这应该是我最后一篇这么详细描述的Axure教程了。
增加了「雄黄」之后,虽然多了随机性,但是同样出现了一些bug,例如「雄黄」和「肉」的刷新位置相同等,小「蛇」就无法吃肉了,如果有时间,我会继续做出更完善的「贪吃蛇」游戏。
可能有很多人觉得用Axure做小游戏实在是一个无聊且无用的行为,还记得在上PMP课程时,讲师第一课就通过一个产品经理买菜的故事,将整个买菜流程作为一个庞大项目管理看待,从确定买菜、计划菜品、计划买菜时间、来回路线等足足讲了1个小时。对于我们产品经理而言,任何事情又不是一个产品设计开发流程呢?当我在使用产品人最喜欢的原型设计软件制作一款小游戏时,何尝不是『机会识别——概念产生——概念评估——开发——上市』的整个流程呢?具体的有用无用,同样因人而异罢了。
如果您有同样的兴致,欢迎来留言交流。希望这篇文章能够对你有些帮助,谢谢!
以下为源码链接:http://pan.baidu.com/s/1o8FW2Rg 密码:uk9n
本文由 @escher 原创发布于人人都是产品经理。未经许可,禁止转载。