VCMI 动画资源格式解析:用 JSON 替换 HoMM3 .def 动画文件的方法与实现原理
游戏开发【免费下载链接】vcmiOpen-source engine for Heroes of Might and Magic III项目地址https://gitcode.com/gh_mirrors/vc/vcmi点击查看免费下载本篇以 VCMI 修改者文档 Animation Format 为核心讲解如何用.json文件替代 Heroes of Might and Magic III 的.def动画文件包括basepath、sequences、images三类配置的完整语法、按钮/城镇/生物动画的替换实例以及生物动画 0–51 帧组的含义。结合渲染层源码RenderHandler.cpp、ImageLocator.cpp可以进一步说明引擎如何解析这些 JSON 配置、如何与原始.def回退共存以及阴影/描边自动生成的运行时机制。为什么需要 JSON 动画格式VCMI 允许修改者用.json文件覆盖overrideHoMM3 的.def动画文件。相比.defJSON 格式带来了三个实际好处出自原文档可以单独覆盖动画中的某一帧例如只换某个图标、某个按钮状态支持现代图片格式——TGA、PNG 等 VCMI 图片加载器支持的所有格式不需要任何专用工具——一个文本编辑器加图片素材即可完成修改。从源码结构看覆盖的判定逻辑在渲染初始化阶段完成引擎先读取.def得到各组的原始帧数再查找同名.json资源逐条覆盖对应帧槽位。这一过程位于 RenderHandler::getAnimationLayout将动画路径规范化为SPRITES/或SPRITES2X/、SPRITES3X/、SPRITES4X/前缀以支持不同缩放倍率的高清资源目录若存在.def按defFile-getEntries()为每组resize出与原始文件相同的帧数——这保证了未覆盖的帧仍然来自原.def再加载同名.json调用 initFromJson 逐帧写入布局表。之后查询某帧时getLocatorForAnimationFrame 优先返回 JSON 提供的ImageLocator如果该槽位没有被 JSON 填写或帧索引越界则回退到默认的defFile:group:frame定位方式。这就是只换部分帧、其余保持原样的实现基础。格式说明Format DescriptionJSON 动画文件支持两个互斥的顶层配置区sequences整组替换和images单帧替换外加一个可选的basepath前缀。原文档给出的完整格式如下{ // 所有图片的基础路径可选。 // 用于避免写很长的图片路径 basepath : path/to/images/directory/, // 动画中的序列/分组列表 // 会用指定的文件列表替换原动画中的对应组 // 即使原动画更长也一样 sequences : [ { // 组索引从 0 开始 group : 1, // 该组内的文件列表 frames : [ frame1.png, frame2.png ... ], // 如需自动为此帧生成阴影。可选0 无1 普通阴影2 剪切阴影如冒险地图用 generateShadow : 1, // 如需自动为此帧生成覆盖层。可选0 无1 描边 generateOverlay : 1, }, ... ], // sequences 的替代方案。允许覆盖文件中的单个帧。 // 一般不应与 sequences 同时使用 images : [ { // 所属组。可选默认 0 group : 0, // 该组内的帧索引 frame : 0, // 该帧对应的文件名 file : filename.png, // 如需自动为此帧生成阴影。可选0 无1 普通阴影2 剪切阴影 generateShadow : 1, // 如需自动生成覆盖层。可选0 无1 描边 generateOverlay : 1, }, ... ] }basepath路径前缀basepath是可选字段所有相对图片名都会拼接在其后。解析代码位于 initFromJsonbasepath config[basepath].String()随后无论是sequences中的帧还是images中的file/defFile都会执行basepath node[file].String()拼接。若basepath指向目录末尾应带/官方示例均如此书写。值得注意的细节initFromJson还会把顶层的margins、width、height通过JsonUtils::inherit(toAdd, base)继承到每一帧上——也就是说顶层可以统一声明裁剪边距和尺寸逐帧省略。sequences按组整体替换sequences数组的每一项对应一个动画组group组索引从 0 开始frames该组的新帧文件列表。解析时先source[groupID].clear()清空原组再按列表顺序填入——这会替换整组内容即使原动画更长原文档明确说明此语义。images按帧精准覆盖images数组的每一项指定单个帧group组索引可选缺省为 0frame组内帧索引file新图片文件名。源码中有一个容易忽略的健壮性处理RenderHandler.cpp 第 186–187 行如果目标group在当前布局里还没有那么多帧例如.def中该组不存在或帧数更少引擎会source[group].resize(frame1)自动扩容后再写入。此外images条目还支持defFile字段——指向另一个.def文件的帧解析时会同样拼上basepath从而允许从别的.def里借一帧这类混合用法。generateShadow 与 generateOverlay运行时生成阴影和描边这两个可选字段控制引擎是否对该帧在运行时自动生成阴影或描边。其取值与内部枚举严格对应定义在 ImageLocator.h字段取值内部枚举说明generateShadow0ShadowMode::SHADOW_NONE不生成阴影1ShadowMode::SHADOW_NORMAL普通投影2ShadowMode::SHADOW_SHEAR剪切投影冒险地图单位常用generateOverlay0OverlayMode::OVERLAY_NONE不生成覆盖层1OverlayMode::OVERLAY_OUTLINE白色 1px 描边2OverlayMode::OVERLAY_FLAG旗色覆盖层这些值在 ImageLocator 构造函数 中被static_cast成对应枚举。实际生成发生在渲染阶段loadScaledImage当以阴影层模式ONLY_SHADOW_HIDE_SELECTION/ONLY_SHADOW_HIDE_FLAG_COLOR取图且generateShadow有效时调用img-drawShadow(是否剪切)当以覆盖层模式ONLY_FLAG_COLOR/ONLY_SELECTION取图且generateOverlay OVERLAY_OUTLINE时调用img-drawOutline(Colors::WHITE, 1)。从源码结构看还有一条命名约定式的替代路径如果未声明generateShadow/generateOverlay渲染器会在图片路径后追加-SHADOW或-OVERLAY后缀去查找预制的阴影/覆盖层图片RenderHandler.cpp 第 407–418 行。即运行时算法生成与手工提供后缀图两种做法并存。实例一替换按钮按钮类动画固定需要 4 个状态帧原文档 Examples / Replacing a button 一节Active激活按钮可用玩家可以按Pressed按下玩家已按下但尚未松开Blocked禁用按钮被阻塞、不可交互。注意部分按钮永远不会被禁用可以不提供这张图Highlighted高亮只有部分按钮在特定情况下使用。例如主菜单中鼠标悬停在按钮上时显示高亮又如可切换设置的开/关状态按钮。原文档给出的示例 JSON{ basepath : interface/MyButton, // 所有图片都位于此目录 images : [ {frame : 0, file : active.png }, {frame : 1, file : pressed.png }, {frame : 2, file : blocked.png }, {frame : 3, file : highlighted.png }, ] }这正是images按帧覆盖的典型用法组 0默认 group的 0–3 帧分别对应四种状态。仓库内就存在多个同结构的真实文件例如 checkbox.json 用两张 PNG 覆盖了大厅复选框的两个状态{ basepath : lobby/, images : [ { frame : 0, file : checkboxBlueOff.png}, { frame : 1, file : checkboxBlueOn.png} ] }更多同类文件可参考 Mods/vcmi/Content/Sprites/ 目录下的deleteButton.json、dropdown.json、rangeHighlightsGreen.json等它们演示了按钮、下拉框、高亮遮罩等不同 UI 元素的覆盖方式。实例二替换简单动画对于冒险地图对象或城镇建筑这类单组循环动画用sequences定义一组帧即可原文档示例{ basepath : myTown/myBuilding, // 所有图片都位于此目录 sequences : [ { group : 0, frames : [ frame01.png, frame02.png, frame03.png, frame04.png, frame05.png ... ] } ] }由于sequences的语义是整组替换这里的帧列表长度无需与原.def相同——即使原动画更长也会被这份列表完全取代。原文档中Replacing creature animation替换生物动画一节标记为 TODO未给出完整示例但方法一致按下面的帧组编号为每组提供帧列表。下面一节给出了生物动画的完整组定义。生物动画帧组Creature Animation Groups生物动画由多个组group构成每组代表一个特定动作。原文档完整列出了 VCMI 使用的组编号及语义此处完整继承基础动画[0] Movement移动生物移动时使用[1] Mouse over鼠标悬停随机待机动作以及鼠标移过生物时播放[2] Idle待机生物堆不执行动作时持续播放的基础动画[3] Hitted受击生物堆被打中时播放[4] Defence防御防御姿态下的替代受击动画近战命中且正在防御时播放[5] Death死亡生物堆死亡时播放[6] Death (ranged)死亡·远程替代死亡动画被远程攻击击杀时播放。转向动画[7] Turn left左转旋转动画的前半部分生物转向观察者一侧[8] Turn right右转旋转动画的后半部分[9]VCMI 未使用存在于 H3 原始文件中[10]VCMI 未使用存在于 H3 原始文件中。近战攻击动画[11] Attack (up)面朝上目标的攻击动画[12] Attack (front)面朝前方目标的攻击动画[13] Attack (down)面朝下目标的攻击动画。远程攻击动画[14] Shooting (up)面朝上目标的远程攻击动画[15] Shooting (front)面朝前方目标的远程攻击动画[16] Shooting (down)面朝下目标的远程攻击动画。特殊动画[17] Special (up)当找不到专用施法或群攻动画时使用的特殊动画[18] Special (front)同上面朝前方[19] Special (down)同上面朝下方。H3 附加动画[20] Movement start移动开始移动动画开始前播放[21] Movement end移动结束移动动画结束后播放。VCMI 附加动画[22] Dead已死亡生物死亡后的静止画面。若未提供则由 Death 组的最后一帧构成[23] Dead (ranged)已死亡·远程远程攻击致死后的画面。若未提供由 Death (ranged) 组最后一帧构成[24] Resurrection复活生物复活时播放。若未提供由 Death 动画的反转序列构成。施法动画[30] Cast (up)面朝上目标施法时[31] Cast (front)面朝前方目标施法时[32] Cast (down)面朝下目标施法时。群体攻击动画[40] Group Attack (up)生物攻击多个目标且主目标在上方时使用如龙息、九头蛇类生物[41] Group Attack (front)主目标在前方时使用[42] Group Attack (down)主目标在下方时使用。H3 附加动画传送[50] Teleportation start传送开始单位传送时在原位置播放。若未提供将改用 movement start 动画[51] Teleportation end传送结束单位传送时在目标位置播放。若未提供将改用 movement end 动画。覆盖的加载与回退机制源码佐证理解以下几个实现细节可以避免修改时踩坑JSON 只覆盖、不删除。布局初始化时先按.def的getEntries()把每组resize到原始帧数RenderHandler.cpp 第 226–233 行JSON 再覆盖指定槽位。因此未写进 JSON 的帧自动保持原样。sequences是破坏性覆盖对命中的组先clear()再填帧所以用sequences重写某一组后该组未列出的原帧不会再出现。多来源合并getResourcesWithName(jsonResource)会收集同名 JSONRenderHandler.cpp 第 235–247 行多个 Mod 层级的同名动画配置按加载器顺序叠加处理后写者覆盖先写者。缩放倍率目录SPRITES2X/、SPRITES3X/、SPRITES4X/前缀用于存放 2x/3x/4x 高清资源只有开启 HD 贴图settings[video][useHdTextures]或缩放系数为 1 时才会使用带前缀的布局getAnimationLayout。源码注释也明确.def只按 1x 数据使用放大素材应使用独立图片。帧缺失时的兜底查询不存在的组或越界帧会返回空ImageLocator帧定位器既无image也无defFile时回退为ImageLocator(path, frame, group, mode)即直接按.def原始帧渲染getLocatorForAnimationFrame。若.def里也没有该帧则记录错误并加载占位图DEFAULTloadImageFromFileUncached。SDL2 客户端行为一致clientsdl2 渲染路径中有对称的 initFromJson 实现同一套 JSON 语法对两种渲染后端通用。实战检查清单按原文档骨架 源码行为制作/审查一份动画覆盖 JSON 时可依次确认文件是否为目标.def的同名.json放在 Mod 资源目录中引擎按资源名匹配basepath是否以/结尾且图片实际位于该目录只换个别帧按钮、图标→ 用images整组重做建筑、单位动作→ 用sequences二者一般不要混用原文档建议组号是否按上面的生物帧组表填写生物动画尤其注意 30/40/50 段的施法、群攻、传送组;需要运行时阴影/描边的帧如冒险地图单位是否声明了generateShadow: 2剪切或generateOverlay: 1描边目标帧在原.def中不存在时是否有意为之——引擎会自动扩容帧槽不会报错。小结VCMI 的动画覆盖格式用一个轻量 JSON 就打通了旧.def资产 现代图片格式 逐帧精准替换的 Mod 工作流basepath管路径、sequences管整组、images管单帧、generateShadow/generateOverlay管运行时生成的阴影与描边生物动画则以 0–51 的固定组编号规范动作语义。格式语义由 docs/modders/Animation_Format.md 定义解析与回退逻辑可在 clientsdl3/render/RenderHandler.cpp 与 client/render/ImageLocator.cpp 中逐行验证真实用例可参考 Mods/vcmi/Content/Sprites/ 下的各 JSON 文件。赞分享游戏开发【免费下载链接】vcmiOpen-source engine for Heroes of Might and Magic III项目地址https://gitcode.com/gh_mirrors/vc/vcmi点击查看免费下载相关推荐Typi配置完全指南从基础到高级断点设置Typi配置完全指南从基础到高级断点设置 Typi是一款强大的Sass mixin工具专为简化响应式排版设计而开发。通过直观的配置方式和灵活的断点系统即使maldev 终极指南恶意软件开发技术深度解析与实践教程maldev 终极指南恶意软件开发技术深度解析与实践教程 在当今网络安全领域理解恶意软件的工作原理对于防御者来说至关重要。maldev 项目提供了一个独特的示例工程网络安全SDN网络故障排查技巧从丢包到性能优化 | SDN HandbookSDN网络故障排查技巧从丢包到性能优化 | SDN Handbook SDN软件定义网络凭借其灵活的流量控制和集中化管理能力已成为现代网络架构的核心。然上一篇core-js 中 ECMAScript globalThis 的实现原理、入口与实战用法下一篇wp-calypso DateRange 组件指南从 Trigger 到 Popover 的完整日期区间选择方案创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

Apache Zeppelin 使用 systemd 托管:从 unit 文件安装到日常运维实战

Apache Zeppelin 使用 systemd 托管:从 unit 文件安装到日常运维实战

数据分析数据可视化大数据后端 【免费下载链接】zeppelin Web-based notebook that enables data-driven, interactive data analytics and collaborative documents with SQL, Scala and more. 项目地址: https://gitcode.com/gh_mirrors/zeppelin1/zeppelin 点击查…

2026/10/11 12:53:01 阅读更多 →
基于Python机器学习的疲劳驾驶检测系统实战

基于Python机器学习的疲劳驾驶检测系统实战

简介:面向机器学习与计算机视觉学习者的疲劳驾驶检测系统源码,整套资源共29个文件、压缩包约152MB,涵盖核心Python程序、可执行字节码文件、驾驶场景视频样本、XML配置文件、图片素材、人脸关键点模型文件及项目说明,能够支撑从数…

2026/10/10 9:01:40 阅读更多 →
破解AI内存墙:从近存计算到软件优化的工程实践

破解AI内存墙:从近存计算到软件优化的工程实践

1. 从“内存墙”说起:一个被低估的性能瓶颈如果你最近两年一直在关注AI硬件的进展,大概率会反复听到一个词——内存墙。这个词听起来有点抽象,但它在工程实践中的含义非常具体:处理器的算力增长速度远远超过了内存带宽和容量的增长…

2026/10/11 10:07:01 阅读更多 →

最新新闻

Django+Vue外卖点餐系统毕设指南:从建表到联调全流程

Django+Vue外卖点餐系统毕设指南:从建表到联调全流程

简介:一套基于Python Django与Vue.js开发的外卖点餐系统毕业设计项目,采用B/S架构,适合计算机相关专业学生作为毕业设计或课程设计参考。前端覆盖首页、菜品详情、订单中心、用户中心等核心用户场景;后台提供总览、订单管理、菜品…

2026/10/11 13:14:52 阅读更多 →
如何在 Virtual Mac 上 5 分钟装好 macOS:新手保姆级快速上手教程

如何在 Virtual Mac 上 5 分钟装好 macOS:新手保姆级快速上手教程

【免费下载链接】VirtualMacOniPad People have dreamed of running macOS on iPad for more than a decade. Today, that dream comes true. With Virtual Mac, iPad finally breaks free from iPadOS, enabling pro apps like Xcode, Terminal, Final Cut Pro, Logic Pro, an…

2026/10/11 13:14:52 阅读更多 →
Kubernetes弹性伸缩实战:HPA、VPA与Cluster Autoscaler原理与配置

Kubernetes弹性伸缩实战:HPA、VPA与Cluster Autoscaler原理与配置

这两年只要跟Kubernetes沾边的团队,几乎都会聊到弹性伸缩。我这些年在一线折腾过不少集群,从最早以为加个 HPA 就万事大吉,到后来被线上抖动、扩容滞后和成本账单来回折磨,才算是把这一整套机制吃透。Kubernetes弹性伸缩实际上不是…

2026/10/11 13:14:52 阅读更多 →
Kubernetes弹性伸缩实战:HPA、VPA、CA与KEDA解析

Kubernetes弹性伸缩实战:HPA、VPA、CA与KEDA解析

做 Kubernetes 这行绕不开的话题就是弹性伸缩。很多人觉得只要部署到集群里,流量大了自然就能扩容,结果一到促销或者突发流量就被报警轰醒,才发现 Pod 数量没有动,节点也快被打满了。原因很简单,Kubernetes 的弹性伸缩…

2026/10/11 13:14:52 阅读更多 →
把CIContext存进@State?SwiftUI-Agent-Skill的“状态即缓存“高级技巧解析

把CIContext存进@State?SwiftUI-Agent-Skill的“状态即缓存“高级技巧解析

【免费下载链接】SwiftUI-Agent-Skill SwiftUI agent skill for Claude Code, Codex, and other AI tools. 项目地址: https://gitcode.com/GitHub_Trending/swi/SwiftUI-Agent-Skill 点击查看 免费下载 SwiftUI-Agent-Skill(技能名 SwiftUI Pro&#x…

2026/10/11 13:14:52 阅读更多 →
健身动作错误归因数据集:专注关节抖动、遮挡漂移与小目标定位

健身动作错误归因数据集:专注关节抖动、遮挡漂移与小目标定位

简介:本资源是面向计算机视觉开发者与运动健康AI研究者的健身动作关键点检测专用数据集,聚焦于自下而上类动作识别与姿态评估,解决健身动作自动判别、姿势纠错与虚拟教练系统构建等核心问题。数据集共1758张真实场景图像(含训练/验…

2026/10/11 13:13:51 阅读更多 →

日新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/11 0:00:27 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/11 0:00:27 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 10:45:37 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 21:32:20 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 10:38:42 阅读更多 →