http://www.jianshu.com/p/ce791bef66bb
PlayMaker是什么?
PlayMaker是Unity3D的一款 可视化 的 有限元状态机(Finite-state machine,简称Fsm) 插件,用来进行交互设计。
很多人把PlayMaker等同于“可视化编程”,官方在宣传时也自我标榜为“PlayMaker - Visual Scripting for Unity3D”。在我看来,这么说其实是不太恰当的。
Unity3D中真正可以称作“可视化编程插件”应该是 uScript Professional,而PlayMaker只能算是一个“可视化交互设计插件”,因为PlayMaker只允许使用FSM这一种“编程策略”,而且Fsm这一策略的设计思路其实是有别于正常撰写程序脚本时所采用的设计思路的。
准确的说,PlayMaker提供了一套可视化的解决方案,让用户可以无需撰写脚本代码,就能运用有限元状态机的设计思路在Unity3D中设计并实现交互逻辑。
所以,我们学习PlayMaker,最主要的是去学习一种交互设计的思维方法。掌握PlayMaker本身的特性和功能当然很有必要,但最终的目的是建立一种思维习惯,这种思维方法和思维习惯,是可以带到其他软件工具中去的,比如UE4的 Blueprints(蓝图系统),甚至真正的Programming。
PlayMaker究竟能做什么?
虽然PlayMaker官方网站上列举了很多使用PlayMaker制作的游戏产品。但实际上,如果你希望通过几周的学习,掌握了解PlayMaker本身用法之后就能够制作一个功能完成的游戏产品,那是极为不现实的。
PlayMaker可以让用户摆脱“写代码”这项工作,但不代表可以让用户摆脱“程序设计”这项工作,而且由于PlayMaker完全基于状态机这种“策略”,实际上增加了“程序设计”的工作难度。掌握PlayMaker这件工具本身的使用方法只是第一步,更重要的是利用这件工具去锻炼我们思维,去解决实际问题。
PlayMaker真正的用途并不是去替代那些程序员(游戏开发)的编程工作,而是给非程序员提供一个快速制作玩法原型(prototype)的工具,让他们能够独立把脑海中“想象”的玩法设计实现出来,无需花费好几年的时间去专门学习编程,也无需去“跪求”程序员大大们的帮助。
当我们经过努力的学习,逐渐能够使用PlayMaker实现一个相对完整的游戏交互逻辑的时候,其实我们的“编程思维”就不知不觉中建立起来了。那些使用PlayMaker制作出完整游戏的开发者,我相信大多数都是很优秀的程序员,因为不论是C# Scripting还是PlayMaker FSM,其背后的本质都是所谓的“计算机程序设计思维”。
所以,PlayMaker究竟能做什么?这个问题其实挺不好回答的。PlayMaker只是一个工具,有优势,也有不足。我们能用这个工具做什么取决于我们自身,而不是这个工具。
PlayMaker的基本概念:Fsm、States、Events、Transition、Actions、Variables
Fsm(状态机)
举个小例子
前面已经讲过了Fsm的基本定义,现在举一个日常生活的例子。
我们一天中会做很多事情,但我们可以把这些事情归纳为几个状态:睡觉;吃饭;上课;打游戏等。如果把早上作为一天的开始,我们的生活逻辑是这样的:
- 一开始我们在睡觉(初始状态),但设了个闹钟(触发条件);到7点了,闹钟响起来(满足触发条件),我们去吃饭(状态转换);吃饭完成(满足触发条件),我们去上课(状态转换);下课铃响(满足触发条件),我们又去吃饭(状态转换);吃完(满足触发条件)看看时间(新状态哦),如果还早(满足触发条件1),就打打游戏(状态转换);如果时间不早了(满足触发条件2),就赶紧去上课(状态转换);……
在不同的状态下我们会做不同的事情,睡觉状态下会循环进行“呼吸”操作。吃饭状态下会依次进行“夹菜”、“送入口中”、“咀嚼”、“吞下”等操作。重要的是,不同状态下的行为设计是彼此独立的,而且与交互逻辑的设计本身也是彼此独立的。我们可以在进行行为设计之前就完成完整的交互逻辑设计,然后再慢慢添加从简单到复杂的各状态行为。
创建Fsm
在PlayMaker中,Fsm是被作为component(组件)添加给GameObject的。因此,一个Fsm可以被看做是一个独立的脚本程序,用以实现一个独立的功能。
从菜单PlayMaker
> PlayMaker Editor
中打开PlayMaker编辑器。
选择需要添加FSM的GameObject(这里我新创建了一个Cube),打开PlayMaker编辑器,在编辑器中按照提示点击鼠标右键,选择Add FSM
,就为该Cube附加了全新的FSM类型的组件(Component):
这时界面上已经提示我们的Fsm是被添加在Cube物体上了:“Cube:FSM”(注意,这里的“FSM”代表的是这个Fsm的名字,新建的Fsm的名字都叫做“FSM”、“FSM 1”、“FSM 2”,最好能够修改一下,以免混乱)。
Inspector面板上我们可以看到这个Component:
没有很多参数,可以修改名称,可以点击Edit
打开编辑器来进行详细设置,可以添加一些简单描述Description...
,还可以选择一个Fsm模板以重复利用以前的工作成果。
PlayMaker的编辑器分两栏,左边叫Graph(图表面板),右边是参数面板。参数面板有4部分组成:FSM、State、Events、Variables,分别用来对状态机、状态、事件、变量进行设置。
在FSM栏中其实没有什么太多需要修改的,名称要改一下,我通常会使用FSM_*
的方式来命名,然后单词首字母大写,单词间不留空格,比如FSM_Movement
、FSM_HealthControl
这样。
当我们创建了一个新的Fsm时,会自动得到一个初始状态State 1
,这个State 1
上方还有一个START
的图标并有箭头指向State 1
。
State(状态)
在空白处点击鼠标右键,选择Add State
,可以添加一个新的状态State 2
。可以看到新的State 2
上是没有触发条件的,这说明State 2
完全没可能被激活,也就是完全不起作用。
我们可以在State 2
上单击右键并选择Set as Start State
,那么State 2
就会变成我们的初始状态,State 1
不起任何作用。
点击Unity3D工具栏中间的Play按钮运行场景,我们会发现State 2
上有一圈绿光,PM编辑器左下角也有提示当前状态处于“State 2”。
这时我们是没有办法让Cube返回“State 1”的,因为我们还没有设置任何触发条件。
Event(事件)和Transition(转换)
下面我们来添加一些触发条件,也就是event事件。
在State 2
上点击右键,选择Add Transition
> FINISHED
。(“FINISHED”也是一个系统事件,代表“本状态已经执行完所有操作的意思)
这时候State 2
下面多出来一行FINISHED
,说明我们已经添加了一个Transition(转换)给State 2
,同时这个Transition的发生条件是FINISHED
事件被触发。
但这时出现了一个错误标志(红色圆圈中间有白色感叹号),这是因为我们给一个State添加了Transition,却还没有指定这个Transition的目标State,就好像我们上了一辆的士,却还没告诉司机往哪里开一样。这个错误标志就是在提示我:你小子还有事情没干完呢!
用鼠标把FINISHED
拖到State 1
上松开,我们就指定了这个Transition的目的地是State 1
,也就是说我们告诉电脑,当FINISHED
这个事件被触发时,请将Cube的当前状态转换到State 1
,这时候错误提示消失了。
点击State栏位右边的Events
进入Events栏,我们会发现这里已经有了一个叫“FINISHED”的Event了,而且显示被使用过1次。Fsm中所有被使用到的Events都会被显示在这一栏内,我们可以通过查看Used
数挑出那些无用的Event并删除掉。
如果需要添加新的Event,只需要在下方Add Event
中输入名称,点击Enter
(回车)键就可以了。
前面在添加Transition的时候会看到除了可以添加普通Transition以外还可以添加Global Transition(全局转换)。全局转换的形态就是好像最前面的START
一样,位于State的上方且黑底。所谓全局转换的意思是不论物体当前处于哪个State,只要该Event被触发,都会转换到目标State。而一般的Transition,只有在其所在State是当前状态的情况下才能够进行转换。
下面我们可以把之前的生活逻辑做成一个Fsm了:
在场景中新建一个空物体(Create
> Create Empty
),Reset位移,改名为MyLife
。
打开PM编辑器,建立如下Graph:
看起来,我们的生活还是蛮复杂的嘛!
点击Play:当前状态停留在Sleeping
。因为我们还没有设置任何Action,所以也不会自动触发任何非系统Event,但我们可以手动激活Transition。
按住Alt
键不放,点击我们希望激活的Transition,会发现当前状态发生了改变。当状态改变到Getting up
的时候,会很快跳到Breakfast
然后又跳到Check time
,这是因为FINISHED
是一个系统事件,状态中的Action都执行完毕就会自动跳转。
“无限循环”错误:
下面我们建立一个简单的Graph来演示一个PlayMaker中很常见的错误:
我们希望状态1完成以后去完成状态2,状态2完成以后又回到状态1,貌似是个很正常的逻辑,在两个状态间不断循环。(底下的State 3
是这个错误的简化版,也是不断循环执行自身的意思)
但如果点击Play
按钮,会出现[DISABLED]
提示,Unity的Console栏也会出现错误提示:
估计大家在自己实践的过程中会经常看到这个错误提示。它表面上的意思是告诉我们,这个FSM循环了超过1000次,预设的最大循环次数是1000,请在Inspector面板中修改设置。如果大家真的跑去修改这个预设的最大循环数,可能会出现两个可能,一是会超过你新设置的最大循环数继续报错,二是会导致死机。
为什么呢?我们在两个State中都没有任何Action,所以会立即转换到目标State,于是在游戏时间1帧内,State 1
和State 2
会不断转换,直到达到1000次被强行停止,在这个过程中,游戏一直停顿在那一帧,如果你的电脑很快,也许这个错误信息不到1秒钟就报出来了,但如果你的电脑很慢,可能会停顿四五秒才会达到1000次循环,在这四五秒中整个游戏是卡住的。
System Events(系统事件)
APPLICATION FOCUS
:游戏运行时APPLICATION PAUSE
:游戏暂停时APPLICATION QUIT
:游戏退出时BECAME INVISIBLE
:物体不可见时BECAME VISIBLE
:物体可见时COLLISION ENTER
:碰撞体进入时COLLICION ENTER 2D
:2D碰撞体进入时COLLISION EXIT
:碰撞体离开时COLLISION EXIT 2D
:2D碰撞体离开时COLLISION STAY
:碰撞体停留期间COLLISION STAY 2D
:2D碰撞体停留期间CONTROLLER COLLIDER HIT
:Controller类碰撞体被触碰时JOINT BREAK
:骨骼断开时JOINT BREAK 2D
:2D骨骼断开时LEVEL LOADED
;关卡载入时MOUSE DOWN
:鼠标在物体上被按下时MOUSE DRAG
:鼠标在物体上被按下然后拖动时MOUSE ENTER
:鼠标滑入物体时MOUSE EXIT
:鼠标滑出物体时MOUSE OVER
:鼠标悬停物体之上时MOUSE UP
:鼠标在物体上按下并松开时(单击)MOUSE UP AS BUTTON
:鼠标单击(作为按钮)PARTICLE COLLISION
:粒子碰到碰撞体时TRIGGER ENTER
:触发器被进入时TRIGGER ENTER 2D
:2D触发器被进入时TRIGGER EXIT
:触发器被离开时TRIGGER EXIT 2D
:2D触发器被离开时TRIGGER STAY
:触发器被停留期间TRIGGER STAY 2D
:2D触发器被停留期间
普通的event必须由Action来触发,而系统事件则无需通过Action来触发。我们可以做一个简单的例子,通过鼠标左键的按下和松开来控制两个状态之间的转换:
新建场景,创建一个Cube,Reset位移。然后打开PM编辑器,创建FSM,并另外创建一个State 2
。在State 1
上点击右键,选择Add Transition
> System Events
> MOUSE DOWN
,同理在State 2
上添加MOUSE UP
,然后把它们连起来。
然后点击Play运行场景。
保持PM的编辑器可见,然后在Game View中按下鼠标左键。
如果是在Cube上按下左键的话,Cube会进入State 2,松开鼠标,Cube返回State 1。
Network Events(网络事件)
CONNECTED TO SERVER
:连接上服务器时DISCONNECTED FROM SERVER
:从服务器断开时FAILED TO CONNECT
:连接服务器失败时FAILED TO CONNECT TO MASTER SERVER
:链接主服务器失败时MASTER SERVER EVENT
:主服务器事件NETWORK INSTANTIATE
:网络重名时PLAYER CONNECTED
:玩家连接成功时PLAYER DISCONNECTED
:玩家连接中断时SERVER INITIALIZED
:服务器重置时
Action(动作)
如果说使用Fsm、States、Events和Transitions可以搭出一个合理的交互逻辑的框架的话,这个交互逻辑在添加Action之前就完全是一个空架子,一个设计而已。只有添加了Action,State才变得有意义,GameObject才会随着PlayMaker设计的这个逻辑来行动。
PlayMaker有非常多的Action,而且还有很多开发者在为PlayMaker编写各式各样的第三方Action(可以理解成有人为PlayMaker这个插件开发插件),一个Action通常执行一项或几项Unity3D的“操作”,比如获取某个GameObject的位置,在场景中新建一个Cube,改变一个材质球的颜色,为一个变量赋值等等。
选择一个State,点击编辑器右下角的Action Browser
可以打开动作浏览器。第一眼看到这个浏览器我整个人是崩溃的,那么多Action找都找不过来,更别说使用了。好在这个浏览器提供了搜索过滤功能,我们可以输入一些关键字来快速定位我们想要使用的Action。
怎么学习PlayMaker的Action
这么多Action应该怎么去学?我们要不要把每个Action的用法都背下来?我个人的经验是从实践中去学习。
首先当然需要把整个Action列表浏览一遍,大致知道PlayMaker提供了哪些方便有用的Action给我们,又有哪些是很基础性的功能,简单却常常会用到。
Action列表自身的组织结构是有分类的,但这个分类主要是依照Action所处理的对象来分的,而且主类就有几十个,并不是特别适合我们学习。我这里提供一个我自己理解的依照Action所执行功能而做的大致分类给大家参考:
按照功能标准在脑海中对所有Action有了个初步印象以后,就要记忆一些关键字了,因为我们99%的情况下,都是通过关键字在Action Browser中去搜索需要的Action,而不是顺着列表慢慢找。记住这些关键字可以极大的方便我们定位Action,节省时间。这些关键字包括:get
、set
、gameobject
、position
、fsm
、float
、bool
、vector3
、collision
、trigger
、ray
、compare
等等。
这个记忆工作可以随着我们学习和练习的过程来做,看得多了,做得多了,慢慢也就知道要实现哪些功能需要用到哪些Action了,那么这些Action的用法就是需要熟练掌握的。总而言之,学习Action要按照“功能类”来一类一类地学,而不是一个一个Action地学。
对于我们用到的Action,花一点点时间去看看这个Action的具体用法。在Action Browser中定位到Action之后是可以看到一个简单的描述的,英文好的同学可以直接阅读一下,看不懂的就去找中文翻译。我有时间的时候也会慢慢完成 PlayMaker Actions (未完成) 这一篇文章,以供大家参考。
对PlayMaker有个基本的掌握之后,网上的PlayMaker教程对你基本上就没有什么难度(总体来说PlayMaker的教学资源还是太少,也太浅),这个时候可以开始看那些入门级别的Unity3D小游戏教程,看人家怎么写脚本来实现交互设计的,然后把别人的思路转化到PlayMaker里面,用PlayMaker去重现这些教程的内容。
关于 Every Frame
选项
有些Actions会具有一个Every Frame
的选项,通常都在最下方。勾选这个选项,会迫使这个Action每帧都执行一遍,直到游戏物体离开当前状态。不勾选的话这个选项的话,每次进入这个状态以后,这个Action的操作都会进行一次,不论游戏物体在这个状态停留多久。
但这并不是说没有这个选项的Action就都是只执行一次的,没有这个选项代表这个Action要么只能执行一次,要么是必须每帧执行的。
怎么来判断是否勾选这个选项呢?比如我们有个Action是监控是否有子弹击中玩家的,那么这个Action当然要每帧都执行一遍咯,但如果是发射子弹的Action,通常就不能每帧执行了,仅在“发射”状态被激活时执行一次就可以了。
变量(variables)
变量是用来储存数据/数值的。
Unity3D自身有变量,不同的Component都有很多或私有(private)或公开(public)的变量,PlayMaker可以通过Action去调用它们(Get Property
)或者直接对其赋值(Set Property
)。
PlayMaker自身也有变量,我们叫做Fsm变量,以区别于Unity3D的变量。调用其他Fsm的变量需要用到Get FSM Variable
这个Action,为其他Fsm变量赋值要用到Set FSM Variable
这个Action。
对于本Fsm内部的变量进行操作是最简单的,很多Action都可以读取某个内部变量值或者将某个值储存在内部变量中。
作者:shimmery
链接:http://www.jianshu.com/p/ce791bef66bb
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。