美文网首页
第一章 快速入门

第一章 快速入门

作者: LongFei_aot | 来源:发表于2017-05-31 13:58 被阅读0次

    这个系列教程很长,涉及到很多编程、游戏方面的概念。与其让你云里雾里地看着看着就放弃,不如先教你如何快速入门,通过模仿来学习,然后在这个MOD的基础上逐步展开、修改。希望这样能减少你的畏难情绪。学会制作MOD可以让你开发出更多的游戏乐趣,祝愿你能成功坚持下去。


    入门一个陌生的领域,第一步总是最为困难的,因为你弄不清楚正确的方向,还有各种各样琐碎的细节来牵扯你的精力。因为不熟悉,在出了问题时也难以解决。
    为了能让你更快地上手,直接操作到最核心的内容,我会提供足够完整的支架MOD供你进行操作,避免把浪费过多的时间在各种不重要的细节上,减少出错的可能。并且,会放出这一节内容全部做完之后的成品MOD,让你可以进行比对学习。

    搭建支架

    我的网盘/饥荒Mod指南文件夹下,下载支架MOD并安装,支架就算搭好了。

    安装方法:
    和一般的MOD安装方法一样,解压到游戏根目录/mods文件夹下,在游戏中启动MOD。
    MOD的名字叫 “从零开始做Samansha_01”。

    此外,为了添加和修改代码,你需要使用一款代码编辑器,不建议使用文本编辑器来写代码。代码编辑器可以提供高亮显示和代码折叠功能,这会极大地提高编程效率和舒适度。
    对于缺乏经验的入门小白,推荐使用Notepad++,完全免费、中文、无需配置,安装完即可使用,非常方便,我初学的很长一段时间内都在使用这款编辑器。
    对于有经验的程序员,请自行选择自己用得顺手的编辑器,个人推荐IDEA+Lua插件,可以很好地对代码进行管理,也支持自动补全,语法检查等功能。

    添加一个新物体

    在饥荒中有个名词叫Prefab,中文译名叫预制物,也可以广义地称之为物体。
    在饥荒的世界中,Prefab是最基础的元素,除了操作面板和地图外,所有的一切,包括食物、人物、动物、植物、水池、矿石乃至于特效等等,都是Prefab。

    可以说,了解Prefab,是制作饥荒MOD的基础。
    要学习Prefab,一个简单的办法是制作一个新的Prefab。
    接下来就展示如何制作一把荷叶伞,并把它添加到游戏里去。

    一把荷叶伞有三个部分,这也是Prefab最基本的组成部分:

    动画:显示在地图上
    图片:显示在物品栏里
    代码:定义一把荷叶伞有什么特征,规定加载哪些动画、图片。

    动画和图片需要经过编译才能被游戏使用,在这一章节中不打算讨论这个问题,所以我直接在支架中提供了原始的动画和图片。

    你只需要按照步骤学习如何用代码定义一把荷叶伞,并加载相应的动画和图片就可以了。

    写代码定义一个物体,实际上就是用一种特殊的语言向饥荒的游戏系统描述这个物体的属性。在这里,所谓的特殊语言就是Lua语言,这是写MOD所需要使用的语言。

    每个人都有一个名字,当我们需要别人的帮助的时候,我们就会喊他们的名字,然后说出我们的请求。饥荒的游戏系统也是一样,如果我们想要让系统给我们一把荷叶伞,那么我们首先要给荷叶伞起个名字。代码通常是用英文写的,这里最好也起个英文的名字,不妨就叫做lotus_umbrella

    现在有了名字,我们需要注册这个名字,让系统知道有这么一个新的物体被添加了。在支架mod的根目录下找到modmain.lua并打开,你会看到如下代码:

    modmain.lua

    PrefabFiles = {
    }
    

    PrefabFiles就是用来注册物体名字的。

    我们可以把“lotus_umbrella”添加上去:

    modmain.lua

    PrefabFiles = {
        "lotus_umbrella",
    }
    

    好了,现在系统知道了“lotus_umbrella”这个名字,那么,在游戏初始化的时候,它就会去找这个名字对应的物体的描述文件,如果找不到,就会报错。所以我们现在需要给物体添加描述的文件。所有的物体描述文件都统一放在MOD根目录/scripts/prefabs 下,所以我们就进入到这个文件夹下,创建一个与物体同名的Lua文件lotus_umbrella.lua,然后打开它。

    现在我们需要用代码来描述这个Prefab了。

    首先,我们需要向系统声明这是一个描述Prefab的文件。这只需要在文件的末尾添加这样一句话:

    return Prefab(物体名,描述函数,加载资源表)
    

    可以看到,对系统来说,只要你给出了物体名,描述函数,和需要加载的资源(图片、动画、声音等),一个Prefab就算是定义好了。你就可以在系统中通过多种方式来获取这个物体。

    我们已经给物体取了名字——lotus_umbrella,现在我们还需要用代码的方式给出描述函数和加载资源表。

    加载资源表

    所谓的加载资源表,就是描述你有哪些美术资源(图片、动画、声音)需要系统加载的。只要列出每项资源的类型和文件路径即可。请按如下的方式书写,这里的Asset就是向系统表明你需要加载这一项资源,第一个参数"ANIM"表示加载的是动画,第二个参数则是对应的路径,路径是从MOD根目录往下算的。

    一般来说,加载资源表这一串代码应该写在代码文件的开头位置。

    local assets =
    {
        Asset("ANIM", "anim/lotus_umbrella.zip"),
        Asset("ANIM", "anim/swap_lotus_umbrella.zip"),
        Asset("ATLAS", "images/inventoryimages/lotus_umbrella.xml"),
    }
    

    这个物体只需要加载四项资源,第一、二项是动画资源,分别代表放在地上和拿在手里的动画,第三项是图片文档,用于物品栏图片。由于图片文档内含有指向具体图片的地址,所以这里不需要再额外加载图片。

    描述函数

    描述函数向游戏系统描述了这个Prefab的各种情况,一般命名为fn。最简单的Prefab的描述函数可以写成这样的形式:

    local fn()
        local inst = CreateEntity()
        return inst
    end
    

    上面的inst就是用系统函数CreateEntity创建出来的一个实体。我们在描述函数中所做的事,就是在这个实体上加上各种性质,然后返回这个实体。

    这个实体只是被创造出来了,虽然已经能在游戏中被创造,但什么也没有,空空如也。丢进游戏里你也无法直接看到。接下来,就需要分析一下需要向系统描述什么。

    动画,要显示在地图上。
    图片,要显示在物品栏里。
    变换(Transform),要能移动位置。
    物理引擎,要能和其它物体发生互动。
    网络,要能被其它玩家看到和互动。

    以上这五项是非常基础的要求,绝大多数物体都需要。对应的代码如下:

    
        inst.entity:AddTransform() -- 添加变换组件,一般只需要添加组件就行了
        inst.entity:AddAnimState() -- 添加动画组件
        inst.entity:AddNetwork() -- 添加网络组件
    
        MakeInventoryPhysics(inst) -- 设置物品拥有一般物品栏物体的物理特性,这是一个系统封装好的函数,内部已经含有对物理引擎的设置
    
        inst.AnimState:SetBank("lotus_umbrella") -- 设置动画属性Bank 为lotus_umbrella
        inst.AnimState:SetBuild("lotus_umbrella") -- 设置动画属性Build 为lotus_umbrella
        inst.AnimState:PlayAnimation("idle") -- 设置默认播放动感为idle
    
        inst.entity:SetPristine() -- 以下这几句是设置网络状态的,并且作为一个分界线,从这个if then 块往上是主客机通用代码,往下则是只限于主机使用的代码。
        if not TheWorld.ismastersim then
            return inst
        end
    
    

    当我们把这些代码加入描述函数后,配合加载的资源,就已经足够让这个物体能在地图上被显示出来了。但这样还不够,我们还需要让这个物体可以像一把雨伞一样被使用。

    这个时候,我们应该去看一下雨伞的代码描述。

    scripts/prefabs/umbrella.lua

    local function common_fn(name) -- 描述函数分成了两段,一段是下面这个,通用的(花伞/猪皮伞共用)
    
        local inst = CreateEntity()
    
        inst.entity:AddTransform()
        inst.entity:AddAnimState()
        inst.entity:AddNetwork()
    
        MakeInventoryPhysics(inst)
    
        inst.AnimState:SetBank(name)
        inst.AnimState:SetBuild(name)
        inst.AnimState:PlayAnimation("idle")  
    
        inst:AddTag("nopunch") -- 添加标签 nopunch,可以让人物在持有雨伞攻击时不是用拳头攻击,而是类似持有武器的高频攻击。当一个物品不具备武器组件的时候,就可以通过这种方式让人物在装备该物品时能做出武器攻击的动作
        
        inst:AddTag("umbrella") -- 添加雨伞标签,会带来一些声音特效。
    
        --waterproofer (from waterproofer component) added to pristine state for optimization
        inst:AddTag("waterproofer") -- 添加防水标签
    
        inst.entity:SetPristine() -- 主客机代码分界线。
        if not TheWorld.ismastersim then
            return inst
        end
    
        inst:AddComponent("waterproofer") -- 设置防水组件,让雨伞具有防水能力
        inst:AddComponent("inspectable") -- 设置可检查组件,让雨伞可以通过按住alt+鼠标左键被检查
        inst:AddComponent("inventoryitem") -- 设置物品栏组件,让雨伞可以被放到物品栏里
        inst:AddComponent("equippable") -- 设置可装备组件,让雨伞可以被人物装备到手上。
    
        inst:AddComponent("insulator") -- 设置保温组件
        inst.components.insulator:SetSummer() -- 设置保温为夏天型,也就是可以延缓夏天时的升温。
    
        MakeHauntableLaunch(inst) -- 设置让雨伞可以被作祟,没什么用的东西。
    
        return inst
    end
    
    local function pigskin() -- 猪皮伞的独有特性
        local inst = common_fn("umbrella")
    
        if not TheWorld.ismastersim then -- 这一段在上面通用部分已经添加过了,其实没啥用。
            return inst
        end
    
        inst:AddComponent("fueled") -- 设置可被添加燃料组件,这里实质上就是耐久度。
        inst.components.fueled.fueltype = FUELTYPE.USAGE --设置燃料类型,也就是需要特定的燃料才能增加,在游戏里这个类型对应的是缝纫包。
        inst.components.fueled:SetDepletedFn(onperish) -- 设置燃料用完之后的回调函数,也就是耐久度归零后,这个物体会怎么样,默认是消失,但你乐意的话也可以让它不消失,而是进化成为一个雨伞怪。
        inst.components.fueled:InitializeFuelLevel(TUNING.UMBRELLA_PERISHTIME)  --设置雨伞的初始燃料值,也就是所谓的耐久度。
    
        inst.components.waterproofer:SetEffectiveness(TUNING.WATERPROOFNESS_HUGE) -- 设置防水性能
    
        inst.components.insulator:SetInsulation(TUNING.INSULATION_MED) -- 设置防热性能
    
        inst.components.equippable:SetOnEquip(onequip) -- 设置装备时的回调函数
        inst.components.equippable:SetOnUnequip(onunequip) -- 设置卸下时的回调函数
    
        return inst
    end
    
    return Prefab("umbrella", pigskin, assets)
    

    作为一个有着丰富MOD制作经验的人,我自然能一眼看穿所有代码的含义,但对于一个刚入门的新手,要解读上面的内容就有点吃力了。此时不要急于求成,应该逐行分析。

    首先需要了解物体描述的基本逻辑。
    少部分底层功能比如上面提到的动画(AnimState),转换(Transform),物理(Physics),网络(NetWork),也就是绝大部分写在if not TheWorld.ismastersim then语句上的内容,是直接通过引擎系统API添加的,这些内容是几乎每个物体都会有的,了解其固定的编写范式就可以了。

    其余的大部分的功能都是通过组件(Component)来实现的,而且统一通过物体的组件表(inst.components)来管理。少部分功能是通过标签(Tag)实现的,但标签本身并不会直接对物体产生影响,只是相当于一个标记,在其它的地方通过检测这个标记来实现某些效果。标签的价值,在了解游戏系统的前提下,可以通过标签快速添加某些功能(比如夜视),但只要你了解了实质,一样可以通过设置组件的方式来实现相同的功能。所以,学习饥荒MOD,最重要的一点还是了解如何使用组件,并且了解游戏中各种常用组件的功能。

    使用组件是非常简单的事,就两步:

    1. 给物体添加组件
    2. 调用组件的函数或给属性赋值来设置个性化内容(如果没有个性化内容,可以忽略掉这一步,比如说可检查组件(inspectable)大部分时候都不需要特别设置)

    添加组件很简单,格式如下:

        inst:AddComponent("组件名")
    

    需要注意的是,绝大部分组件都是只在主机上工作的,所以必须写在主客机分割代码if not TheWorld.ismastersim then的下面。如果你不清楚该写在哪里,可以参考同样游戏里使用了该组件的物体的代码。

    调用组件函数或者给属性赋值则需要些得长一些

        inst.components.组件名.函数名(参数) -- 调用函数
        inst.components.组件名.属性名 = 你想要设置的属性值 -- 给属性赋值
    

    接下来,返回上面的猪皮伞的代码,看看一把猪皮伞都包含哪些组件。
    底层组件:Transform,AnimState,Network,Physics
    表层组件(Component):insulator,waterproofer,inspectable,inventoryitem,equippable,fueled

    底层组件是必备的,就不多谈了。看看表层组件,雨伞的核心功能就是遮阳挡雨,对应的组件就是insulator和waterproofer。雨伞作为通用的可装备物品,也需要可以被检查,可放入物品栏,可装备,对应组件是inspectable,inventoryitem,equippable。最后,雨伞需要有一个耐久度,不能无限制的使用。耐久度有多种组件可以表示,这里用的是fueled,这个组件从字面上就是可添加燃料的意思,可以类比火堆的耐久可以通过添加干草来提升,只不过这里的燃料不是干草,而是缝纫包。

    显然,核心功能的组件是不能缺失的,否则就不足以称其为雨伞了。然后通用的可装备物品的三个组件也是不可缺失的,否则就没法让人正常使用。但最后一个耐久度,就不是必须的。如果你想要雨伞能无限使用,那么就可以去掉这个组件。如果你想让雨伞自然腐烂(更符合荷叶伞的含义),那么可以换另外一个组件perishable。

    从上面的分析我们自然可以对用到组件分为三种类型。第一种是核心组件,这些组件直接影响着着物体的核心功能(区别于其它物体的特征),第二种是通用组件,这些组件支撑着物体的通用功能(和其它物体共有的一些特性),第三种是辅助组件,给物体增加一些限制,即使去掉也不会影响物体的使用。如果你想要添加一个和游戏原有物体相似的物体,比如说,添加一把荷叶伞,那么,你在阅读猪皮伞源码的时候,就应该关注第一种和第二种组件。如果你只是希望嫁接核心功能,比如希望长矛能拥有遮阳挡雨的能力,那么你只需要关注第一种组件。至于第三种组件,则是在你想要添加某些限制的时候才考虑,比如耐久度。

    在弄清楚我们需要使用哪些组件之后,下一步就是了解组件的函数和属性了。这并不是一件容易的事。以我的经验,游戏本身的组件有200多个,其中经常出现的大约有50多个,大部分组件都含有数十个函数或属性。要一口气全部学完显然是不太现实的。我的学习经验是,从一个具体的物品入手,去分析其核心功能组件、通用组件和辅助组件各自是什么。然后看它每个组件都用了什么方法,修改一下相应的数值,看看在游戏中的反应如何。更高效的方法是直接阅读相应组件的函数的源码,不过这是在积累了一定的知识和代码阅读能力之后的事了,对于刚入门的小白而言,直接修改参数,在游戏中体会变化,是最简单有效的方式。

    阅读游戏源码,对想要摆脱单纯复制代码的MOD制作者来说,是必须掌握的能力。但许多人都会被大段的代码弄晕,找不到主干,无法顺利从源码中提取出有用的知识。这上面提供的思路,至少可以帮助你迅速学习到你想要学习的知识。

    在上面的分析之后,通过参考雨伞的源码,就可以完整地给出我的荷叶伞的代码,包括加载资源表和描述函数两部分。

    请参考我的代码,并与猪皮伞的代码做比较,看看有哪些异同,然后自己改动一下各个组件函数的参数,进游戏里看看有什么变化。

    scripts/prefabs/lotus_umbrella.lua

    -- 加载资源表
    local assets =
    {
        Asset("ANIM", "anim/lotus_umbrella.zip"),
        Asset("ANIM", "anim/swap_lotus_umbrella.zip"),
        Asset("ATLAS", "images/inventoryimages/lotus_umbrella.xml"),
    }
    
    
    -- 装备回调函数
    local function onequip(inst, owner)
        owner.AnimState:OverrideSymbol("swap_object", "swap_lotus_umbrella", "swap_lotus_umbrella") -- 以下三句都是设置动画表现的,不会对游戏实际内容产生影响,你可以试试去掉的效果
        owner.AnimState:Show("ARM_carry")
        owner.AnimState:Hide("ARM_normal")
        owner.DynamicShadow:SetSize(1.7, 1) -- 设置阴影大小,你可以仔细观察装备荷叶伞时,人物脚下的阴影变化
    end
    
    -- 卸载回调函数
    local function onunequip(inst, owner)
        owner.AnimState:Hide("ARM_carry") -- 和上面的装备回调类似,可以试试去掉的结果
        owner.AnimState:Show("ARM_normal")
        owner.DynamicShadow:SetSize(1.3, 0.6)
    end
    
    -- 耐久度归零回调函数
    local function onperish(inst)
        inst:Remove() -- 当耐久度归零时,荷叶伞自动消失
    end
    
    local function fn()
        local inst = CreateEntity() -- 创建实体
    
        inst.entity:AddTransform() -- 添加变换组件
        inst.entity:AddAnimState() -- 添加动画组件
        inst.entity:AddNetwork() -- 添加网络组件
    
        MakeInventoryPhysics(inst) -- 添加物理属性
    
        inst.AnimState:SetBank("lotus_umbrella") -- 设置动画的Bank,也就是动画内容组合
        inst.AnimState:SetBuild("lotus_umbrella") -- 设置动画的Build,也就是外表材质
        inst.AnimState:PlayAnimation("idle") -- 设置生成时应该播放的动画
    
        inst:AddTag("nopunch")
        inst:AddTag("umbrella")
    
        inst:AddTag("waterproofer")
        ---------------------- 主客机分界代码 -------------------------
        inst.entity:SetPristine() 
        if not TheWorld.ismastersim then
            return inst
        end
        ---------------------------------------------------------------  
        
        ---------------------- 通用组件 -------------------------
        -- 可检查
        inst:AddComponent("inspectable")
        -- 可放入物品栏
        inst:AddComponent("inventoryitem")
        inst.components.inventoryitem.atlasname = "images/inventoryimages/lotus_umbrella.xml" -- 设置物品栏图片文档。官方内置的物体有默认的图片文档,所以不需要设置这一项,但自己额外添加的物体使用自己的图片文档,就应该设置这一项
    
        -- 可装备
        inst:AddComponent("equippable")
        inst.components.equippable:SetOnEquip(onequip) -- 设置装备时的回调函数
        inst.components.equippable:SetOnUnequip(onunequip) -- 设置卸载时的回调函数
        
        ---------------------- 核心组件 -------------------------
        --防雨
        inst:AddComponent("waterproofer")
        inst.components.waterproofer:SetEffectiveness(TUNING.WATERPROOFNESS_HUGE) -- 设置防雨系数
        --遮阳
        inst:AddComponent("insulator")
        inst.components.insulator:SetSummer() -- 设置只能防热
        inst.components.insulator:SetInsulation(TUNING.INSULATION_MED) -- 设置防热系数
    
    
        ---------------------- 辅助组件 -------------------------
        -- 可腐烂的组件,耐久会随时间推移而自然地降低,常用于食物
        inst:AddComponent("perishable")
        inst.components.perishable:SetPerishTime(TUNING.PERISH_MED) -- 设置耐久度
        inst.components.perishable:StartPerishing() -- 当物体生成的时候就开始腐烂
        inst.components.perishable:SetOnPerishFn(onperish) -- 设置耐久度归零的回调函数
        inst:AddTag("show_spoilage") -- 添加一个标签,与腐败有关的,你可以试试去掉,看看有什么效果
    
        MakeSmallBurnable(inst, TUNING.SMALL_BURNTIME) -- 系统函数,设置物体可以被点燃
        MakeSmallPropagator(inst) -- 系统函数,设置物体可以传播明火
    
        return inst
    end
    
    return Prefab("common/inventory/lotus_umbrella", fn, assets) -- 这里第一项参数是物体名字,写成路径的形式是为了能够清晰地表达这个物体的分类,common也就是普通物体,inventory表明这是一个可以放在物品栏中使用的物体,最后的lotus_umbrella则是真正的Prefab名。游戏在识别的时候只会识别最后这一段Prefab名,也就是lotus_umbrella。前面的部分只是为了代码可读性,对系统而言并没有什么特别意义
    

    上面代码里有一些TUNING.XXX,这些代码指代一个常数,可以在scripts/tuning.lua里看到相应的值。你也可以不使用这些常数而自己设置一个具体的数字。但为了保证游戏的数值平衡,我建议最好还是使用这些常数,如果有微调,可以在这些常数的基础上进行四则运算。

    在上面代码的注释中提到了回调函数。这是一个编程概念,我不打算在入门教程中展开,可以简单地理解为这个回调函数在设置好之后,会在某个场景下被自动执行,比如在人物装备/卸载手持物品时,会改变手部的状态。他的函数参数都是定死了的。可以参考其它物体的源码是如何使用的,也可以直接看组件相应的源码,对于小白当然是推荐前者,先从模仿开始。

    看到这里你就应该已经可以自己在已有的支架MOD上,做出一把荷叶伞了。
    如果你是一个刚入门的小白,那么,在继续阅读下面的内容之前,我建议你跳到本节的最后,也就是作业那一小节,先完成第一道题。

    完善新物体

    在上一节中我们学习了物体的核心代码,这些核心代码决定了物体的内容。已经使得它能够被使用了。但这个物体仍然有许多问题,比如说没有名字,没有检查时的描述,无法通过正常的游戏游戏方式获取等等。所以我们还需要进一步对它进行完善。

    可制造

    在游戏中,每一个可制造的物品都有一个Recipe。
    对于MOD来说,官方提供了专门的MOD API,只需要使用这个MOD API就行了。
    本章的重点在于快速入门,所以不会详细解释这方面的内容,后续章节会有专门讲解。这里只给出代码和相应注释。打开MOD根目录下的modmain.lua,添加如下内容即可让荷叶伞可以制造

    modmain.lua

    PrefabFiles = {
        "lotus_umbrella",
    }
    
    
    -- 一些预设置,防止系统报错
    env.RECIPETABS = GLOBAL.RECIPETABS 
    env.TECH = GLOBAL.TECH
    
    
    -- AddRecipe是官方提供的MOD API,专门用在modmian.lua。参数非常多,和scripts/recipe.lua里的Recipe类的参数(跳过第一个参数self)是一一对应的。
    -- 第一个参数,prefab的名字。
    -- 第二个参数,配方表,用{}框起来,里面每一项配方用一个Ingredient。Ingredient的第一个参数是具体的prefab名,第二个是数量,这里cutgrass和twigs分别是干草和树枝。这就表明荷叶伞可以用1干草1树枝制作出来。
    -- 第三个参数,荷叶伞的归类,RECIPETABS.SURVIVAL表明归类到生存,也就是可以在生存栏里找到。
    -- 第四个参数,荷叶伞需要的科技等级,TECH.NONE 表明不需要科技,随时都可以制造。
    -- 后续5个参数都是nil,表明不需要这些参数,但需要占位置
    -- 最后一个参数,指明图片文档地址,用于制作栏显示图片。
    AddRecipe("lotus_umbrella", {Ingredient("cutgrass", 1), Ingredient("twigs", 1)}, RECIPETABS.SURVIVAL, TECH.NONE, nil, nil, nil, nil, nil,"images/inventoryimages/lotus_umbrella.xml") 
    

    代码中用到了RECIPETABSTECH这两项常数,具体的值可以查阅游戏根目录/scripts/constants.lua,如果要更换荷叶伞的制作归类和科技等级,可以查阅这两项常数的值。

    描述

    虽然荷叶伞已经有了一个可以被系统识别的名字--lotus_umbrella,但如果你此时制作出一把荷叶伞,鼠标停留在上面时,显示的是MISSING_NAME--还缺乏一个会直接显示的名字。同样的,还缺乏检查时的描述以及制作栏的描述。要添加这些内容也很简单,在上面制作栏代码的下面添加如下内容即可

    modmain.lua

    env.STRINGS = GLOBAL.STRINGS -- 预设置
    STRINGS.NAMES.LOTUS_UMBRELLA = "荷叶伞" -- 物体在游戏中显示的名字
    STRINGS.CHARACTERS.GENERIC.DESCRIBE.LOTUS_UMBRELLA = "这伞能挡雨吗?" -- 物体的检查描述
    STRINGS.RECIPE_DESC.LOTUS_UMBRELLA = "荷叶做的雨伞" -- 物体的制作栏描述
    

    可以从上面的代码中发现规律,最后一部分都是LOTUS_UMBRELLA,恰好是物体的prefab名的大写。这是约定,只要遵守就行了。也就是说你要为物体添加描述,那么,首先写好上面的第一句预设置的代码,然后根据需要每个物体添加后面三句代码中的几句,并将最后一部分改成相应的prefab名的大写即可。

    自定义动画和图片

    通过上述的学习,你就已经掌握了通过代码来添加一个新物品的技能。但你在制作新的物品时,可能想使用新的美术资源,也需要取一个新的prefab名。这一小节可以教你如何制作自己的动画和图片,并和代码相联系。

    工具准备

    • 自动编译工具
      无论你是否是Steam玩家,如果你要做含有自定义图片/动画的MOD,最好购买一份steam版的游戏,然后安装官方提供的Mod Tools套装:打开steam -> 库 -> 工具 -> Don'tStarve Mod Tools -> 安装。这个套装不仅能帮助你上传Mod到steam上,还能在你启动游戏时自动编译动画和图片(启动游戏时会弹出一个命令提示符的黑框,就是用于检测是否并自动编译那些未编译的图片和动画的,请不要关闭)。
      如果你就是不想买游戏,那么请到这里下载在Mod Tools套装的源码,并自行编译。

    • 手动编译工具
      没有动画的手动编译工具

    • 动画制作工具
      官方内部应该是用Flash制作的,但他们推荐MOD制作者使用Spriter。这一款软件在Mod Tools套装中有免费版。

    • 图片制作工具
      只要能做出符合格式要求的图片就可以。个人偏好PhotoShop。

    制作物品栏图片

    可以通过自动编译或者手动制作两种方式来制作可供游戏使用的图片。个人更推荐自动编译的方式,以约定的形式制作素材,可以节约你的时间。

    • 格式要求
      png格式,长、宽为64个像素,RGB 8位,背景透明。

    • 自动编译
      Mod根目录/images/inventoryimages下添加物品栏图片,文件名为prefab名,Mod Tools会自动帮你编译成游戏使用的.tex图片,并编写对应的.xml图片文档。
      启动游戏之后自然会帮你完成这一部份图片的编译。

    • 手动制作
      没有Mod Tools或者因为某些不明原因不能自动编译时,可以使用TEXCreator来转换图片。TEXCreator可以在TexTool 1.3中找到,在我的网盘内有下载。使用方法参考老崔的教程

    制作动画

    注意:如果没有Mod Tools,你是没法编译动画的。

    在制作动画之前,你需要了解一些基本概念,这些概念在代码中都有对应:

    • Animation:动画,任何会在地图上显示出来的实体,都有动画。比如,人物在站立不动的时候,就会循环播放idle动画。
    • Bank:一组动画集合。一个Bank可以拥有多个Animation,而每个Animation则具体地表述了Build中的图片该如何使用。所有的人物,使用的bank都是wilson。注意,新物品的Bank名不要使用游戏中已有的Bank名,以免造成覆盖,致使动画无法播放。一般的保险做法是与prefab同名。
    • Build:动画材质。比如兔子在冬天会变色,但仍是那只兔子,只是换了一个白色外观的Build。不同人物的形象也是由Build来区分,他们的Bank和Animation没有变化。
    • Symbol:动画中的某个部位。在Spriter里,同一个文件夹下的图片,都被归结为同一个symbol。文件夹的名字,就是symbol的名字。symbol的主要用途在于用代码做精细控制。比如,装备武器的时候,就是用武器的symbol覆盖了人物的swap_object这个symbol。同时,人物的左手变成大手,也是因为隐藏了ARM_normal这个symbol,改成显示ARM_Carry这个symbol。

    了解了这些基本概念之后,就可以来制作动画了。
    荷叶伞的动画非常简单,只需要两个。一个是放在地上的动画,不需要任何变化,只要一张图片即可。另一个是拿在手上的动画,实质上,由于这个动画是由人物的动画控制的,所以我们只需要提供一张图片覆盖人物手中的位置就可以了,剩下的工作是游戏内置的人物动画来完成的。也就是说,我们只制作两张图片即可。一般来说,我们完全可以只用一张图片,稍做改动即可。

    我用于制作动画的图片是下面两张:

    地上动画 lotus_umbrella.png


    lotus_umbrella.png

    手上动画 swap_lotus_umbrella.png


    swap_lotus_umbrella.png

    建立Spriter项目
    在mod根目录下建立一个名为exported的文件夹,再在下建立两个新的文件夹,一个放置地上动画,名字和prefab名相同,另一个放置手持动画,名字为swap_prefab名。在本教程中,这两个文件夹的名字取为lotus_umbrella和swap_umbrella。再分别进入两个文件夹,建立一个新的文件夹,名字和各自的上级文件夹名字相同。然后放入相应的动画图片。

    exported文件夹结构如下:

    dir.png
    然后就可以建立动画项目了。先建立地上动画的项目。打开Spriter,File-->New Project ,点击OK,在弹出的浏览框里进入到饥荒游戏根目录\mods\\mymod\exported\lotus_umbrella,点击选择文件夹。(注意下面的"文件夹:"输入框里不要填字) 建立项目.png 建立项目2.png

    点开右边的那个文件夹,在里面双击图片lotus_umbrella.png,将左上角的十字调整到合适的位置

    pivot.png

    附带说一句,mod tools提供的spriter是简易免费版,不提供如图所示的九宫格自动定位坐标点的功能。如果你需要这个功能,请购买spriter pro。
    这个十字就是图片的控制点,决定着你的人物在拾取它的时候会拾取哪个部位,对拾取这个动作其实影响不大,但是对后面手持的影响就很大了。然后把图片拖到中间的大屏幕上,设定合适的大小(位置不用管)
    然后看到下面的Animations栏,要改动名字。
    entity_000,这个是对应着物体的Bank的名字,约定写为物体的prefab名,lotus_umbrella。
    NewAnimation则对应着Animation的名字,约定写为idle。
    做完这些,你就可以保存了,注意保存的位置必须要在图片文件夹所在的位置,比如这里就是
    \mods\mymod\exported\lotus_umbrella
    保存的文件名字,就是你的装备的Build名,约定为prefab名,lotus_umbrella。
    这样一来,物品的地上动画就做好了。

    手持动画的制作步骤和上面差不多,只不过bank和build要写为swap_lotus_umbrella,而Animation则要写为BUILD(注意要大写),然后设置控制点要设置在合理的位置,这个控制点决定着人物拿着物体的位置。

    完成这些步骤后,打开游戏,等待自动编译完成,便可以在游戏里看到设置的效果了

    代码联系

    有两部分内容需要用代码进行控制。其一是加载哪些美术资源,其二是描述如何使用这些资源。

    加载的部分是非常简单的。对于prefab来说,就是写一个asset表。
    比如下面这样,就描述了一个荷叶伞所需要的全部美术资源。

    -- 加载资源表
    local assets =
    {
        Asset("ANIM", "anim/lotus_umbrella.zip"), -- 基本动画(放在地上)
        Asset("ANIM", "anim/swap_lotus_umbrella.zip"), -- 手持动画
        Asset("ATLAS", "images/inventoryimages/lotus_umbrella.xml"), -- 物品栏图片
    }
    

    加载动画使用ANIM类别,加载图片使用ATLAS类别,然后再写上相对的文件路径即可,这里的文件路径起点是你的MOD根目录,比如说mod根目录下的anim文件夹下的lotus_umbrella.zip文件,在这里的路径就是anim/lotus_umbrella.zip

    描述如何使用这些资源,则相对复杂得多,我们可以根据需要,在任何时候改变对资源使用的描述,以达到在游戏中改变物体外观形态的效果。在这篇入门教程里,我们只是简单地设置物体的原始形态,以及为物体添加装备/卸载的回调,使之能达到一般手持物体的效果。

    回调函数的部分

    -- 装备回调函数
    local function onequip(inst, owner)
        owner.AnimState:OverrideSymbol("swap_object", "swap_lotus_umbrella", "swap_lotus_umbrella") -- 第一个参数是人物的Symbol,默认是swap_object,第二个参数是手持物体的build,在上面我们已经设置为swap_lotus_umbrella,第三个参数是手持物体的Symbol,也就是上面设置的存放物体图片的文件夹的名字,也是swap_lotus_umbrella
        owner.AnimState:Show("ARM_carry") -- 显示持物手
        owner.AnimState:Hide("ARM_normal") -- 隐藏普通手
        owner.DynamicShadow:SetSize(1.7, 1) -- 设置阴影大小,你可以仔细观察装备荷叶伞时,人物脚下的阴影变化
    end
    
    -- 卸载回调函数
    local function onunequip(inst, owner)
        owner.AnimState:Hide("ARM_carry") -- 和上面的装备回调类似,可以试试去掉的结果
        owner.AnimState:Show("ARM_normal")
        owner.DynamicShadow:SetSize(1.3, 0.6)
    end
    

    原始形态设置的部分

        -- 动画
        inst.AnimState:SetBank("lotus_umbrella") -- 设置动画的Bank,也就是动画内容组合
        inst.AnimState:SetBuild("lotus_umbrella") -- 设置动画的Build,也就是外表材质
        inst.AnimState:PlayAnimation("idle") -- 设置生成时应该播放的动画
        -- 物品栏图片
        inst:AddComponent("inventoryitem")
        inst.components.inventoryitem.atlasname = "images/inventoryimages/lotus_umbrella.xml" -- 设置物品栏图片文档。官方内置的物体有默认的图片文档,所以不需要设置这一项,但自己额外添加的物体使用自己的图片文档,就应该设置这一项
    

    这些代码在上面的完整的lotus_umbrella代码中已经写好了,这里只是再次展示一下。

    作业

    在这里下载本章的支架MOD:scaffold_01.zip,完成以下作业

    1. 自己编写代码,向游戏中添加一把荷叶伞。相应的动画和图片素材已经编译好了,可以直接通过代码载入使用。编写完成后,打开游戏,启用MOD并进入游戏世界,打开控制台,输入c_give("lotus_umbrella"),回车后可以获得一把荷叶伞。看看你是否能成功获得一把荷叶伞。
      如果成功了,在游戏根目录/scirpts/constants.lua里查找FULETYPE,看看都有哪些燃料类型,然后尝试改变荷叶伞的燃料类型,看看怎么修改,可以通过添加木头的方式增加荷叶伞的耐久度。
    2. 在第1题的基础上,完善荷叶伞的名字,描述,并使得其可以被制造出来。查阅游戏根目录/scripts/constants.lua,找到RECIPETABSTECH,对照自己的modmain.lua中制作栏部分的代码,尝试修改荷叶伞的制作归类和科技等级。
    3. 删除anim文件夹下的lotus_umbrella.zipswap_lotus_umbrella.zip文件,自己按照上面的方法制作相应的动画文件。相应素材可以在exported文件夹内找到。

    如果你中途遇到了问题,可以参考我的完整成品scaffold_01f.zip,安装后的Mod名字为“从零开始做Samansha_01f”对照着看看问题出在什么地方。

    相关文章

      网友评论

          本文标题:第一章 快速入门

          本文链接:https://www.haomeiwen.com/subject/lpxjfxtx.html