- 桌面应用
- 数字人
- AI 应用
【免费下载链接】Mate-Engine
A free Desktop Mate alternative with a lightweight interface and custom VRM support, though with more features.
导读
本文围绕 Mate-Engine 中「Custom Dance Player」自定义舞蹈播放器展开,完整讲解如何在 Unity 中制作一个.unity3d舞蹈 AssetBundle(由.anim动画、.controller动画控制器与.mp3/.ogg/.wav音频构成),并给出获得最佳 MMD 面部表情与运镜效果的文件制作标准。文中同时结合仓库源码说明播放器的实际加载逻辑、.me舞蹈 Mod 的导出流程以及多实例同步机制,帮助你制作出可直接放入CustomDances目录或打包为舞蹈 Mod 的合规舞蹈文件。
一、舞蹈文件的本质:一个 AssetBundle,三样资源
Mate-Engine 的 Custom Dance Player 将每一支舞蹈封装为一个.unity3dAssetBundle 文件。从播放器源码(AvatarDancePlayer.cs)可以看到,启动时它会扫描两个来源目录,凡是扩展名为.unity3d的文件都会被当作一支舞蹈加载:
Application.streamingAssetsPath/CustomDances(即项目的 CustomDances 目录,对应代码中streamingSubfolder = "CustomDances");Application.persistentDataPath/Mods(对应modsFolderName = "Mods",通常指 StreamingAssets/Mods 在持久化数据目录中的拷贝)。
因此,只要把打包好的.unity3d文件放入上述目录,重启(或触发 Rescan)后就会出现在播放器列表中。
舞蹈文件结构(.unity3d AssetBundle)
根据 README_dance.md 的规范,每支舞蹈的 Bundle 内应包含以下资源,推荐三者使用一致的命名:
| 资源类型 | 命名约定 | 说明 |
|---|---|---|
| Animator Controller | [DanceName].controller | 简单的单状态控制器:新建 Animator Controller 后,把[DanceName].anim拖入控制器窗口,Unity 会自动生成一个状态,Entry无条件过渡到该状态 |
| 音频文件 | [DanceName].wav/.mp3/.ogg | 支持 3 种常见格式;时长必须与动画长度匹配,否则会导致音画不同步 |
| 动画 | [DanceName].anim | 包含舞蹈动画数据 |
从源码看,加载时播放器会从 Bundle 中取出第一个 AnimationClip 与第一个 AudioClip使用(见 AvatarDancePlayer.cs),所以 Bundle 内通常只需放置这一支舞蹈对应的动画与音频,避免多个 Clip 混淆。
播放器对资源的实际使用方式
播放时,SmoothPlayFlow 的执行顺序是:
- 冻结 Animator(
animator.speed = 0)、暂停音频、切换到"等待"状态; - 通过
AssetBundle.LoadFromFile加载 Bundle,取出AnimationClip与AudioClip; - 用
AnimatorOverrideController把占位动画CUSTOM_DANCE覆盖为舞蹈动画(见 EnsureAnimatorReady); - 先启动音频(
audioSource.Play()),再解除 Animator 冻结、播放动画。
这个"先音频、后动画"的顺序正是原文档提到的WaitForAudioThenStartAnimation思路:在播放器里通过leadSeconds提前量调度(默认 1.5 秒)确保音频起播后动画才进入舞蹈状态,从而规避音频缓冲导致的音画不同步(详见下文"多实例同步"小节)。
二、分步制作一只合规的舞蹈文件
第 1 步:准备动画(.anim)
在 Unity 中制作或导入舞蹈动画 Clip。建议动画只驱动与舞蹈相关的骨骼/曲线,命名统一为[DanceName].anim。仓库中播放器自带的占位动画 CUSTOM_DANCE.anim 与结束动画 DANCE_END.anim 位于 Custom Dance Player/Prefab 目录,可作为参考样例(前者在播放时被替换为舞蹈动画,后者在舞蹈结束时触发自动切歌)。
第 2 步:创建单状态控制器(.controller)
按规范创建一个 Animator Controller:
- 新建 Animator Controller,命名为
[DanceName].controller; - 把
[DanceName].anim拖入 Animator 窗口; - Unity 自动生成一个状态,
Entry无条件过渡到该状态即可。
播放器侧使用的控制器是 CustomDanceAvatarController.controller。仓库文档(README.md)特别说明:不直接使用舞蹈文件自带的 controller,而是用一个包含全部所需参数的自定义 AnimatorController 覆盖角色原控制器,因为直接使用舞蹈 controller 会让 Unity 反复上报"缺少参数"警告,进而引发内存问题。这也是为什么舞蹈 Bundle 里的 controller 只需"最简单的单状态结构"——真正承载状态机的是播放器自己的控制器。
第 3 步:准备音频(.mp3/.ogg/.wav)
音频支持.mp3、.ogg、.wav三种常见格式,命名与动画一致([DanceName].mp3等)。音频时长必须与动画长度匹配,否则会出现音画不同步。播放器中进度与时间显示均以AudioClip.length/audioSource.time为准(见 AvatarDancePlayer.cs),因此音频时长实际上决定了整支舞蹈的播放时长与自动切歌时机。
第 4 步:打包为 AssetBundle
在 Unity 中将三者放入同一个 AssetBundle(建议命名为[DanceName].unity3d)并执行 Build。最终的.unity3d文件即可放入CustomDances目录。
进阶方式:仓库还提供了Dance Mod(.me)导出管线,可将动画 + 音频自动打包成内部含
dance.bundle与dance_meta.json的.me文件,详情见下文第五节与第七节。
三、MMD 面部表情标准:让表情曲线真正生效
原文档明确指出:为了获得最优的 MMD 面部表情支持,舞蹈文件中的面部表情应使用标准的日式 MMD BlendShapes(如あいうえお),并通过模型Body(SkinnedMeshRenderer)上的动画曲线进行控制。
仓库中对这一标准有两层实现佐证:
1. 面部表情名清单
AvatarDancePlayer.cs 内置了一个日式 MMD BlendShape 名称集合,包括:
- 眼睛类:
まばたき(眨眼)、ウィンク、ウィンク2、ウィンク右、笑い、なごみ、びっくり、ジト目、瞳小、キリッ、星目、はぁと、はちゅ目、ハイライト消し等; - 口型类:
あ、い、う、え、お、えーん、ん、▲、ω口、はんっ!、にっこり、にやり、べろっ、てへぺろ、口角上げ、口角下げ、口横広げ、真面目、困る、怒り、照れ、涙、すぼめ等。
停止舞蹈时,ResetMMDBlendShapes 会把这些名字匹配到的 BlendShape 权重全部归零,避免表情残留。
2. 无 MMD 表情模型的兜底转换
如果当前角色本身没有日式 MMD BlendShapes,播放器会通过 AvatarDanceShapeConverter.cs 完成兜底:
- 候选路径依次为
Body、Model/Body、Face(见 candidatePaths); - 先调用
HasMmdBlendshapes检测模型是否已具备 MMD 表情;若已具备则直接跳过转换(见 AvatarDanceShapeConverter.cs); - 若不具备,则把播放器自带的 DummyBlendshapeMesh.asset(内含常见日式 MMD BlendShapes 的网格)挂到候选路径上作为代理渲染器,并借助
PlayableGraph手动求值舞蹈动画,逐帧读取代理网格上的 BlendShape 权重(GetMax取各候选 SMR 中同名表情的最大值),再写入UniversalBlendshapes的通用表情字段(AvatarDanceShapeConverter.cs)。
关键映射关系(可在 AvatarDanceShapeConverter.cs 中确认):
| MMD 表情 | UniversalBlendshapes 字段 |
|---|---|
まばたき | Blink |
ウィンク/ウィンク2 | Blink_L/Blink_R |
あいうえお | AIUEO |
にこり | Joy |
怒り | Angry |
困る | Sorrow |
真面目 | Neutral |
笑い | Fun |
UniversalBlendshapes(UniversalBlendshapes.cs)随后在LateUpdate中把通用字段写入角色的实际表情系统,同时兼容 VRM 0.x(BlendShapePreset,见 vrm0Presets)与 VRM 1.0(ExpressionKey,映射见 vrm10KeyMap)。这就是"即使角色没有 MMD 表情,舞蹈表情也能生效"的完整链路。
四、相机动画标准:Camera_root 层级与 180° 初始旋转
原文档要求,为获得最佳运镜效果,舞蹈文件中应包含如下相机层级:
Camera_root └── Camera_root_1 └── Camera(Camera 组件)各层级职责:
| 节点 | 职责 |
|---|---|
Camera_root | 控制相机整体位置 |
Camera_root_1 | 控制相机距离(z 轴) |
Camera | 通过 Camera 组件控制FOV(视野) |
关键要求:创建上述层级后,需给Camera_root添加一个y 轴 180° 的初始旋转,以保证相机的初始朝向与 MMD 舞蹈惯例一致。
仓库中的辅助脚本 DanceCameraDistKeeper 会在播放期间维持舞蹈相机与角色髋部之间的固定 z 轴距离,防止模型穿模或相机丢失目标,这与上述相机规范配合使用效果最佳。
五、舞蹈文件的两种投放方式:CustomDances 目录与 .me 舞蹈 Mod
方式一:直接放入 CustomDances 目录
把打包好的.unity3d文件放进 Assets/StreamingAssets/CustomDances(构建后位于StreamingAssets根下)。播放器启动时会扫描该目录(streamingSubfolder = "CustomDances")并递归读取所有.unity3d文件(见 AvatarDancePlayer.cs)。
方式二:导出为 .me 舞蹈 Mod
仓库的 Mod 导出器支持把舞蹈封装成.me压缩包。TryAddME的加载逻辑(AvatarDancePlayer.cs)表明.me舞蹈包的结构为:
[DanceName].me (zip) ├── *.bundle // 内部包含 AnimationClip 与 AudioClip 的 AssetBundle └── dance_meta.json // 元数据(或兼容的 dance.json)dance_meta.json的字段定义见 MEDanceModCreator.cs 中的DanceMeta:
| 字段 | 含义 |
|---|---|
songName | 歌曲名 |
songAuthor | 歌曲作者 |
mmdAuthor | MMD 动作作者 |
songLength | 歌曲时长(秒) |
placeholderClipName | 占位动画名(默认CUSTOM_DANCE) |
播放器会优先用songAuthor(其次mmdAuthor)填充作者显示(见 AvatarDancePlayer.cs)。此外,.me舞蹈包在首次加载时会被解压到Application.temporaryCachePath/ME_Cache下缓存,并按源文件修改时间判断是否需要重新解压;播放器还会对.bundle计算SHA1 作为稳定标识(stableId),用于多实例同步时唯一定位同一支舞蹈。
.me文件同样受 Mod 开关控制:加载前会通过IsModEnabled检查 Mod 是否被停用(见 AvatarDancePlayer.cs)。
六、播放器底层实现:加载、播放、队列与多实例同步
资源加载与列表
LoadAllSources 会合并扫描CustomDances与Mods两个目录,按文件名(去扩展名)作为舞蹈 ID,并排序生成列表。BuildListUI 使用对象池构建列表 UI(支持 TMP 与回退字体两套文本组件,useFallbackFont字段控制)。
播放流程
PlayIndex→SmoothPlayFlow的完整时序已在第二节说明。播放过程中Update会持续检测isCustomDancing参数:若动画提前退出舞蹈状态,会自动执行StopAndUnload卸载 Bundle 与音频数据(AvatarDancePlayer.cs)。播放结束(音频播完或时间到达)时触发TryAutoNext自动切到下一首。
队列、收藏、搜索与循环/随机
AvatarDancePlayerTools.cs 提供了配套的列表增强:
- 搜索过滤:
ApplyFilter按标题做大小写不敏感匹配(可忽略 Unicode 零宽字符); - 收藏:收藏列表保存到
Application.persistentDataPath/favorite_songs.json(favoritesFileName字段,见 AvatarDancePlayerTools.cs); - 只显示收藏:开启后会把当前可见曲目构建为播放队列
SetQueueByIndices,上下曲与自动切歌都在该队列内循环; - 循环 / 随机:
loopOn、shuffleOn由对应 Toggle 写入播放器(见 AvatarDancePlayerTools.cs)。
多实例同步(多角色共舞)
当多个 Mate-Engine 实例(例如多个角色同屏共舞)同时运行时,播放器通过文件总线 + 领导者选举实现同步(配置字段见 AvatarDancePlayer.cs):
| 字段 | 默认值 | 作用 |
|---|---|---|
enableSync | true | 是否启用同步 |
syncFileName | avatar_dance_play_bus.json | 同步总线文件名,写入persistentDataPath/Sync/ |
pollInterval | 0.05秒 | 从总线读取命令的轮询间隔 |
leadSeconds | 1.5秒 | 命令执行提前量(给音频缓冲留时间) |
总线文件内容形如 Sync/dance_sync.json(仓库根目录下的同步示例,结构含key/time/playing/version/hasStart/startUtc/ev等字段):
{"key":"","time":0.0,"playing":false,"version":0,"hasStart":false,"startUtc":0.0,"ev":0}工作机制(实现见 AvatarDancePlayer.cs):
- 各实例启动时通过命名 Mutex
MateEngine.AvatarDanceSync.Leader竞争"领导者"身份(失败时退化为按命令行--instance参数取 0 号实例,见 TryAcquireLeader); - 领导者点击播放/上一曲/下一曲/停止时,计算
atUtc = UtcNow() + leadSeconds,把命令(PlayByStableId、StopPlay等)连同目标时间原子化写入总线 JSON(临时文件 + 改名写入,见SafeWrite); - 所有非领导者实例以
pollInterval轮询总线,发现v(版本号)递增后按atUtc精确调度执行,从而保证多实例同一时刻切歌; - 领导者在歌曲剩余约 0.18 秒时即开始预调度下一首,避免切歌缝隙;非领导者会临时静音(
MuteFollower),待播放命令到达再恢复音量。
若需关闭同步,可在 UI 中切换同步开关;AvatarSyncDanceTools.cs 通过文件锁(FileShare.None独占总线文件)阻止其他实例写入,实现"主实例独奏"。
七、制作与导出工具链:从动画到 .me 舞蹈 Mod
仓库在 Assets/Editor/ModExporterWindow.cs 提供了图形化导出窗口(菜单MateEngine/ME Mod Exporter),切换为Dance Mod模式即可导出舞蹈:
- 在场景中挂载
MEDanceModCreator组件(MEDanceModCreator.cs),填入:controller:Animator Controller(可为 AnimatorOverrideController);danceClip:舞蹈动画 Clip(留空时自动从 controller 中按placeholderClipName = "CUSTOM_DANCE"查找);song:音频 Clip;songName/songAuthor/mmdAuthor:元数据(留空歌曲名时自动取song.name,见OnValidate);
- 在导出窗口中选择该组件、填写舞蹈名与作者、选择构建目标(默认
StandaloneWindows64); - 点击Export Dance Mod。
ExportDanceMod的内部流程(ModExporterWindow.cs):
- 将舞蹈动画复制到
Assets/__DanceExportTemp__并设置为dance.bundle的 AssetBundle 名称; - 复制音频并加入同一
dance.bundle; BuildPipeline.BuildAssetBundles构建 Bundle;- 写入
dance_meta.json(内容即上文DanceMeta各字段); - 把构建目录压缩为
ExportedMods/[DanceModName].me并自动定位到资源管理器。
导出产物与播放器TryAddME的解析格式完全对应,可直接投放到Mods目录。仓库 ExportedMods 目录 中已有若干.me示例文件,可作为 Mod 包结构参考。
八、制作规范自查清单与常见问题
提交前自查清单
- 动画、控制器、音频三者命名一致(
[DanceName].anim/.controller/.wav|.mp3|.ogg); - 控制器为单状态结构,
Entry无条件进入舞蹈状态; - 音频时长与动画长度匹配(播放进度以音频为准);
- 面部表情使用日式 MMD BlendShape 名称(如
あいうえお),驱动曲线挂在Body(SkinnedMeshRenderer)上; - 如需运镜:模型下建立
Camera_root → Camera_root_1 → Camera层级,Camera_root初始 y 轴旋转 180°,Camera_root_1控制 z 轴距离,Camera 组件控制 FOV; - 如需导出
.me舞蹈 Mod:使用MEDanceModCreator填写元数据,经ME Mod Exporter导出。
常见问题与应对
- 音画不同步:优先检查音频时长是否与动画一致;播放器已内置
leadSeconds提前量与"先音频后动画"的播放顺序来缓解缓冲问题,仍不同步时可配合手动同步滑块微调。 - 表情不生效:确认角色模型是否带日式 MMD BlendShapes;若没有,确认播放器附带的
DummyBlendshapeMesh与AvatarDanceShapeConverter处于启用状态(它会把 MMD 表情转换为UniversalBlendshapes通用表情)。 - 角色跳舞时部件消失:播放器在角色切换后会开启所有
SkinnedMeshRenderer的updateWhenOffscreen(对应SMRHandle工具),舞蹈期间若仍出现部件丢失,可在播放器工具中排查代理渲染器遮挡设置。 - 直接使用舞蹈 controller 出现大量参数警告:属已知问题,播放器设计上使用自定义的
CustomDanceAvatarController覆盖原控制器,请勿绕过。
参考资料(仓库内进一步阅读)
- 舞蹈文件规范原文:Assets/StreamingAssets/CustomDances/README_dance.md
- Custom Dance Player 组件说明(含脚本职责划分):Assets/MATE ENGINE - Custom Dance Player/README.md
- 播放器主逻辑:AvatarDancePlayer.cs
- 表情转换:AvatarDanceShapeConverter.cs 与 UniversalBlendshapes.cs
- 列表增强工具:AvatarDancePlayerTools.cs
- 舞蹈 Mod 导出:MEDanceModCreator.cs 与 ModExporterWindow.cs
- 桌面应用
- 数字人
- AI 应用
【免费下载链接】Mate-Engine
A free Desktop Mate alternative with a lightweight interface and custom VRM support, though with more features.
相关推荐
AtlasOS 驱动优化完整指南:按症状下药,用 4 个工具解决低帧、推流卡顿与输入延迟
AtlasOS 驱动优化完整指南:按症状下药,用 4 个工具解决低帧、推流卡顿与输入延迟 这是一篇 AtlasOS 驱动优化指南。AtlasOS 是一个开源的
桌面应用数字人AI 应用Wormhole-crypto实战指南:使用encryptStream和decryptStream处理数据流
Wormhole crypto实战指南:使用encryptStream和decryptStream处理数据流 Wormhole crypto是基于RFC 818
密码学如何用AI舞蹈生成器创作专业级3D舞蹈:终极免费教程
如何用AI舞蹈生成器创作专业级3D舞蹈:终极免费教程 想要创作令人惊艳的3D舞蹈动画吗?现在,通过AI舞蹈生成器技术,任何人都可以轻松生成专业级的舞蹈动作!🎵
人工智能深度学习媒体生成计算机视觉
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考