Skip to main content

UVU 的工作逻辑(未完成)

UVU(UdonVoiceUtils)基于作者 Guribo 在UdonUtils 中设计的 MVC 模式运行,分为:

  • PlayerAudioController:MvcBase.Controller 的子类,负责接收用户输入、处理业务逻辑,并协调模型和视图的交互,是游戏逻辑的核心调度中心。
  • PlayerAudioView:MvcBase.View 的子类,负责将数据模型(Model)的变化实时反映到视觉表现上,同时通过控制器(Controller)协调用户交互逻辑。
  • PlayerAudioConfigurationModel:MvcBase.Model 的子类,负责封装业务数据,管理数据变更状态,协调数据更新通知,保证资源生命周期安全。

MVC 模式代表 Model-View-Controller(模型-视图-控制器) 模式。这种模式用于应用程序的分层开发。

  • Model(模型) - 模型代表一个存取数据的对象。
  • View(视图) - 视图代表模型包含的数据的可视化。
  • Controller(控制器) - 控制器作用于模型和视图上。它控制数据流向模型对象,并在数据变化时更新视图。它使视图与模型分离开。

image.png

Tlp Base Behaviour

我们首先需要了解 Udon Sharp Behaviour 的生命周期:

  1. OnEnable 准备开始工作。

  2. Start 进行初始化设置(只执行一次)。

  3. OnDeserialization, OnOwnershipTransferred 等(网络事件): 处理网络相关的事情,比如接收数据、所有权转移。

  4. FixedUpdate 进行物理计算,比如重力、碰撞预测(这个时间间隔是固定的)。

  5. AnimationEvents 处理动画事件(第一次机会),比如动画播到某个关键帧触发某个动作。

  6. CollisionEvents 处理碰撞检测,比如玩家碰到了什么东西(OnPlayerCollision...),或者物体之间撞上了(OnCollision...),或者物体进入了触发器区域(OnTrigger...)。

  7. InputEvents 处理玩家输入,比如玩家按了跳跃键(InputJump),或者捡起了东西(InPickup)。

  8. Update 进行主要的逻辑更新,比如移动非物理物体、处理游戏逻辑(这个时间间隔不固定,取决于帧率)。

  9. AnimationEvents 再次处理动画事件(第二次机会)。

  10. LateUpdate 在 Update 之后进行一些收尾工作,比如调整摄像机位置(因为摄像机通常要跟着玩家走,等玩家位置在 Update 里确定后再调)。

  11. PostLateUpdate 所有更新都完成后,这时玩家的位置等信息是最新、最准确的时刻。适合做一些需要绝对精确位置的操作。

  12. RenderingEvents 处理跟画面渲染相关的事件,比如物体进入摄像机视野(OnBecameVisible),或者进行后期图像处理(OnRenderImage)。

  13. OnDisable 停止工作(比如脚本被禁用或物体被隐藏)。

  14. OnDestroy 被彻底拆解销毁(比如物体被删除),清理所有资源。

svg_1.png

官方解释图表:事件执行顺序 |VRChat Creation

Guribo 编写了 Tlp Base Behaviour ,对 Udon Sharp Behaviour 的功能进行了扩展,以便于通过扩展出来的功能实现完备的 MVC 结构:

1. 标准化初始化与验证流程 (Standardized Initialization & Validation)

这是该基类最重要的功能之一。在 Unity/VRChat 中,不同脚本的 Start 方法执行顺序可能不确定,如果一个脚本依赖另一个还没准备好的脚本,就会出错。

  • 它将 Unity 的 Start 方法封装起来,并引入一个名为 SetupAndValidate() 的虚方法 (virtual method)
  • 所有继承它的子类都应该重写 (override) SetupAndValidate() 方法,在这里检查所有必要的组件或引用(例如,在 Unity 编辑器里拖拽的变量)是否都已设置妥-当。
  • 如果在 Start 过程中 SetupAndValidate() 返回 false,基类会自动调用 ErrorAndDisableGameObject(),打印详细错误日志并禁用当前游戏对象,从而防止一个“已损坏”的组件在游戏中运行引发后续更多问题。
  • 通过 HasStartedOk 属性,其他脚本可以安全地查询此脚本是否已成功初始化。

2. 增强的网络同步功能 (Enhanced Networking)

VRChat 的核心是网络同步,而 Udon 的网络同步需要开发者手动管理,比较繁琐。这个基类极大地简化了这一过程。手动调用网络同步请求(RequestSerialization)可能会失败,或者需要频繁发送导致网络拥堵。

  • MarkNetworkDirty(): 提供一个清晰的接口来“标记”数据已更改,需要同步。它不会立即发送,而是安排在当前帧的稍后时间点统一处理。
  • AutoRetrySendOnFailure: 如果勾选此选项,当一次网络同步请求失败后,系统会自动在下一帧重试,直到成功为止。这大大增强了网络同步的可靠性。
  • SyncPaused: 允许本地暂停某个对象的网络同步。这在需要对对象进行一系列本地修改而不想中途发送不完整数据时非常有用。取消暂停后,它会自动向房主(Owner)请求最新数据或将本地的最终数据发送出去。
  • 请求同步 (RPC_RequestSerialization): 为非房主提供了一个标准方法,可以向房主请求一次完整的数据更新。

3. 集成的日志系统 (Integrated Logging System)

在复杂的 VRChat 场景中,定位问题来源可能很困难。Unity 自带的 Debug.Log 信息量有限,无法区分日志等级,也不方便追踪日志来源。

  • 日志等级 (Severity): 允许为每个脚本设置不同的日志输出级别(如 Debug, Info, Warning, Error)。在最终发布的世界中,可以将等级调高(例如 Warning),这样就不会输出繁杂的调试信息,只保留警告和错误。
  • 统一的日志器 (TlpLogger): 它会自动寻找场景中名为 "TLP_Logger" 的统一日志管理对象。如果找不到,它会给出一个非常明确的错误提示,而不是让每个脚本都报错。
  • 丰富的上下文信息: 所有的日志输出都会自动添加一个前缀 [执行顺序 脚本在场景中的路径],例如 [0 /MyObject/MyScript]。这使得在海量日志中定位问题来源变得极其简单。
  • 调试断言 (Assert): 提供了 Assert(condition, message) 方法,只在特定编译环境下(TLP_DEBUG)生效。如果条件 condition 为 false,它会立即报错并禁用组件/对象,帮助开发者在开发阶段尽早发现逻辑错误。

4. 脚本执行顺序管理 (Execution Order Management)

脚本的执行顺序对逻辑正确性至关重要,但 Unity 不会默认警告顺序问题。

  • 它在 Start 期间会进行一项巧妙的检查(AssertExecutionOrderCorrect)。通过 VRChat Player Tag 记录本帧上一个启动的脚本及其执行顺序,如果发现当前脚本的执行顺序(ExecutionOrder)小于或等于上一个脚本,但它们不是同一个脚本,就会发出一个警告
  • 这能有效帮助开发者发现并修复那些因执行顺序不当而导致的潜在 Bug。

5. 对象池支持 (Object Pooling Support)

在游戏中频繁生成(Instantiate)和销毁(Destroy)对象会造成性能开销和内存碎片,尤其是在 VRChat 中。对象池是常见的解决方案。

Guribo 使用 CyanPlayerObjectPool 作为对象池的解决方法

  • 定义了一套标准的对象池接口:OnCreated()OnReadyForUse()OnPrepareForReturnToPool()
  • 这意味着任何继承自 TlpBaseBehaviour 的脚本都可以轻松地被一个兼容的对象池系统管理。当从池中取出时,会调用 OnReadyForUse 进行初始化;当返回池中时,会调用 OnPrepareForReturnToPool 进行状态重置。

MvcBase

接下来,Gurbo 通过 Tlp Base Behaviour 继承了一个子类,MvcBase,作为 MVC(Model-View-Controller)设计模式在 Unity 中(使用 Udon)的一个基础实现。

这个类的主要作用是自动连接同一 GameObject 上的 MVC 组件,让它们能协同工作。当 MVC 所有组件都挂载在同一个物体上时,一键初始化单个 GameObject 上的完整 MVC 结构。

  1. 检查 GameObject 是否有效

  2. 自动查找该物体上的 4 个关键组件:

    • Model:数据存储

    • View:界面显示

    • Controller:业务逻辑

    • UdonEvent:事件触发器

  3. 调用 InitializeMvc() 进行连接

  4. 失败时输出详细错误日志

PlayerAudioController

PlayerAudioController 是 MvcBase.Controller 的子类。

MvcBase.Controller

MvcBase.Controller 负责接收用户输入、处理业务逻辑,并协调模型和视图的交互,是游戏逻辑的核心调度中心。

PlayerAudioController

PlayerAudioController 在此之上实现了在 VRChat 场景中全面接管并动态调整所有玩家(包括本地玩家和远程玩家)的声音和虚拟形象(Avatar)音频。

太复杂了,写不完了