Avatar 与你

Avatar 与你是一篇面向新开发者,为新开发者解明在 Avatar 开发中常见元素基本概念的指导性文章。


封面图标:Prosymbols Premium

第零部分:开始之前

谁合适这篇文章

稍早前你上传了你的第一个 Avatar,但你的野心十分庞大,你并不满足于一个普通的原厂 Avatar

你想要更多更多,还有更多...

        如果以上描述听起来符合你,那么你适合这篇文章。

学习成果

        本篇章以学习结果为导向,向你(一个刚刚开始接触 Avatar 开发的创作者)介绍 Avatar 的基本概念。在完成本篇的学习后,预期你会了解 Avatar 的基本结构以及 Avatar 功能的基本结构。由此,你将具备对 Avatar 的清晰认知,以便进阶学习以及不依赖脚本的情况下独立开发Avatar的基本能力。

前置知识

        在阅读本篇前,你必须具备以下前置知识:

        在阅读本篇时,你应当体现以下个人基本能力:

本篇着重于概念的理解和学习,不教授具体的操作和具体细节

本篇章主要以英文Unity Editor界面为准

如对部分名词有疑问,你可参阅本篇附录的术语对照表,VRCD的术语对照表或使用机器翻译工具

本篇章不包括带引导的实作以及高等级的技术细节

为方便理解,部分条目会使用自创词组和概念。

-- 严正警告 --

对任何初级开发者的严正警告

     如果你想要学习如何开发一个功能强大性能优良的 Avatar,在你透彻了解 Avatar 的运作前,不要使用任何自动脚本:

在透彻了解 Avatar 前,不要使用任何自动插件

在透彻了解 Avatar 前,不要使用任何自动插件

在透彻了解 Avatar 前,不要使用任何自动插件


自动插件指的是能够自动将符合某种规格的预制件自动安装到 Avatar 上的 Unity 插件,例如 Modular AvatarVRCFury

 

 

为什么在初级阶段不应该使用自动脚本

     在初级阶段使用自动脚本组装 Avatar 将阻断你获取操作经验的可能,你本该了解的技术细节将被跳过并由脚本替你完成。这相当于用ChatGPT帮助你生成你的英文作文

     你将不具备在脱离脚本后处理 Avatar 问题的能力,并且当脚本出错或没有按照预期工作时,你将不能自行解决问题,并且你将难以从其他开发者处获取帮助

     当你了解了 Avatar 的工作原理后,你可以随意使用此类工具。

 

任何自动工具仅能以为你提供便利的目的融入你的工作流,而不是取代你的某一部分工作

第一部分:你的 Avatar 文件

     现在你打开了 Unity Editor 准备开始大展拳脚,首先你需要导入你的 Avatar。

Avatar 的文件形式

     很有可能你已经从某处得到了一个 Avatar:

     这一切的结果都是你得到了一个 unitypackage 或者压缩包,比如:

     除此之外,也可能单纯的只是:

对于你目前的阶段而言,你不需要深入了解压缩包里的其他内容。如果你准备好了,可以直接前往下一个章节或继续往下看来浅薄的了解压缩包里的其他文件是什么。

压缩包里的东西

     这对你目前的阶段来说并不重要,但你依然可以浅尝即止的了解你在一个压缩包中看到的东西都是什么。我们还是以 Maya_Ver1.02.zip 为例。

你知道吗,Maya_ver1.02.zip 是我的资源库中最早存在的文件之一,所以我选择它为例子 ;)

Maya_Ver1.02.zip

     像这样的文件结构,通常是一个在 Booth.pm 出售的 Avatar。

在当前阶段,你不需要了解全部文件有什么用,你只需要 .unitypackage 即可

第二部分:Avatar的基本构成

这个部分将简单讲解一个 Avatar 的基本构成,关于功能的构成请参阅下一章节

     现在你已经准备好了 SDK,可以着手看看你的 Avatar 了。如果你还记得,刚才提到你的 Avatar 是一个类似这样的文件:

     我比较喜欢我自己的 Avatar,所以我会用我自己的 Avatar 为例来解释:

image.png

     现在将 .unitypackage 导入到你的 Project 里,你会找到你的 Avatar 所属的文件夹。不同的创作者会有不同的命名习惯和摆放资源的习惯,通常在这些包中你会找到至少一个 Avatar 的 Prefab,将它拖到场景中。

操作正确时,如图所示

确认你放入场景中的是 Prefab 而不是 FBX

Nebbia 在一个 Scene中                  

Avatar 的基本构成

     以 Nebbia 为例,这个Avatar的结构如下:

     其中:

     我是李华我的体内有206块骨头形成了我的骨架三个月前我摔断了我的手臂骨折的骨头穿透了我的肌肉皮肤,前几天刚痊愈。

     由此,如果你想要 Avatar 作出一个动作,比如抬起右手,你就需要旋转 Avatar 的右肩右手臂手掌这几块骨骼来摆出抬起右手的姿势。而身体会因为骨骼的运动而拉伸和位移,最终展示出你看到的 Avatar 抬起右手。

骨架 这个称呼并不意味着 Armature 仅仅指代人体骨架

任何 Mesh 都可以通过骨头来控制变形和位移,而任何操控 Mesh骨头都归于 Armature

Armature 虽然看起来像是一个普通的 Object,并且操作起来也像一个普通的 Object,但它有着比普通 Object 更特殊的性质,在这一阶段中你暂时不需要知道更深入的细节

你知道吗,即使骨头不在 Armature 内,也可以正常的操控 Mesh。即使如此,骨头所属的 Armature 必须存在,不能被移除

为什么一切都基于 Hips

     如果你的观察足够仔细,你会发现图中展示的骨头叫 Hips,其他骨头都是这个 Hips 的一部分。对于 Avatar 的人体骨骼结构而言,Hips 是最大的父。任何其他的骨头都是 Hips 的子项。这意味整个 Avatar 都是基于 Hips 构建起来的:

     最大的父:一个根物体,里面包含其他东西的物体。例如 Nebbia 就是整个 Avatar 最大的父是整个 Avatar 的根部

image.png

展示Avatar骨骼结构的Hierarchy面板,其中所有的骨骼都是 Hips 的一部分

     对于这一点的解释是:在 3D 建模中,骨架层级通常以 髋骨 Hips 作为基础,这是因为髋骨在人体骨骼中的位置相当于整个身体的中心轴点。在进行角色动画制作时,髋骨作为根骨骼能够提供稳定的基础,确保其他骨骼能够按照正确的层级和关系进行动作。

     此外,由于髋骨位于身体的中心,围绕髋骨设置的层级结构可以更自然地模拟人体的运动,如行走、跳跃等,使得动作看起来更加流畅和自然。因此,将髋骨作为骨架层级的起点,有助于动画师更有效地控制和动画整个角色。

如果你想进一步了解3D角色的 Rigging,可参阅 Rigging Characters | Hierarchies and Character Animation | Peachpit

     另一边,Armature 虽然看起来是最大的父,包含了所有的骨头,但它只是骨架这个概念本身,它并不实际控制 Mesh,它是一种特殊的 Object,用于创建和管理骨骼结构。

如果你打算让所有人离开,你不会驱散人群这个概念,而是驱散张三,李四和王五

如果你想了解更多关于 Armature 的内容,可参阅 Armatures - Blender 4.1 Manual

VRCD 的入门包中对骨骼和骨架有另一种解释,如果你觉得以上的解释理解起来有些困难,可参阅 二、骨骼 | VRCD 文档库

如何定义一个人

     不出意外的话,你会希望你制作的 Avatar 是一个人形,很多常理上对 Avatar 的预期都以人形为准。

     在 Unity 中,一组物体是否为人形由它的 Avatar 定义。但Avatar 非彼 Avatar

     一般来说,你得到的VRC Avatar 都是 Upload Ready,即已经完成了几乎所有配置,包括 Avatar。所以在这个阶段你不需要在意 Avatar

但如果你出于某些原因需要编辑你的 Avatar Mapping,你可以在 Avatar 上找到 Animator Component,在那里你可以找到你的 Avatar,或者你也可以将你的 Avatar 的 FBX 展开来找到你的 Avatar

如果你在其他软件软件修改了骨架结构并覆盖到 Unity 内,你的 Avatar 可能会出现错误并需要调整配置

FBX 和 Prefab

     在 Asset Window 浏览文件的时候你可能会对一些资源的类型有所疑问

image.png

Asset Window

图中展示了1.Prefab 2.Material 3.FBX 以及 FBX 展开的内容

Prefab

     Prefab 就像模型玩具中一组 预先组装好的部分,比如一条手臂,这样你就可以直接将2个组装好的手臂拼装到身体上,而不用在身体从肩膀开始一点一点上拼装手臂。例如,如果你需要在游戏世界中多次创建 相同的房子,你可以 将这个房子制作成一个Prefab,然后像复制粘贴在游戏世界的不同位置。

FBX

     FBX 文件就像是一个旅行箱(一包文件)。当你需要去不同的地方(不同的软件)旅行时,你可以将你的衣服、鞋子和护照等物品(Bone, Mesh, Texture, Material...)都打包到这个旅行箱里。然后,不管你到了哪里,只要打开这个旅行箱,你需要的东西都整齐地放在里面,可以立即使用。

     至于 Material,就是在接下来的部分讲了。

Texture, Material, Shader 与 Mesh

     你很有可能已经发现了,你没法直接将一张贴图从 Asset Window 中拖动到某个东西上来改变它的外观。这是因为描述一个东西的外观不单单只是一张贴图的事情。

     如果你要让别人想象到一个不在场的物体,你会描述这个物体的形状颜色材质 / 质感是如何的:

苹果是圆形的,红色的,表面是大体光滑的,有一些斑纹

     在 Unity 中你需要定义一个物体的形状颜色材质 / 质感

这个玩具是方形的,绿色的,表面粗糙哑光

     而一个可见的物体由 Mesh, Material, Texture, Shader 共同组成。

     而这些东西的关系是这样的:

     这些东西组合在一起,最终呈现出这个可见的物体。

     如果用同样的方式去描述一个 Avatar 的脸:

     如果你的 Avatar 是一个 FBX 模型,则 Mesh 存放于在 FBX 中Material 和 Texture 则来自你从别处导入 Unity Editor在 Unity Editor 中创建。

     如果你想要变化你的 Avatar 的外观,你需要做的就是调整 Avatar 上某个 Mesh 的 Material,Material 使用的 Shader 和 Texture。但如果你想要修改 Mesh 或者 Texture,你可能需要使用外部软件,如 Blender 和 Adobe Photoshop

VRC Avatar Descriptor

      VRC Avatar Desciprtor 是一个 Unity 组件(Component)。它是你的 Avatar 最核心,最重要的组件。这个组件定义了一组会被作为 Avatar 的 Object ,包含 Avatar 的一些特性,比如:

口型

眼动

Playable Layers

追踪与动画

轮盘菜单

基础 Physbone Collider

      只有当某个 Object 有 VRC Avatar Descriptor 的时候,才会被识别为一个 Avatar,最终允许被上传而你的 Avatar 会根据你设定的 VRC Avatar Descriptor 运行。

image.png

Inspector,展示了 VRC Avatar Descriptor的一部分

关于 VRC Avatar Descriptor 的细节不属于本篇范畴

第三部分: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 里,写在两处有不同用途:

 如果你想使用一个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和其他组件的逻辑关系

结语

No,我没想好结语写什么,也许不需要这个部分。

附录:关键词对照表

原文

常见中文代称或解释以及欠标准的译名和名词

自动脚本,自动插件

能够自动将符合某种规格的预制件自动安装到 Avatar 上的 Unity 插件

例如 Modular Avatar 和 VRCFury

Avatar

模型皮肤,外观,头像

VRChat Creator Companion

VCC,工程管理器

Armature

骨架

Mesh

模型,网格

Texture

材质,贴图,纹理

Material

材质球,材料球

Shader

着色器,光影

Animator

动画器,状态机,动画师

Animation

动画

(Unity) Editor

Unity,Unity 编辑器,编辑器

(Unity) Project

工程,工程文件

World

世界,地图

Prefab

预制体,预制件

Hierarchy

场景列表,列表

Hips

髋部腰部胯部,臀部

大腿骨和骨盆相连的骨骼部位

Upload Ready

完整,完备

即刻可以上传的

VRChat

VRC

Object

物体,组件

Component

组件脚本

在 Inspector 中添加给物体的组件

Asset Window

资产窗口,资源窗口

显示了你 Projec t中所有资源的窗口

Inspector

检查器,信息窗口

Skinned Mesh

蒙皮网格,蒙皮

被骨头控制的Mesh

liltoon

一款常用的 Shader,以功能完备著称

常用于 Booth 上的商品和 Upload Ready 的 Avatar

poiyomi toon

一款很多人使用的shader

以难以维护和极其强大和多样的功能著称

Renderer

渲染器

一个用来渲染某个Mesh的组件,有Mesh和Skinned Mesh两种

VRC Avatar Descriptor

Avatar的核心组件

Playable Layers

VRC Avatar Descriptor的一部分,Avatar不同用途的的5个Animator

Physbone Collider

动骨碰撞碰撞

一个让它所在的物体可与Physbone进行碰撞的Component

Physbone

动骨,新动骨

允许这个脚本所在的物体及其所有子项变成一个有物理效果的链条,特指VRChat Avatar Dynamics更新中取代Dynamic Bone的系统

Parameter

参数

Expression Parameters

参数表,同步参数表,同步表,参数

Expression Menu

菜单,轮盘菜单

Remote Player

远端玩家,其他人,其他玩家

自己之外的任何人

Local Player

,自己

低头看到的那副身体

State

状态,动画

Animator里的一个方块,代表一个状态,里面可以包含一个 Animation

Transition

过度,动画过渡

Condition

条件,过度限制

Weight

权重,重量影响

注:译文中红色部分是错误的说法,蓝色部分存在歧义