☰
Mate-Engine 自定义舞蹈播放器:舞蹈文件(.unity3d)制作、打包与 MMD 标准规范指南
2026/10/16 6:01:52 网站建设 项目流程
  • 桌面应用
  • 数字人
  • AI 应用

【免费下载链接】Mate-Engine

A free Desktop Mate alternative with a lightweight interface and custom VRM support, though with more features.

项目地址:https://gitcode.com/gh_mirrors/ma/Mate-Engine
点击查看免费下载

导读

本文围绕 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 的执行顺序是:

  1. 冻结 Animator(animator.speed = 0)、暂停音频、切换到"等待"状态;
  2. 通过AssetBundle.LoadFromFile加载 Bundle,取出AnimationClip与AudioClip;
  3. 用AnimatorOverrideController把占位动画CUSTOM_DANCE覆盖为舞蹈动画(见 EnsureAnimatorReady);
  4. 先启动音频(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:

  1. 新建 Animator Controller,命名为[DanceName].controller;
  2. 把[DanceName].anim拖入 Animator 窗口;
  3. 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
ウィンク/ウィンク2Blink_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歌曲作者
mmdAuthorMMD 动作作者
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):

字段默认值作用
enableSynctrue是否启用同步
syncFileNameavatar_dance_play_bus.json同步总线文件名,写入persistentDataPath/Sync/
pollInterval0.05秒从总线读取命令的轮询间隔
leadSeconds1.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):

  1. 各实例启动时通过命名 MutexMateEngine.AvatarDanceSync.Leader竞争"领导者"身份(失败时退化为按命令行--instance参数取 0 号实例,见 TryAcquireLeader);
  2. 领导者点击播放/上一曲/下一曲/停止时,计算atUtc = UtcNow() + leadSeconds,把命令(PlayByStableId、StopPlay等)连同目标时间原子化写入总线 JSON(临时文件 + 改名写入,见SafeWrite);
  3. 所有非领导者实例以pollInterval轮询总线,发现v(版本号)递增后按atUtc精确调度执行,从而保证多实例同一时刻切歌;
  4. 领导者在歌曲剩余约 0.18 秒时即开始预调度下一首,避免切歌缝隙;非领导者会临时静音(MuteFollower),待播放命令到达再恢复音量。

若需关闭同步,可在 UI 中切换同步开关;AvatarSyncDanceTools.cs 通过文件锁(FileShare.None独占总线文件)阻止其他实例写入,实现"主实例独奏"。


七、制作与导出工具链:从动画到 .me 舞蹈 Mod

仓库在 Assets/Editor/ModExporterWindow.cs 提供了图形化导出窗口(菜单MateEngine/ME Mod Exporter),切换为Dance Mod模式即可导出舞蹈:

  1. 在场景中挂载MEDanceModCreator组件(MEDanceModCreator.cs),填入:
    • controller:Animator Controller(可为 AnimatorOverrideController);
    • danceClip:舞蹈动画 Clip(留空时自动从 controller 中按placeholderClipName = "CUSTOM_DANCE"查找);
    • song:音频 Clip;
    • songName/songAuthor/mmdAuthor:元数据(留空歌曲名时自动取song.name,见OnValidate);
  2. 在导出窗口中选择该组件、填写舞蹈名与作者、选择构建目标(默认StandaloneWindows64);
  3. 点击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.

项目地址:https://gitcode.com/gh_mirrors/ma/Mate-Engine
点击查看免费下载

相关推荐

上一篇:完整实战指南:如何高效部署开源远程设备管理平台MeshCentral
下一篇:如何使用TikTokDownload工具高效下载抖音无水印视频:完整实用指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询