Skip to main content

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


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

But Why?Why为什么要这么做?

     Avatar的任何功能基于这样的结构可以用输入输出法描述

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

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

 

动画决定了 Avatar 的实际表现

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

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

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

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

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

 而我可以设计一    那么,这功能运作起来就是这样的系统

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

参数t cube_IsOn = true时true 时打开播放一个方块方块:出现

  • 播放方块c被打开的动画 这样,方块出现了

参数t cube_IsOnfalse时false 关闭方块:

  • 播放方块c被关闭隐藏的动画 这样,方块消失了

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

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

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

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

If (cube_IsOn = true我在轮盘菜单里){

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

    };

  • 变化参数t

    If (cube_IsOn = true

    • 方块被打开

我在轮盘菜单里关闭方块:

  • 变化参数t = false
    • 方块被关闭

当在菜单中打开功能时,实际上做的事是将参数t设置为true,参数t是true时播放方块打开的动画){

当在菜单中关闭隐藏功能时,实际上做的事是将参数t设置为false,参数t是true时方块();   //播放一个隐藏方块关闭的动画

};

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

e5869904a31c6b6ecb0ed5975c8169db.png 

更加复杂的功能与逻辑

     如果你能理解一个最基本的开关方块的流程图

逻辑是果你理解方块开关,那么相信也能已经多少有点理解这个

Avatar

65ec7924e9694ae22be872f32a456991.png

Avatar功能结构和逻辑关系

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 中的动画是Avatar功能中最核心和你想象一环。如果Parameter和逻辑是一台车的车体和传动,那么动画就是发动机和车轮,实际让车动起来略有不同。在传统东西。它不仅仅是一系列图片,在Unity中,动画影视作品中,一个 Keyframe 关键帧指的是一张手动绘制出来的图像。而在 Unity 中,一个 Keyframe 记录任何的的是物体一个特定时间点时,某个属性的值无数时刻中某一刻状态比如:

物体A 在开始的瞬间处于位置 (0, 0, 0)

物体A 在第三帧时处于位置 (1, 0, 0)

或者

物体A的位置:

帧1:处于(0, 0, 0)

帧2:处于 (0.5, 0, 0)

帧3:处于 (1, 0, 0)...

 如此一来,当这个动画开始播放的时候,起始时,物体A会移动到位置(0, 0, 0),第二帧时移动到(0.5, 0, 0),第三帧移动到(1, 0, 0)... 以此类推

任何功能都是通过动画执行达成image.png

传统动画影视作品的关键帧,一张展示手绘原画与关键帧关系的图像,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的动画 Sleeve 的 Object 在初始时处于打开状态(=1),并且在第一帧时还有一个不明的状态(=?)

什么是Parameter

 Parameter是Avatar功    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。
    • Bool
  • 启用第几套服装?(第4套)
    • Int
  • 颜色变换多少?(35%)
    • Float

image.png       image.png

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

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

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

  • Animator:用于Animator的 Animator 的过度条件

  • Expression Parameter:用于与远端玩家同步,控制 Animator 的参数以及用于 Expression Menu
  •  如果你想使用一个Parameter作为过度的条件,它必须在Animator的 Animator 的参数表里才能被 Animator 使用

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

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

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

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

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

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

      cube

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

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

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

      cube

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

      而这个变化的信息被发送给了玩家B看 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的 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和其他组件的逻辑关系