Skip to main content

第三部分:Avatar功能的基本构成


     很多Avatar的功能确实都很神奇。你可能已经见过了那些功能完备的武器和"特效"。但你的目标很简单,最起码先学会做一个物体开关。接着你在各大视频网站和贴吧论坛找教程,两个小时,昏昏欲睡,最后一步一步跟着做出了一个开关。

But Why为什么要这么做?

     Avatar的任何功能都可以用输入输出法描述:

Input -> Parameter --[Condition]--> State(Animation)

通过某种方式进行输入输入会改变参数参数使得条件满足进入不同的状态,而对应状态内的动画播放

 

动画决定了 Avatar 的实际表现

一个简单的开关动画是如何组成的

     任何功能都可以用 Input -> Parameter --[Condition]--> State(Animation)的方式来描述,只是复杂度的不同而已。

     如果我想要制作一个可以通过轮盘菜单开关的方块,并且我希望这个功能是这样的:

当我打开开关的时候,出现一个方块

当我关闭开关的时候,方块消失

     那么,这个功能运作起来就是这样:

设一个布尔值为参数,姑且称这个参数为 cube_IsOn 意思是"cube是打开的"用来代表方块的  /  初始值是 false (关)

参数 cube_IsOn = true 时,播放一个方块出现的动画 这样,方块出现了

参数 cube_IsOn = false 时,播放方块隐藏的动画 这样,方块消失了

     用输入输出法来描述,看起来像这样:

Input -> Parameter --[Condition]--> State(Animation)

在轮盘菜单拨动开关 -> cube_IsOn --[true // false]--> 方块打开(方块出现的动画) // 方块关闭(方块隐藏的动画)

      用伪代码的形式来表达,这个可以通过轮盘菜单开关的方块则是:

If (cube_IsOn = true){

打开方块();   //播放一个打开方块的动画

};

If (cube_IsOn = false){

隐藏方块();   //播放一个隐藏方块的动画

};

输入不仅仅只有轮盘菜单。有一些行为也会使得一部分参数变化,你可以未来自己发掘。

 

更加复杂的功能与逻辑

     如果你能理解一个最基本的开关方块的逻辑是如何的,那么相信你已经多少有点理解了 Avatar 的功能实际上就是利用参数的变化状态之间的过度条件来控制不同的动画播放与否,并借此来控制不同物体的开关,行为,状态。

 

e5869904a31c6b6ecb0ed5975c8169db.png

开关方块的流程图

     如果你认为你已经初步把握了功能的组成,你可以看一看这个简易的"或"逻辑是如何进行的:

65ec7924e9694ae22be872f32a456991.png

     假设一切从 A 开始。

     当条件 B 满足时,就播放 B

     当条件 C 满足时,就播放 C

     以先达到条件的为准。比如先播放了 C,即使条件 B 随后也满足了,仍然保持 C 的播放。

     假设正在播放 C,而条件 B C 都满足,只有等到条件 C 不满足了,才会重新回到 A,然后开始播放 B

一个3状态2条件的 Avatar 功能的结构和逻辑关系         

   如果你不能轻易的理解,那么可以自行在 Unity 编辑器中制作一个相同的逻辑,然后进入 Play Mode 自行把玩

 

什么是动画

     在 Unity 中的动画和你想象的动画略有不同。在传统的动画影视作品中,一个 Keyframe 关键帧指的是一张手动绘制出来的图像。而在 Unity 中,一个 Keyframe 记录的的是一个特定时间点时,某个属性的值

image.png

传统动画影视作品的关键帧,一张展示手绘原画与关键帧关系的图像,出处请参见此

     比如,一个 Unity 的动画包含的内容可能是一个方块在初始的一刻处于 (0,0,0) 的位置,并且在1秒后处于 (0,5,0) 的位置,即向上移动了5单位的距离。而从初始到1秒之间的过程,Unity会为你平滑地过渡从 (0,0,0) 到 (0,5,0) 的移动过程;或者,一个 Unity 的动画包含的内容可能是一个角色的手臂沿着某个方向 (X/Y/Z) 旋转了45°,即在初始的旋转角度为(0°,0°,0°),而在2秒后旋转角度变为(0°,45°,0°)。

 

image.png

Animation Window,展示一个叫做 Sleeve 的 Object 在初始时处于打开状态(=1),并且在第一帧时还有一个不明的状态(=?)

什么是Parameter

     Parameter 是 Avatar 功能中至关重要的一环,Avatar 的所有状态和动画都依赖 Parameter 来决定播放与否。在 VRChat 环境下,包含 Bool (true // false)Integer (0,1,2,3,4,5,6...)Float (0.12, 0.531, 0.7442, 0.000, 1.000...)

     而 Parameter 可以控制状态何时过渡,从而决定何时播放什么动画。 Parameter 是在 Animator Controller 中的变量,可以通过轮盘,Contact 或其他方法来改变 Parameter。

image.png       image.png

左图:Animator 的 Parameter; 右图:VRC Expression Parameters 的 Parameter

同一个Parameter为什么要写在两个地方

     你看过的教程应该都会告诉你要将 Parameter 写在 Animator 和 Expression Parameters 里,写在两处有不同用途:

  • Animator:用于 Animator 的过度条件
  • Expression Parameter:用于与远端玩家同步,控制 Animator 的参数以及用于 Expression Menu

 如果你想使用一个Parameter作为过度的条件,它必须在 Animator 的参数表里才能被 Animator 使用

     想象你的游戏在一个盒子中,别人的游戏也在他自己的盒子中,两个盒子有一模一样的场景,物件,安排...等等。你们有在一起的感觉是因为你们看到了对方对这个世界造成的影响。如果要让对方看到你造成的影响,你必须要把信息传递过去

玩家A推动了方块 x 到(0, 5, 0)

     如果玩家B始终收不到这条信息,那么玩家B的盒子里永远也不会发生玩家A推动方块这件事情,他就永远看不到这个事情发生,即没有同步

     如果把这个情景换成 VRChat 的 Avatar,情况就是:

玩家 A 的 Avatar 上一个名为 cube_IsOn 的 parameter 从 true 变成 false。

cube IsOn 的变化使得 Animator 进入了一个方块被关闭的状态,播放了一个关闭方块的动画,使得方块被关闭了

     当一个Parameter被写在了Expression Parameters时,这个Parameter就会被发送给其他人,确保其他人总是会跟上你的Parameter变化

玩家 B 收到了玩家 A 的 Avatar 上一个名为 cube_IsOn 的 parameter 从 true 变成 false

cube IsOn 的变化使得 Animator 进入了一个方块被关闭的状态,播放了一个关闭方块的动画,使得方块被关闭了

而这个变化的信息被发送给了玩家 B ,玩家 B 看到你的方块被关闭了

     如果 Parameter 没有被写在 Expression Parameters 里并勾选 Sync,就不会被直接同步给远端玩家,一切变化只会在你的世界里发生,对方什么也看不到

     如果 Parameter 没有被写在 Animator 里,则这个 Parameter 无法被 Animator所使用。

对于同一个 Parameter,Animator 和 Expression Parameter 中的名称必须完全一致,这样才能使正确的 Parameter 被同步

Parameter同步,本地,远端的细节是进阶内容,不在本篇范围内

image.png

VRC Expression Parameters界面

image.png

Animator主面板和Parameter列表

Transition, Condition和Animator

     Animator中你需要手动创建动画层才能开始做任何事情。你可以想象动画层是很多张图片叠在一起每一层都会执行它当前所在的动画,然后和下一层叠加起来。如果下一层正在执行的动画和上一层的动画影响了同样的东西,那么以下一层的动画为主。动画层具有权重,这个权重会决定这一层和其他的层混合的比重,一般来说会将它设置为 1。

 

image.png

调整动画层的属性,Weight是权重

     你的一切功能都必须在 Animator 的某一层或多个层协作完成,而一个动画层中可能包括:

  • 状态
      • 代表了当前这一层所处的状态
  • 动画
      • 一个 状态 里的动画
  • 参数
      • 过度条件以 参数 作为判断条件
  • 过度
      • 允许从一个 状态 前往另一个状态
    • 条件
      • 过度的条件

 

image.png

Animator,展示了一个动画层,一些状态和过度条件

 任何功能都只不过是不同的动画,Parameter和过度一同运作产生的结果。妥善的构思和设计好你的功能应该如何实现

构思一个功能

如果你想做一个功能,你就必须构思如何去实现它。

如果我想做一把能收起来能开火的最基本的枪,这把枪就可以分成这两个部分:

  • 这把武器有一个开关来控制它拿出来或者收起来
  • 这把武器在我的右手做出开枪手势的时候就开火

那么,首先构思怎么做开关:

用一个Parameter (Bool)来控制武器的开关,当开关 = ture的时候就是把武器拿出来,当开关= false的时候就是收起来

武器开关 = true  播放 武器打开的动画

武器开关 = false 时 播放 武器关闭的动画

然后构思怎么做开火:

右手的手势 = 开枪手势时开火,其他时候什么都不做,而且只有在枪拿出来时才能开火

右手的手势 = 开枪手势 以及 武器开关 = true时 播放开火动画

其中任何一个条件没有满足  播放 武器什么都不做

 到此,这把枪的功能逻辑就基本构思好了,接下来就去把它实现:

image.png

Animator,展示了武器的开关逻辑

image.png

Animator,展示了武器的开火逻辑

Entry, Exit和Any State

     Entry, Exit和Anystate是Animator中的一些特殊状态,它们有不同于一般状态的功能

 

Exit

     Exit是一个出口,当过渡到这个State离开的时候,就会从Entry再开始。你需要注意的是,任何动画层都不一定最后必须从Exit离开只要达到了目的,从Exit离开与否并不重要

 

Entry

     Entry是Animator中一层开始的地方,当一个Avatar加载完成时或者经由Exit离开时,就会从这里开始。

 

Anystate

     这是最特殊的一个State。一般来说,State之间必须有Transition才能进行过度。但如果某一个State和Anystate相连,只要满足了条件无论当前是在哪个State上都会过渡到与Anystate相连并且满足条件的State。

image.png

Animator界面,展示一个Anystate用途的情景

     在这个例子中,理论上永远也无法到达状态C,但将状态C与Anystate连接并且设置条件之后,无论当前是在状态A还是B,只要满足条件就会从Anystate过渡到状态C,但到达状态之后就无法回到状态A和B了,因为没有过度

VRC Expressions Menu

     这是控制Parameter最常见的方式。Expression Menu包含Avatar的轮盘菜单里的内容,用于控制Expression Parameters列表里的Parameter,你可以在一个菜单页面内设置最多8个控制

     虽然一个页面最多只有8个控制,但你可以选择菜单里设置二级菜单,设置三级菜单... 以此类推。除此之外,你也可以选择某个控制展示的文字图标类型(按住,切换,转轮... 以此类推) 以及最重要的,它控制的Parameter

Expression Menu只可以控制在Expression Parameters表内的Parameter

image.png

VRC Expression Menu的界面

Expression Menu和其他组件的关系看起来像这样:

f7c5ea84645d0e32b954a7d78d4d9f0d.png

Expression Menu和其他组件的逻辑关系