事件(event)的触发是PlayMaker交互设计的基础。
鼠标(mouse)、键盘(keyboard)、按钮(button)触发
这类触发逻辑的原理是:
当用户通过输入设备(比如鼠标、键盘、触碰屏、手柄等)向游戏发送信息时(比如按键被按下、滚轮被拨动等等),触发指定event并通过该event实现状态改变。
这类触发事件的相关动作可以在PlayMaker的Input Actions中找到。
1. 鼠标(mouse)
-
GetMouseButton
单纯检测鼠标按键是否被按下,并将结果储存在一个bool变量中 -
GetMouseButtonDown
在鼠标按键被按下时立刻触发指定event -
GetMouseButtonUp
在鼠标按键被松开时立刻触发指定event
我们可以在PlayMaker中检测到的鼠标按钮包括:Left、Middle、Right、None。
注意:MouseButtonDown不等于鼠标单击。鼠标单击代表快速执行按键的按下和松开两个操作。如果一定需要检测“单击”输入操作,需要使用多个state来设计一个简单的逻辑:
-
State 1
检测按键是否被按下 -
State 2
检测按键是否被松开,但添加了一个短暂的计时器(使用wait
动作),在时限内如果按键松开,单击操作成立,触发LMB down事件 - 如果在短暂的时间间隔内(比如0.1s)按键没有被松开,说明单击操作不成立,触发cancel事件
- 因为cancel事件导致
State 2
休眠,所以之后即便再松开按键也不再会触发LMB down事件了
触发检测到按键松开则跳转到“单击”所触发的目标状态,同时在State 2
中设定一个计时器,设置一个短暂的时间间隔(比如0.1s),一旦超过时间间隔,立即跳转到“
2. 键盘(keyboard)
-
AnyKey
任何按键被按下都会触发event -
GetKey
单纯检测一个按键是否被按下,并将结果储存在一个bool变量中 -
GetKeyDown
在指定按键被按下时立刻触发指定event -
GetKeyUp
在指定按键被松开时立刻触发指定event
指定按键比较麻烦,要从一个很长的列表中选择,这也算是“可视化”带来的弊端之一吧。
3. 按钮(Button)
-
GetButton
单纯检测一个按钮是否被按下,并将结果储存在一个bool变量中 -
GetButtonDown
在指定按钮被按下时立刻触发指定event -
GetButtonUp
在指定按钮被松开时立刻触发指定event
注意,按钮名称需要自行输入,必须严格与Input面板中的名称对应,请注意大小写规范以及是否空格。
按钮(Button)的概念在Unity3D中比较特殊。并不一定专指鼠标按键、键盘或者手柄,而是通过预先的定义可以囊括一系列不同的按键,以方便使用。
从菜单Edit
> Project Settings
> Input
可以打开Input面板:
这里指定了18个不同的Axes,可以理解为“轴向值”。Name
是这个轴向的名称,Positive Button
和Alt Positive Button
中所指定的按键被按下时,该轴向的值迅速增长到1,Negative Button
和Alt Negative Button
中所指定的按键被按下时,该轴向的值迅速减少为-1,无任何按键被按下时,轴向值迅速归0。
而Button就是指的轴向中所规定的所有按键。比如这个叫做Horizontal的轴向,我们可以理解为“横向输入按钮”,当键盘left
、right
键或a
、d
键被按下时,就相当于“横向输入按钮”被按下了。
我们经常会使用到的Button有:
- 使用Horizontal、Vertical来设置运动方向或旋转方向
- 使用Fire1来控制攻击行为
- 使用Jump来控制跳跃
- 使用Cancel来取消或者呼出游戏菜单
- …
我们还可以自己增加更多的Axes。
碰撞(collision)触发
这类触发逻辑的原理是:
Unity3D的游戏物体可以设置碰撞边界(Collider),当有其他的带有collider组件的游戏物体进入/离开/停留这个游戏物体时,可以触发event并由此引起游戏物体的状态改变。
Collision Event行为:
- 当
Collision
中所指定的情况发生,且碰撞对象带有Collide Tag
中所指定的Tag时,触发Send Event
中指定的事件,并可以将碰撞对象储存为一个GameObject类型变量,将碰撞力量储存为一个Vector3类型变量 -
On Collision Enter
、On Collision Stay
、On Collision Exit
三个选项针对其他碰撞体collider进入、停留、离开本碰撞体的状况 -
On Controller Collider Hit
选项针对Controller类型的碰撞体与本碰撞体碰撞的状况 -
On Particle Collision
选项针对与粒子物体发生碰撞的状况
Get Collision Info行为:
- 这个行为可以紧接着Collision Event行为使用,用来储存更多关于碰撞的信息,比如:碰撞物体、相对速度(vector3)、相对速度(float)、碰撞点坐标(vector3)、碰撞点法线方向(vector3)、物理材质名称。
在Collider组件上,我们可以勾选Is Trigger
选项来将碰撞体变成一个触发器(trigger)。触发器和碰撞体的区别在于,触发器不会实际阻挡其他碰撞体,相当于一个虚拟的开关。
如果设置成了Trigger,不能使用Collision Event和Get Collision Info行为了,而要使用Trigger Event和Get Trigger Info行为,用法是一样的。
Trigger Event
Get Trigger Info
通常适合使用碰撞触发来制作的交互逻辑有:
- 拾取游戏物体:
将道具放置在场景中,设置成trigger类碰撞体,玩家进入碰撞体范围时触发相应事件 - 玩家“获得”物品
- 道具模型消失
- 游戏积分增加
- …
- 启动机关:
当碰撞发生时机关被触发,可以是“开门”、“启动升降机”、“进入下一关”等等 - 伤害、死亡:
当“子弹”物体与对象发生碰撞时,对对象造成伤害或者让对象死亡 - 特效:
当“子弹”击中对象时,从“伤口”处发射粒子特效(血、烟雾、碎片等等)
射线(raycast)触发
这类触发逻辑的原理是:
从目标点向特定方向发射一条光线(ray),然后获得这条光线与场景物体的碰撞信息。通常是从某个游戏物体沿着特定轴向(x、y、z)发射,或者是摄影机平面上某一点垂直于摄影机平面向场景发射。
Raycast是即时发生的,不论距离多远,都会在同一帧返回碰撞结果,所以适用于需要立即得到反馈的情况。
Raycast:
Raycast从一个游戏物体(From Game Object
)或某个点(From Position
)沿着特定坐标空间(Space
)某个方向(Direction
)发射一条长度为Distance
单位长度的探测光线,并将结果储存在相应的变量中:
-
Hit Event
:如果碰到游戏物体,则触发相应事件 -
Store Did Hit
:是否碰到游戏物体(bool) -
Store Hit Object
:碰撞对象物体(GameObject) -
Store Hit Point
:碰撞接触点(Vector3) -
Store Hit Normal
:碰撞法线方向(Vector3) -
Store Hit Distance
:发射点距离碰撞点距离(float) -
Repeat Interval
:射线发射频率(0代表仅发射1次;1代表每帧发射一次;2代表每两帧发射一次…) -
Layer Mask
:可以指定仅探测与特定层中的物体之间的碰撞 -
Debug
:可以在视图中显示一条直线方便我们判断raycast是否正确
注意:ray是有长度的,在这个长度范围以内如果没有触碰到物体,那就是“没有击中”。这个长度默认为100米,如果场景过大有可能会出现距离不够而不能正确返回结果的情况。同时特定情况下也可以将这个值缩小以获得特定效果,比如游戏设定手枪射距为30米的话,就要将ray设成30。
Raycast All:
Raycast All和Raycast用法几乎完全一样,但Raycast All会返回Ray碰撞到的所有游戏物体并返回为一个GameObject类型的数组(array)变量Store Hit Objects
Get Raycast Hit Info:
Get Raycast All Info:
这两个行为用在Raycast行为之后用以获得更多的碰撞信息。
有一些action同样基于Raycast原理,但名字中没有“ray”这个词,也没有被归类在Physics动作组中。
Mouse Pick:
Mouse Pick会由鼠标屏幕二维坐标转换成摄影机平面二维坐标,再将这一坐标对应的三维空间位置沿着垂直于摄影机平面的方向发射一条长度为Ray Distance
的ray,并返回碰撞结果为相应数据类型的变量值。
Mouse Pick Event:
Mouse Pick Event同样基于上述原理,是用来查看鼠标是否在特定游戏物体(Game Object
)上,并根据不同的鼠标状态而触发不同的事件:
-
Mouse Over
:鼠标划入游戏物体范围内 -
Mouse Down
:鼠标在游戏物体范围内按下左键 -
Mouse Up
:鼠标在游戏物体范围内松开左键 -
Mouse Off
:鼠标从游戏物体范围内划出
Mouse Pick 2d与Mouse Pick 2d Event这两个行为与上两个行为完全一致,只不过专门适用于2D游戏,因为它们返回的位置坐标数据类型是Vector2而不是Vector3
通常适合使用碰撞触发来制作的交互逻辑有:
- FPS类游戏中的设计判定,使用Raycast来判断是否击中目标,比发射子弹后再判断碰撞要准确得多
- 获得鼠标指向位置,并通过这个位置来指引角色运动
- 通过鼠标与游戏物体进行互动
数据触发
关于“数据”和“数据格式”的详细介绍,可以参看《Unity3D的变量及数据类型》。
数据触发的基本原理是:
通过监控数据的数值状态或数值变化,并使用各种数值比较action来触发不同的事件。
Bool Test检查一个bool变量的值,然后根据“True/False”可以触发最多2个不同的event
Int Compare和Float Compare检查两个不同的int数值或者float数值,根据“第一个数值等于/小于/大于第二个数值”可以触发最多3个不同的event
String Compare检查两个字符串是否一致,根据“一致/不一致”可以触发最多2个不同的event
Object Compare检查两个Object(也就是组件)是否一致,根据“一致/不一致”可以触发最多2个不同的event
Object Compare检查两个数组是否一致(一致的意思是A数组中每一个数据都等于B数组中对应序号的数据),根据“一致/不一致”可以触发最多2个不同的event
Enum Compare检查两个Enum状态是否一致,根据“一致/不一致”可以触发最多2个不同的event
Fsm State Test检查特定游戏物体上某个FSM是否正处于某个特定State,根据“True/False”可以触发最多2个不同的event
这些检查都可以将结果储存进一个bool变量中(
Store Result
)。
Every Frame
选项如果被勾选,则代表该action会一直不断执行,也就是实时监控数据是否满足比较标准。如果不被勾线,则只在刚进入该state时执行一次数值比较。
在这些情况下我们通常使用这种数值触发来设计交互逻辑:
- 角色离某个NPC的距离小于特定数值的时候,角色吸引NPC的注意,NPC开始追逐角色
- 角色生命值小于等于0的时候,角色死亡
- 当时间间隔超过特定数值的时候,开始执行某种操作
- 当对象处于特定state(比如攻击僵直)的时候,指令输入不起作用