资讯动态

饥荒烹饪锅Mod开发:从源码读懂自定义料理配方与优先级机制

发布时间:2026/10/15 19:27:41 来源:尧图企业网站定制
简介这是一份面向《饥荒》模组开发者的Lua源码包用于在烹饪锅中实现自定义食物主要解决玩家不理解食物定义、食谱注册与效果触发等问题适合刚接触mod编写或想复刻食物逻辑的游戏玩家。资源以zip压缩包形式发布共10个文件核心是4个lua脚本分别承担物品属性配置、食谱注册、主逻辑入口等职责另含tex/png贴图与xml描述文件整套包仅17KB结构十分轻量。已有373人浏览学习说明这类源码对饥荒mod新手有较直接的参考价值。源码覆盖了从物品定义名称、图标、属性、食谱配方原料、烹饪时间到注册进游戏、特殊效果如喂食猪人的完整链路并兼顾了多mod兼容与测试要点。通过阅读和调试这份工程读者可快速掌握饥荒烹饪锅食物的制作规范也能在此基础上扩展属于自己的独特菜谱。1. 先想清楚为什么给饥荒烹饪锅加菜绕不开这堆源码某开发者想给烹饪锅加一道「两个烤肉加蜂蜜炖出来的新料理」他直接在游戏里翻设置、找菜谱文件折腾一晚上没找到入口——因为饥荒里一道菜不是写在一张「料理配方表」里而是在一堆 lua 源码里通过「食材组合 优先级匹配 prefab 生成」跑出来的。这篇笔记要解决的问题就是把「制作饥荒 烹饪锅食物 源码」这件事拆透你怎么读懂这堆源码、怎么给烹饪锅加自定义料理、怎么让新菜真的出现在锅里而不是被原版菜抢走以及改坏了怎么排查。适合两类人一类是想做烹饪锅 Mod 的新手另一类是拿到别人源码包但看不懂为什么某些菜能出锅、某些菜永远被顶掉的维护者。2. 拆解烹饪锅的运转逻辑先看懂食物是怎么被煮出来的2.1 一条食材扔进锅背后要过四道关卡烹饪锅这个设备在饥荒里对应一个组件component常见实现里叫crockpot。它做的事情很简单接收放进来的食材、累计烹饪时间、到点后从一堆候选料理里挑一个生成。但「挑一个」这步才是源码的重点——它不是随机挑而是按一套固定顺序做判定。我一般把判定拆成四步第一步检查是不是特殊配方比如曼德拉草汤、太妃糖、怪物千层饼这类只靠单一食材就能锁定的料理第二步遍历所有 Recipe按「食材标签是否匹配」筛出候选第三步在候选里按优先级排序第四步根据料理的priority和食材的「需要度」做最终选择。落到代码上一个料理的关键定义大概长这样-- recipes.lua 里的一个料理定义 AddRecipe(honeyham, { Ingredient(smallmeat, 2), Ingredient(honey, 1), Ingredient(veggie, 1) }, { potlevel 1, priority 30, cooktime 40 })这段代码的逻辑是AddRecipe第一个参数是这道菜在源码内部用的名字第二个参数是一个食材数组每个Ingredient里第一个值是食材 prefab 名第二个值是数量第三个参数是一个配置表。potlevel控制这道菜能在几级锅出现priority是匹配权重cooktime是煮多久。注意这里用的是smallmeat而不是「小肉块」因为源码里所有东西都走 prefab 名不是显示名。也就是说当你放进去的食材凑齐了「两小块肉 一蜂蜜 一蔬菜」三样时这道菜才进入候选池。这四道关卡里最容易被忽略的是第一关。原版里有些料理走「特殊配方」通道不参与普通匹配。比如你往锅里丢一个曼德拉草不管剩下三格放什么出来的基本都是曼德拉草汤因为它在特殊配方表里被优先拦截了。所以你想自定义料理时如果食材组合容易被误判成特殊配方你的菜就永远轮不到上场。2.2 一个料理在代码里的完整身份prefab、stew、tuning光有AddRecipe还不够。一道菜真正出现在游戏里必须有一个对应的 prefab——也就是它的实体定义。这个 prefab 决定了它在背包里长什么样、吃了回多少属性、能不能被鸟换种子、能不能喂给某些生物。常见做法是单独写一个 prefab 文件然后在 prefab 里注册一个stewable组件用它承载料理数值-- prefabs/xxxstew.lua 里的简化示例 local assets { Asset(IMAGE, images/inventoryimages/customstew.tex), Asset(ATLAS, images/inventoryimages/customstew.xml), } local function fn() local inst CreateEntity() inst.entity:AddTransform() inst.entity:AddAnimState() inst.entity:AddNetworkEntity() MakeInventoryPhysics(inst) inst:AddTag(preparedfood) inst:AddComponent(edible) inst.components.edible.foodtype FOODTYPE.MEAT inst.components.edible.hungervalue 75 inst.components.edible.healthvalue 20 inst.components.edible.sanityvalue 5 inst:AddComponent(stackable) inst.components.stackable.maxsize 1 inst:AddComponent(perishable) inst.components.perishable:SetPerishTime(TUNING.PERISH_PRESERVED) return inst end return Prefab(customstew, fn, assets)这段代码看着长实际上核心就几件事给这个实体挂上可食用、可堆叠、会腐烂三个组件。foodtype决定它算肉还是蔬菜会直接影响沃利这类角色的偏好hungervalue、healthvalue、sanityvalue就是吃下去后恢复的三维数值。perishable的SetPerishTime用的是TUNING里的预设值——这里的TUNING是游戏全局调参表我一般会填PERISH_PRESERVED表示保存期长如果你改成PERISH_SLOW或更短这道菜会烂得很快。2.3 这个标题的「源码」到底指源码做什么很多抱着「制作饥荒烹饪锅食物源码」来的人拿到手后发现是一堆.lua文件第一反应是「这不是源码吧」。这里得先把预期摆正饥荒的 Mod 层源码就是 lua不涉及引擎层。你不需要编译、不需要装工具链游戏本身就自带一个 lua 解释器你写的代码放对位置就能加载。真正要改的东西集中在三块料理注册表recipes.lua 一类的定义、prefab 实体文件、以及图标贴图资源。这个认知很重要——因为如果你把精力花在找「有没有更底层的编译源码」上方向就偏了。常见的源码包结构是 mods 目录下挂一个 Mod 文件夹里面modmain.lua作为入口modinfo.lua给游戏读取元信息recipes.lua或直接把AddRecipe写在modmain里。你拿到别人的源码包第一件事不是打开一个个文件读而是先在modinfo.lua里看它依赖什么、加载优先级是什么再在modmain.lua里搜AddRecipe和AddPrefab这两个函数基本就能定位这个 Mod 改了什么。3. 动手做第一道自定义料理最小可跑的烹饪锅 Mod3.1 搭骨架modinfo 与目录结构让游戏认得出你的 Mod在写任何料理逻辑之前先把 Mod 的壳建好。饥荒读取 Mod 的方式是扫描 mods 文件夹下每个子文件夹先读modinfo.lua拿到名字和描述再执行modmain.lua。我习惯的目录结构是这样的mods/customstew/ modinfo.lua modmain.lua recipes.lua prefabs/customstew.lua images/inventoryimages/customstew.tex images/inventoryimages/customstew.xml注意用户数据目录里的mods文件夹不是游戏安装目录的mods——后者是内置官方内容你往里面写文件一次验证更新就被覆盖这是最常见的翻车点。modinfo.lua内容如下-- modinfo.lua return { name Custom Stew Mod, description Adds a custom crock pot dish, author YourName, version 1.0, mod_version 1, dst_compatible true, all_clients_require_mod true, icon_atlas modicon.xml, icon modicon.tex, }这里的dst_compatible和all_clients_require_mod是联机版两个关键字段。前者表示这个 Mod 支持联机模式后者表示所有客户端都必须装同一个 Mod——如果你的新菜只在自己本地注册别人进你的房间会看到你手上拿着一个「未知物品」甚至直接掉线。icon_atlas和icon是 Mod 在列表里显示的图标如果你暂时没有贴图可以先省略这两个字段游戏会用默认图标代替不影响功能。3.2 注册入口把 AddRecipe 和 AddPrefab 挂到 modmain壳建好后modmain.lua是加载核心。你在这里导入配方文件和 prefab 文件逻辑很简单先require配方再让游戏知道你新增了一个 prefab。我一般会这样写-- modmain.lua local recipes require(recipes) AddPrefab(customstew)但这里有个坑require(recipes)在饥荒 Mod 环境里不一定会按你预期工作因为recipes.lua这个名字可能跟游戏内部模块冲突。更稳的写法是把自己的配方文件起一个不重名的名字比如custom_recipes.lua然后在 modmain 里这样引用-- modmain.lua local custom_recipes require(custom_recipes) AddPrefab(customstew)实际加载顺序上require会返回一个模块表但如果你的custom_recipes.lua在文件顶层直接调用了AddRecipe那require执行的一瞬间配方就已经注册完了。我看过不少新手在这步纠结「为什么 require 了没反应」其实不是没反应是AddRecipe在文件加载时已经执行而游戏刷新料理列表有自己的时机。如果你需要确保在游戏初始化完成后再注册可以依赖modimport或把注册逻辑包在AddGamePostInit回调里但绝大多数自定义料理场景用顶层调用就够了。3.3 第一道菜注册一个不会被抢走的料理终于到写菜的部分。这里用一道新手举例最多的「蜂蜜烤肉」来演示两个小肉 一个蜂蜜 一个任意蔬菜。之所以选它是因为这个组合同时涉及肉类、蜂蜜、蔬菜三类标签能让你立刻体会到标签匹配的威力-- custom_recipes.lua AddRecipe(honey_roast, { Ingredient(smallmeat, 2), Ingredient(honey, 1), Ingredient(veggie, 1) }, { potlevel 1, priority 50, cooktime 20, num_to_give 1, })这段代码里的num_to_give我特别说明一下它表示一次烹饪产出几个料理默认是 1。有些原版菜比如火鸡大餐会把num_to_give设成 2因为它本质上是「一道菜份量足够养活两个人」。你自定义时先别动这个字段把 1 稳住再说。接下来是核心匹配逻辑当玩家往锅里放了两个小肉、一个蜂蜜、一个蔬菜后系统会把锅里的食材转换成一个「标签需求表」然后遍历所有 Recipe看哪个被满足。priority在这里的用处是如果同时有多道菜都满足食材条件系统会优先选priority高的。我把它设在 50是一个偏高的值目的是避免这道菜被原版那些 priority 20-30 的常见菜顶掉。但这个做法有个副作用一旦你调高了 priority玩家不管怎么组合食材只要凑齐这两个小肉一蜂蜜一蔬菜出的基本都是你的菜。这是一把双刃剑后面避坑章里我会详细讲怎么用标签和优先级做「限定出现」而不是「抢占出现」。代码写完后别忘了在 prefabs 目录里补上customstew.lua内容参考上一章的别炖菜模板把数值改成你想要的就行。3.4 图标与贴图为什么新菜在背包里是紫绿方块代码全部写对后你进游戏会立刻遇到一个视觉问题新菜出来了但图标是紫绿相间的方块。这不是因为你代码写错了而是因为游戏找不到customstew.tex和customstew.xml这组贴图资源。常见的解决方式有两种第一种是正经做贴图用游戏自带的工具把绘制好的 PNG 转成 tex 和 xml第二种是偷懒方案直接借用游戏里某个已有的料理贴图。我一般调试阶段用第二种-- prefabs/customstew.lua 里的简化写法 inst:AddComponent(inventoryitem) inst.components.inventoryitem:ChangeImageName(cookedmeat) inst.components.inventoryitem.imagename cookedmeat inst.components.inventoryitem.atlasname images/inventoryimages/cookedmeat.xml这么改的意思是告诉游戏这个物品的实体模型用自定义的但背包图标借用「熟肉」的贴图。好处是能立刻看到效果坏处是如果你把图标名写成cookedmeat而这道菜实际是炖菜玩家把图标放大了会觉得很违和。正经方案里ChangeImageName对应的tex和xml需要放在images/inventoryimages/目录下并且 xml 里的atlas项要和文件名一致。这一步没有捷径就是做图、导出、放对位置、改对名字。4. 原版菜谱的优先级玄学为什么你的菜老是煮成别的4.1 priority 与需要度同锅食材如何裁决归属把一堆食材扔进锅同时满足好几道菜的配方要求这是特别常见的情况。比如你放了两个蜂蜜一个肉一个浆果——蜂蜜烤肉可能满足蜂蜜火腿也可能满足果酱蜜饯也可能满足。最终出哪道菜不是看哪道菜先被定义而是看两个数值的组合。第一个是priority第二个是「需要度」。「需要度」这个概念在源码里不是显式字段而是通过Recipe里条件判断的复杂程度隐式体现的。说得直白点当食材数量完全覆盖某个配方时这个配方进入候选如果食材数量只覆盖了一部分匹配度就差。系统会先把匹配度高的配方挑出来再在它们之间比较 priority。这个过程我经常跟朋友说成「先看你能不能做再看你想不想做」——能不能做是食材覆盖判断想不想做是 priority 排序。为了让你对数值有个参照感我把常见料理的 priority 分布和适用场景列成一张表这表是按我多年调试经验总结的不是官方数据priority 区间代表类型的菜我的使用建议80-100特殊料理、节日限定、角色专属菜做「特定食材组合铁定出这道菜」时用40-70高级肉菜、需要多种食材的复合菜自定义主要菜的甜区能顶掉大部分原版同组合菜10-30普通单人份料理、早期果腹菜做基础填充菜、或想「偶尔出但是不抢风头」时用0-10失败料理、勉强能吃的糊糊几乎不设设了就是摆设我的血泪经验是新手最容易把 priority 设到 100 以上结果玩家放啥都出你的菜连原版经典配方都被顶没了。更聪明的做法是设成「比你想顶掉的那道菜高 10 到 20」。比如原版蜂蜜火腿 priority 大约在 30 档你想让自己的蜂蜜烤肉在同样食材下优先出就设 50而不是 99。4.2 改原版菜谱的三种姿势覆盖、新增、替换食材除了新增料理很多人的需求是「我不加菜我就想把原版某道菜的配方改一下」。比如觉得火鸡大餐要 4 个鸡腿太苛刻想把需求改成 2 个鸡腿。这个诉求在源码层面有三种做法第一种做法是直接覆盖原版 Recipe核心逻辑是先找到 Game 里已经注册好的配方表然后改它的 ingredients-- 覆盖原版火鸡大餐的食材需求 for k, v in pairs(Recipes) do if v.name turkeydinner then v.ingredients { Ingredient(drumstick, 2), Ingredient(veggie, 1) } end end这段代码要写在modmain.lua的加载阶段而且要在原版食谱注册完成后执行。饥荒的 Mod 加载顺序是先加载游戏本体全部数据再执行 Mod 的 modmain所以你在 modmain 里遍历Recipes表时原版菜已经在里面了。Recipes是一个全局注册表key 是料理名value 是配方对象通过v.name匹配你要改的菜然后替换 ingredients 数组。第二种做法是不碰原版新增一道名字不同的菜但它食材组合和原版非常相似。这个做法我推荐给所有刚上手的人因为它完全不影响原版玩家体验——你没改火鸡大餐只是加了一道看起来很像的菜。第三种做法是替换食材比如把某个食材换成同标签的另一个食材这个其实还是覆盖只是只动数组里一项。三种方式我通常按「是否要保留原菜」来选保留原菜就新增不保留就直接覆盖。4.3 锅的类型与派生菜potlevel 设错会怎样potlevel是一个容易被忽略的字段。它的作用是指定这道菜能在哪种锅上做出来。原版里有普通烹饪锅也有后期解锁的便携烹饪锅、甚至某些 Mod 里的强化锅。如果potlevel设成 2但你的世界里只造了普通锅那这道菜怎么煮都出不来——不是配方不匹配是锅的等级不够。这里要区分「锅等级」的机制普通锅默认potlevel 1便携锅或高级锅通常支持2或更高。你在自定义菜时如果你希望这道菜「只有高级锅能做」就设 2如果你希望任何锅都能做设 1 或 0。设 0 通常表示最低级锅也能做但某些判定逻辑会把 0 当成「没限制」或「不能做」所以我自己一般只设 1 或 2不碰 0。还有一个连坐问题派生菜。有些料理不是直接煮出来的而是通过其他道具合成的。如果你设了很高的 potlevel 让某道菜只能在高级锅出但玩家手里的高级锅配方还没解锁他放进食材后锅会转很久然后给你一碗糊糊——这个体验非常差。我的习惯是给自定义菜设 potlevel 之前先问自己一句「这道菜是要让所有玩家轻松做出来还是要做成高级玩家的奖励」。前者设 1后者设 2千万别模棱两可。5. 烹饪锅 Mod 避坑实录我从翻车里总结的 5 个排查方向这一章直接上干货。以下五条是我在调试烹饪锅自定义料理时踩过最多次的坑每一条都按「现象 → 原因 → 解决」来写你看代码跑不通时按图索骥就行。5.1 坑一新菜做出来了但背包里显示紫绿方块这个现象我上面提到过这里再展开说。现象料理能出锅能吃数值也对但凡是这盘菜背包图全是紫绿相间的马赛克。原因inventoryitem组件找不到imagename对应的贴图资源文件。你那道菜的 prefab 里写了imagename customstew但images/inventoryimages/目录下根本没有customstew.tex和customstew.xml。解决要么补上真正的贴图资源要么像我在 3.4 里那样把imagename改成已有料理的名字比如cookedmeat。这不算最终方案但能让你在调试期间把精力集中在料理逻辑上而不是被贴图挡住。5.2 坑二锅放满食材后一直转圈最后出了别的东西现象你精心设计了食材组合放进去之后锅盖一直冒烟时间一到出来的不是你的菜而是原版的某道菜。原因你的料理在候选池排序时输给了另一道优先级更高或匹配度更高的菜或者你的食材组合本身被特殊配方通道拦截了。解决先在modmain.lua里临时写一行print(custom recipe candidate)然后查看log.txt确认AddRecipe是否真的注册成功。如果能注册但出来还是别的菜就把 priority 调高直到你的菜能稳定胜出。我一般从 50 起步每次加 10直到找到不被顶掉的临界值然后固定住。5.3 坑三代码明明写了但游戏里完全没变化现象你把代码全部写好刷新资源、重进游戏结果锅里还是原版那几道菜你的新菜连影子都没有。原因最常见的有三种——Mod 没启用、缓存没更新、AddRecipe写在了错误的加载阶段。解决第一步到 Mod 管理界面确认开关打开了第二步把mods/settings里的配置删掉重进第三步在modmain.lua第一行加print(custom stew loaded)去log.txt看有没有这行输出。没有输出就说明整个文件没被加载优先级比配方逻辑更高。5.4 坑四食材名字用错了怎么煮都是失败料理现象你代码里写的是Ingredient(蜂蜜, 1)玩家往锅里放了真正的蜂蜜但锅出的是失败料理。原因饥荒的Ingredient接收的是 prefab 名不是中文显示名。honey、smallmeat、berries这些才是合法值中文名在解析时直接找不到对应食材于是食材列表为空。解决写配方时对着游戏数据里的东西对照查 prefab 名常见做法是先print一下GLOBAL.prefabs或者直接查社区整理的食材对照表。这不算高级问题但特别容易在食材一多之后犯。5.5 坑五两个 Mod 同时注册同名料理一个把另一个静默覆盖现象装了两个自定义菜 Mod游戏没报错但其中某个 Mod 的菜完全消失了或者两张图标长得一样的菜出现在不同分类里。原因两个 Mod 的AddRecipe用了同一个内部名字。饥荒的配方注册表是一个全局字典后注册的同名条目会覆盖先注册的。解决给自己的所有料理和 prefab 起一个带前缀的名字比如mycustomstew、mycustomdish01不要用honeyham这种可能跟其他 Mod 撞车的通用名。同时养成习惯在modinfo.lua里声明自己的命名空间方便排查冲突。6. 验证与进阶让新菜在实测里按你预想的数值生效代码能跑通只是第一步数值验证才是让玩家不骂你的关键。我的习惯是先进游戏用控制台直接给角色喂一道新菜然后看三维变化是否匹配。控制台里这样操作-- 控制台给玩家一个自定义料理并打印饥饿值 c_give(customstew) local player GetPlayer() print(hunger before: .. tostring(player.components.hunger.current)) player.components.hunger:DoDelta(-100) print(hunger after empty food: .. tostring(player.components.hunger.current))这段代码的思路是先把背包里已有的饥饿扣到接近 0然后手动吃刚生成的那道菜。如果hungervalue设了 75吃完后饥饿值应该接近 75 或封顶到最大值。如果你发现吃一口只回了 10那不是代码错了而是你可能在 prefab 里改的是另一个副本的数值或者AddRecipe里传的食材组合根本没匹配到你这道菜——你需要看烹饪日志确认。进阶玩法有两种我很推荐你尝试。第一种是「季节限定菜」给料理挂一个 tag然后在crockpot组件的cook回调里检查当前季节只有冬天才允许做饭放进这道菜。第二种是「厨师专属菜」利用performer角色标签只有某角色能烹饪做法是在AddRecipe后追加一个判断条件检查玩家的 prefab 名。这两种做法的技术含量不高但对玩家来说新鲜感很强也是很多成熟 Mod 的标配。我最后养成的习惯是每加一道菜就在modmain.lua顶部建一个常量表把所有料理名、priority、数值集中管理而不是散落在各个文件里。这样调试时改一个数进游戏测一次比在十几个文件里来回翻省太多时间。希望帮到你——先跑通一道菜再谈美化。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑