第四部分:Avatar功能的基本构成
很多Avatar的功能确实都很神奇。你可能已经见过了那些功能完备的武器和"特效"。但你的目标很简单,最起码先学会做一个物体开关。接着你在各大视频网站和贴吧论坛找教程,两个小时,昏昏欲睡,最后一步一步跟着做出了一个开关。
But Why?
Avatar的任何功能都基于这样的结构:
Input -> Parameter -> State(Animation)
你进行输入,输入会改变参数,参数满足条件使动画播放
举例,我想要我的手里有一个可以开关的方块,我希望:
当我打开开关的时候,我的手里出现一个方块
当我关闭开关的时候,我的手里的方块消失
而我可以设计一个这样的系统:
有一个参数t用来代表 开 / 关, 初始值是 false (关)
当参数t = true时,打开方块:
- 播放方块c被打开的动画
当参数t = false时,关闭方块:
- 播放方块c被关闭的动画
那么它的逻辑就是:
当我在轮盘菜单里打开方块:
- 变化参数t = true
- 方块被打开
当我在轮盘菜单里关闭方块:
- 变化参数t = false
- 方块被关闭
当在菜单中打开功能时,实际上做的事是将参数t设置为true,参数t是true时播放方块打开的动画
当在菜单中关闭功能时,实际上做的事是将参数t设置为false,参数t是true时播放方块关闭的动画
但你需要知道,输入不仅仅只有菜单。有一些行为也会使得一部分参数变化,你可以未来自己发掘。
开关方块的流程图
如果你理解方块的开关,那么你也能理解这个:
Avatar功能的结构和逻辑关系
Avatar的功能实际上就是利用参数变化和条件来控制不同的动画播放与否
借此来控制不同物体的开关,行为,状态...
什么是动画
动画是Avatar功能中最核心的一环。如果Parameter和逻辑是一台车的车体和传动,那么动画就是发动机和车轮,实际让车动起来的东西。它不仅仅是一系列图片,在Unity中,动画记录任何物体或属性在无数时刻中某一刻的状态,比如:
物体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)... 以此类推。
任何功能都是通过动画执行来达成的
Animation Window,展示一个叫做Sleeve的动画
什么是Parameter
Parameter是Avatar功能中至关重要的一环。它是一台车中的传动系统,要做任何事情都必须通过它来驱动,它记录一个状态。
- 方块是否打开?(是)
- Bool
- 启用第几套服装?(第4套)
- Int
- 颜色变换多少?(35%)
- Float
左图:Animator的Parameter; 右图:VRC Expression Parameters的Parameter
同一个Parameter为什么要写在两个地方
你看过的教程都告诉你要将Parameter写在Animator和Expression Parameters里。写在两处有不同用途:
Animator:用于Animator的过度条件
如果你想使用一个Parameter作为过度的条件,它必须在Animator的参数表里。
Expression Parameter:用于与远端玩家同步,控制Animator的参数以及用于Expression Menu
想象你的游戏在一个盒子中,别人的游戏也在他自己的盒子中,两个盒子有一模一样的场景,物件,安排...等等。你们有在一起的感觉是因为你们看到了对方对这个世界造成的影响。如果要让对方看到你造成的影响,你必须要把信息传递过去:
玩家A推动了方块x 到(0, 5, 0)
如果玩家B始终收不到这条信息,那么玩家B的盒子里永远也不会发生玩家A推动方块这件事情,他就永远看不到这个事情发生,即没有同步。
如果把这个情景换成VRChat的Avatar,情况就是:
玩家A的Avatar上一个名为cube_1的parameter从true变成false
- 方块被关闭了
当一个Parameter被写在了Expression Parameters时,这个Parameter就会被发送给其他人,确保其他人总是会跟上你的Parameter变化。
玩家B收到了玩家A的Avatar上一个名为cube_1的parameter从true变成false
- 玩家B看到你的方块被关闭了
如果Parameter没有被同步给远端玩家,一切变化只会在你的世界里发生,对方什么也看不到。
Parameter同步,本地,远端的细节是高级话题,不在本篇范围内
VRC Expression Parameters界面
Animator主面板和Parameter列表
Transition, Condition和Animator
Animator中你需要手动创建动画层才能开始做任何事情。你可以想象动画层是很多张图片叠在一起,每一层都会执行它当前所在的动画,然后和下一层叠加起来。如果下一层正在执行的动画和上一层的动画影响了同样的东西,那么以下一层的动画为主。
动画层具有权重,这个权重会决定这一层和其他的层如何混合在一起,一般来说会将它设置为
调整动画层的属性,Weight是权重
你的一切功能都必须在Animator的某一层中完成,而一个动画层中可能包括:
- 状态
-
- 代表了当前这一层所处的状态
-
- 动画
-
- 一个 状态 里的动画
-
- 参数
-
- 过度条件以 参数 作为判断条件
-
- 过度
-
- 允许从一个 状态 前往另一个状态
- 条件
- 过度的条件
-
Animator,展示了一个动画层,一些状态和过度条件
任何功能都只不过是不同的动画,Parameter和过度一同运作产生的结果。妥善的构思和设计好你的功能应该如何实现。
构思一个功能
如果你想做一个功能,你就必须构思如何去实现它。
如果我想做一把能收起来,能开火的最基本的枪,这把枪就可以分成这两个部分:
- 这把武器有一个开关来控制它拿出来或者收起来
- 这把武器在我的右手做出开枪手势的时候就开火
那么,首先构思怎么做开关:
用一个Parameter (Bool)来控制武器的开关,当开关 = ture的时候就是把武器拿出来,当开关= false的时候就是收起来
武器开关 = true 时 播放 武器打开的动画
武器开关 = false 时 播放 武器关闭的动画
然后构思怎么做开火:
当右手的手势 = 开枪手势时开火,其他时候什么都不做,而且只有在枪拿出来时才能开火
右手的手势 = 开枪手势 以及 武器开关 = true时 播放开火动画
其中任何一个条件没有满足 时 播放 武器什么都不做
到此,这把枪的功能逻辑就基本构思好了,接下来就去把它实现:
Animator,展示了武器的开关逻辑
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。
Animator界面,展示一个Anystate用途的情景
在这个例子中,理论上永远也无法到达状态C,但将状态C与Anystate连接并且设置条件之后,无论当前是在状态A还是B,只要满足条件就会从Anystate过渡到状态C,但到达状态之后就无法回到状态A和B了,因为没有过度。
VRC Expressions Menu
这是控制Parameter最常见的方式。Expression Menu包含Avatar的轮盘菜单里的内容,用于控制Expression Parameters列表里的Parameter,你可以在一个菜单页面内设置最多8个控制。
虽然一个页面最多只有8个控制,但你可以选择菜单里设置二级菜单,设置三级菜单... 以此类推。除此之外,你也可以选择某个控制展示的文字,图标,类型(按住,切换,转轮... 以此类推) 以及最重要的,它控制的Parameter。