第四章 Prefab-基礎知識


這一章講解Prefab。Prefab是饑荒世界構(gòu)成的基礎,也是Mod技術(shù)的基本內(nèi)容。

Prefab,中文譯名叫預制物,也可以廣義地稱之為物體。
在饑荒的世界中,Prefab是最基礎的元素,除了操作面板和地圖外,所有的一切,包括食物、人物、動物、植物、水池、礦石乃至于特效等等,都是Prefab。可以說,了解Prefab,是制作饑荒MOD的基礎。

本章將講解Prefab的相關(guān)基礎知識,下一章將講解如何構(gòu)建不同種類的Prefab。如果你急于知道如何做一件衣服,一頂帽子,或者一件法杖,那么你可以先跳過本章,看下一章。但我建議你認真學習Prefab的基礎知識。這些基礎知識是構(gòu)建的根基,只有掌握它們,你才有能力發(fā)揮你的創(chuàng)造力。

本章內(nèi)容較多,先了解知識結(jié)構(gòu)有助于更好地學習。

知識結(jié)構(gòu)如下圖,本章只講述基礎知識的部分

Prefab基礎知識結(jié)構(gòu).png

基本概念

Prefab,可以說是饑荒世界的原子,一棵樹,一塊卵石,一口鍋等等……凡是在世界地圖中能夠參與互動的,全部都是Prefab。

想要構(gòu)建一個Prefab對象,可以用Prefab類的構(gòu)造函數(shù)來表示:Prefab("common/inventory/lotus_umbrella", fn, assets)
一個Prefab由三部分組成,對應Prefab類構(gòu)造函數(shù)的三個參數(shù),分別是Prefab名,描述函數(shù)和加載資源表。

  • Prefab名:用于向系統(tǒng)注冊,從而使得系統(tǒng)能夠精確地定位到某個Prefab進行操作,比如生成一棵草。在Prefab類構(gòu)造函數(shù)中,只會識別最后一個/后面的字符。比如上面的例子,系統(tǒng)會認為Prefab名為lotus_umbrella
  • 描述函數(shù):用于描述Prefab的內(nèi)涵,比如說它的外表是什么,有什么功能等等,應該傳入一個函數(shù)。
  • 加載資源表:用于向系統(tǒng)說明,為了正確地呈現(xiàn)這個Prefab,需要加載那些動畫、圖片、聲音文件,應該傳入一張表。

Prefab分解

--------------------------------------- 加載資源表 -----------------------------------
local assets =
{
    Asset("ANIM", "anim/lotus_umbrella.zip"),
    Asset("ANIM", "anim/swap_lotus_umbrella.zip"),
    Asset("ATLAS", "images/inventoryimages/lotus_umbrella.xml"),
}
--------------------------------------- end 加載資源表  -----------------------------------


--------------------------------------- 描述函數(shù) -----------------------------------
... 一些定義在外部的函數(shù)

local function fn() -- 描述函數(shù)
    local inst = CreateEntity() -- 創(chuàng)建實體
    ... 對inst添加各種各樣的組件,并對每個組件進行一些設置
    return inst
end
--------------------------------------- end 描述函數(shù)  -----------------------------------



return Prefab("common/inventory/lotus_umbrella", fn, assets) -- 第一個參數(shù)就是Prefab名,系統(tǒng)只會識別最后一個斜杠后面的名字,fn代表描述函數(shù),assets代表加載資源表

Prefab名是很容易理解的,直接寫一個自己喜歡的名字就可以了,重點在于了解如何描述加載資源表和描述函數(shù)。

加載資源表

加載資源表本質(zhì)上就是一個Lua table,其中的元素都是Asset對象。如果不明白這句話也沒關(guān)系,使用起來是非常簡單的。你只需要弄清楚當前的Prafab需要什么樣的動畫、圖片、聲音資源,以及它們的存放位置,然后用Asset逐行描述出來就行了。

一張典型的加載資源表

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

Asset第一個參數(shù)是資源類型,第二個參數(shù)則是資源文件的路徑。
各類型資源的對應名稱如下,注意要大寫

  • 動畫:類型名為ANIM
  • 圖片:只需要指明xml文檔的路徑就行,tex文件不需要給出(xml文檔會指明tex文件的所在)。類型名為ATLAS
  • 聲音:fev后綴的聲音集合文件,對應類型"SOUNDPACKAGE",fsb后綴的聲音實體文件,對應類型為"SOUND"

描述函數(shù)

描述函數(shù)是Prefab中最重要的部分,這部分的編程邏輯看起來很復雜,但本質(zhì)也是很簡單的,在描述函數(shù)里,我們實質(zhì)上只是在做三件事:

  1. 創(chuàng)建實體 local inst = CreateEntity()
  2. 為實體(entity)添加各種組件,并設定組件的初始狀態(tài)
  3. 返回實體 return inst

可以用前一節(jié)的部分代碼表述如下

--------------------------------------- 描述函數(shù) -----------------------------------
... 一些定義在外部的函數(shù)
local function fn() -- 描述函數(shù)
    local inst = CreateEntity() -- 創(chuàng)建實體
    ... 對inst添加各種各樣的組件,并對每個組件進行一些設置
    return inst
end
--------------------------------------- end 描述函數(shù)  -----------------------------------

創(chuàng)建實體的方法就是local inst = CreateEntity(),這里變量名inst就是instance的簡寫,是一種約定的寫法,建議沿用。
最簡單的Prefab,就是不添加任何組件的Prefab,創(chuàng)建實體后直接返回該實體即可。只是不添加任何組件的話這個Prefab就沒有任何功能可言,這不是我們想要的。由此我們了解到,Prefab的本質(zhì)就是一個可以填充各種內(nèi)容的實體。為了更便于理解,下文中當實體和Prefab之間沒有歧義的時候,會選擇用實體這個稱呼。

組成結(jié)構(gòu)

創(chuàng)建實體實體和返回實體都是非常簡單的,所有的Prefab在這一點上都是一致的,重要的是了解如何向?qū)嶓w添加組件,以及應該添加哪些組件,如何設置組件的初始狀態(tài)。不同的組件和組件初始狀態(tài),造就了饑荒世界豐富多彩的Prefab。

在游戲系統(tǒng)中,有很少一部分組件是需要和游戲引擎進行直接交互的,要添加這類組件,必須使用inst.entity:AddXXX()這樣的代碼。這些組件是用C++進行編寫的,我們無法了解其內(nèi)部細節(jié),更無法進行修改,我稱之為Entity組件。我們只能了解Entity組件的各種方法,并用這些方法來設置組件的初始狀態(tài)。
除去少數(shù)Entity組件,更多的組件是用lua語言編寫的,要添加這些組件,代碼格式為inst:AddComponent("xxx")。我們還可以在官方的compoentns文件夾下的lua文件中直接看到相應源碼,了解相關(guān)的所有細節(jié)。這樣的組件,就直接沿用官方的稱呼,Component。
兩類組件只在添加方式上有些差異,使用的方法是相似的。下面將重點講解Entity組件的使用方法,而Component由于數(shù)量眾多,方法繁雜,會在后續(xù)單獨劃出一章來講解。

在聯(lián)機版中,常常需要考慮聯(lián)機的問題。如果你希望你的Prefab在生成之后,還能與其它電腦進行通信,就需要添加一段設置網(wǎng)絡代碼,鑒于大部分Prefab都需要在主客機之間進行通信交互,所以此片段幾乎是必定要添加的。本章屬于入門性質(zhì),不會過多探討主客機網(wǎng)絡通信的問題,僅僅給出描述函數(shù)更為詳細的代碼結(jié)構(gòu)如下:

--------------------------------------- 描述函數(shù) -----------------------------------
... 一些定義在外部的函數(shù)
local function fn() -- 描述函數(shù)
    local inst = CreateEntity() -- 創(chuàng)建實體
    -- 在網(wǎng)絡代碼往上的這部分代碼,會在所有主機和客戶端上都運行
    -- Entity組件,Tag和網(wǎng)絡變量
    -- 主客機通用Component(如用于處理說話的talker)
    -- 主客機通用自定義處理
    
    -------------- 網(wǎng)絡代碼 -----------------
    inst.entity:AddNetwork()
    inst.entity:SetPristine()
    if not TheWorld.ismastersim then
        return inst
    end
    -------------- END 網(wǎng)絡代碼 -------------
    -- 從這里往下的代碼,只會在主機上運行。
    -- 大多數(shù)Component
    -- sg和brain
    -- 主機端自定義處理
    
    
    return inst
end
--------------------------------------- end 描述函數(shù)  -----------------------------------

Entity組件

Entity組件的使用頻率很高,幾乎每個Prefab都會有,而Component則不一定會有。需要注意的是,Entity組件是在主客機端都會運行的,所以在描述函數(shù)里,初始化設置的部分必須寫在網(wǎng)絡代碼片段的上面。

Entity組件的數(shù)量不多,所以可以直接全部列出來:

  • Transform:變換組件,控制Prefab的位置、方向、縮放等等
  • AnimState:動畫組件,控制Prefab的材質(zhì)(Build),動畫集合(Bank)和動畫播放(Animation)
  • Phiysics:物理組件,控制Prefab的物理行為,比如速度,碰撞類型等等。
  • Light:光照組件,添加該組件可使得Prefab成為一個光源。
  • Network:網(wǎng)絡組件,添加與否決定了一個Prefab在主機上生成時,是否會被客戶端“看”到。
  • MapEntity:地圖實體組件,使用該組件可以為Prefab在小地圖上創(chuàng)建一個圖標。
  • SoundEmitter:聲音組件,控制Prefab的聲音集合和播放

添加一個Entity組件的代碼是inst.entity:AddXXX(),其中XXX是組件名,比如添加Transform組件,可以寫成inst.entity:AddTransform()
使用一個Entity組件的某個方法的代碼是inst.XXX:YYY(),其中XXX是組件名,YYY是方法名。比如說設定實體的材質(zhì)(Build)為lotus_umbrella,則可以寫成 inst.AnimState:SetBuild("lotus_umbrella")

下面就來逐一講解Entity組件的常用方法,本章只會講解一部分Entity組件。

注意:下文中的inst表示引用實體對象的引用,請自行根據(jù)上下文改變對應變量名。

Transform-變換

Transform的中文譯名為變換,主要控制實體的位置、方向和縮放大小。

  • 位置

饑荒的空間坐標系是右手坐標系,x,z為地面坐標,y為高度坐標。坐標軸的具體的方向是隨著視角的旋轉(zhuǎn)而變化的,但坐標系本身是保持相對不變的。
不理解上面的這段話?沒關(guān)系,你只要知道x,z是地面坐標,y是高度坐標就行了。
獲取位置
inst.Transform:GetWorldPosition()
會返回三個值,分別對應實體當前所在的世界坐標x,y,z
官方還提供了一個用lua代碼封裝好的更簡潔的用法
inst:GetPosition()
與上面的Transform的方法不同,這個用法不會直接返回x,y,z的坐標,而是把坐標封裝成一個Point對象(x,y,z)。如果你想要返回x,y,z坐標,可以寫成:
inst:GetPosition():Get()
設置位置
inst.Transform:SetPosition(x, y, z)
其中x,y,z為世界坐標

  • 方向

雖然饑荒的人物看起來只有4個方向,但在系統(tǒng)中是能描述出實體的360°準確方向的。
具體的數(shù)值區(qū)間為-180°到180°
獲取方向
inst.Transform:GetRotation()
返回實體的當前方向角度degree,區(qū)間范圍為-180°到180°
設置方向
inst.Transform:SetPosition(degree)
設置實體的方向角度,如果degree為大于180或者小于-180的數(shù),會自動轉(zhuǎn)化到對應-180到180區(qū)間上的角度。

  • 縮放

縮放是按比例來算的,也有x,y,z三個值,所有的實體默認初始縮放的x,y,z值為1,1,1
x,y決定縮放比例,z和x,y的比例則決定實體在左右和上下兩個方向上的速度。
一般情況下,推薦設置x,y,z為相同的值。成比例地擴大或縮小。
獲取縮放
inst.Transform:GetScale()
會返回三個值,分別對應縮放比例x,y,z
設置縮放
inst.Transform:SetScale(x, y, z)
其中x,y,z為縮放比例

  • Face

所謂Face,是指同一個實體在面向不同角度時,播放同一個動畫,會顯示不同形態(tài),比如人物靜止不動時,面向上下左右,會有不同的姿態(tài)。

人物不同方向的站立姿態(tài)

人站立_上
人站立_下
人站立_右
人站立_左

這一點在特效施放時很重要,如果Face設置不當,特效就可能在某個朝向上不能正確顯示。

inst.Transform:SetNoFaced() --設置無面,始終只有一個動畫形態(tài)
inst.Transform:SetTwoFaced() --2面,只有下、右
inst.Transform:SetFourFaced() --4面,上下左右
inst.Transform:SetSixFaced() --6面,上下左右+左下、右上
inst.Transform:SetEightFaced() --8面,上下左右+四個斜向

常用方法表

方法名 參數(shù) 返回值 描述
GetWorldPosition 坐標x,y,z 獲取實體的位置
SetPosition 坐標x,y,z 設置實體的位置
GetPredictionPosition 坐標x,y,z 用于客機預測位置,降低網(wǎng)絡延遲造成的影響
GetLocalPosition 坐標x,y,z 對Prefab來說和GetWorldPosition沒有區(qū)別。但實體不止可以表示Prefab,也可以表示UI組件。這個方法一般是用在UI組件上
GetRotation 角度degree 獲取實體的方向角度
SetPosition 角度degree 設置實體的方向角度
GetScale 縮放比例x,y,z 獲取實體的縮放比例
SetScale 縮放比例x,y,z 設置實體的縮放比例
SetNoFaced 設置無面
SetTwoFaced 設置2面
SetFourFaced 設置4面
SetSixFaced 設置6面
SetEightFaced 設置8面

AnimState

AnimState負責處理實體的動畫內(nèi)容。要掌握如何使用AnimState,就先需要了解饑荒中的動畫概念。

最基本的概念是Animation,也就是動畫。當一個Prefab在世界地圖上被呈現(xiàn)出來的時候,就是在播放一段動畫。不動的動畫也算是動畫,是只有1幀的動畫。動畫播放結(jié)束后,不會自己憑空消失,如果沒有進行設置的話,就會在世界地圖上呈現(xiàn)出最后一幀的畫面。對應到Spriter項目,就是單個動畫。
每個動畫會由多個部分組成,我們可以通過代碼來局部改變某個部分。這樣的一個部分就叫Symbol。最典型的例子就是在裝備物品時,會改變?nèi)宋锏耐庥^,這就是通過覆蓋Symbol來實現(xiàn)的。
然后是Bank,就是一堆動畫的集合,對應到Spriter項目,就是多個動畫的上級,一般來說,Bank一旦設定好了就不會改變,但也有例外,比如騎牛。騎在牛上和在地上用的是兩套不同的Bank。
最后是Build,Build就是材質(zhì),對應Spriter項目文件的名字。材質(zhì)就是動畫的外在表現(xiàn)。比如說兔子,有黃色和白色兩種,區(qū)別只在于它們用了不同的Build。

  • 設置
  • SetBuild:設置Build,例:inst.AnimState:SetBuild("samansha")
  • SetBank:設置Bank,例:inst.AnimState:SetBank("wilson")
  • SetLayer:設置層級,會影響到多個物體重疊時的動畫呈現(xiàn)(誰在最前面),例:inst.AnimState:SetLayer(LAYER_WORLD)
  • SetOrientation:設置朝向,在設置Prefab緊貼地面時會很有用,比如農(nóng)場,池塘都是緊貼地面的。例:inst.AnimState:SetOrientation(ANIM_ORIENTATION.OnGround)
  • SetSortOrder:設置排序順序,在層級相同時有影響。例:inst.AnimState:SetSortOrder(3)
  • 動畫播放
  • PlayAnimation:播放動畫,例:inst.AnimState:PlayAnimation("idle")
  • PushAnimation:推送動畫到動畫播放隊列中,Prefab會按隊列中各動畫的先后推送順序依次播放,與PlayAnimation的區(qū)別是,Push會等待隊列動畫播放結(jié)束,而Play是直接打斷播放隊列,立刻播放設定的動畫。例:inst.AnimState:PushAnimation("idle")
  • 局部修改
  • OverrideSymbol:用其它動畫的某個Symbol來覆蓋當前Prefab的Symbol,其中,三個參數(shù)分別為要覆蓋的當前Prefab的Symbol名,覆蓋用的動畫文件名(無后綴),覆蓋用的Symbol名:inst.AnimState:OverrideSymbol("swap_object", "swap_lotus_umbrella", "swap_lotus_umbrella")
  • Show:展現(xiàn)Prefab的某個Symbol,例:inst.AnimState:Show("ARM_carry")
  • Hide:隱藏Prefab的某個Symbol,例:inst.AnimState:Hide("ARM_normal")

常用方法表

方法名 參數(shù) 返回值 描述
SetBuild Build名 設置Build
SetBank Bank名 設置Bank
SetLayer Layer值 設置Layer
SetOrientation Orientation值 設置Orientation
SetSortOrder Order值 設置SortOrder
SetFinalOffset Offset值 設置tFinalOffset
PlayAnimation Animation名,是否循環(huán) 播放動畫,動畫名為第一個參數(shù),第二個參數(shù)決定是否循環(huán)播放,默認為否,一般可以不填
PushAnimation Animation名,是否循環(huán) 推送動畫到播放隊列中,動畫名為第一個參數(shù),第二個參數(shù)決定是否循環(huán)播放,默認為否,一般可以不填
OverrideSymbol 要覆蓋的Symbol名,覆蓋用的Build名,覆蓋用的Symbol名 用新Symbol覆蓋原Symbol
Show Symbol名 展示某個Symbol
Hide Symbol名 隱藏某個Symbol

常見AnimState方法組合
AnimState常用于改變Prefab的動畫表現(xiàn),但單個方法能改變的內(nèi)容很少,一般都是組合使用,這里給出一些常用的片段。

  • Prefab初始化

凡是需要在游戲中能被看到的Prefab,都需要添加初始化代碼。
初始化代碼應該寫在描述函數(shù)里,且在網(wǎng)絡代碼片段之前,約定默認播放動畫為idle。
內(nèi)容很簡單,就是設置Build,Bank和初始播放動畫。

    inst.AnimState:SetBank("bank名")
    inst.AnimState:SetBuild("build名")
    inst.AnimState:PlayAnimation("idle")
  • 將農(nóng)場、池塘等Prefab平放在地圖上

如果你注意觀察,會發(fā)現(xiàn)農(nóng)場、池塘的動畫表現(xiàn)和一般的Prefab不一樣,它們似乎是被平放在了地圖上,而不像一般的Prefab是呈二維狀態(tài),立起來的。你當然可以畫一個平放的圖,不過,這事用代碼來做更容易也更精確。

    inst.AnimState:SetOrientation(ANIM_ORIENTATION.OnGround) -- 設置Prefab平放在地上
    inst.AnimState:SetLayer(LAYER_BACKGROUND) -- 設置圖層位置,會影響到重疊動畫的表現(xiàn),貼在地上的圖層會位于一般Prefab圖層之下。比如人物會遮蓋農(nóng)場的動畫。
    inst.AnimState:SetSortOrder(3) -- 設置排序順序,使用官方常用的3就行了。
  • 裝備/卸載物品

在裝備、卸載物品時,我們常常能看到人物的局部變化,這也是通過AnimState來完成的。
原理就是為Prefab的equipable組件設置裝備和卸載回調(diào)函數(shù),在人物裝備、卸載物品時執(zhí)行相應回調(diào)函數(shù)。
下面代碼片段中給出的就是回調(diào)函數(shù),第一個參數(shù)inst是物品,第二個參數(shù)owner是人物。

其中核心語句是owner.AnimState:OverrideSymbol("swap_owner_symbol", "swap_build", "swap_symbol"),關(guān)于這一句代碼,請參照上文對OverrideSymbol方法的解釋。

對手持、身體和頭部裝備,有各自不同的固定代碼,分別列出如下

手持裝備

-- 裝備回調(diào)
local function onequip(inst, owner)
    owner.AnimState:OverrideSymbol("swap_object", "swap_build", "swap_symbol") --
    owner.AnimState:Show("ARM_carry")
    owner.AnimState:Hide("ARM_normal")
end

-- 卸載回調(diào)
local function onunequip(inst, owner)
    owner.AnimState:Hide("ARM_carry") -- 和上面的裝備回調(diào)類似,可以試試去掉的結(jié)果
    owner.AnimState:Show("ARM_normal")
end

身體裝備

-- 裝備回調(diào)
local function onequip(inst, owner) 
    owner.AnimState:OverrideSymbol("swap_body", "swap_build", "swap_symbol")
end

-- 卸載回調(diào)
local function onunequip(inst, owner) 
    owner.AnimState:ClearOverrideSymbol("swap_body")
end

頭部裝備

-- 裝備回調(diào)
local function onequip(inst, owner)
    owner.AnimState:OverrideSymbol("swap_hat", "swap_build", "swap_symbol")
    owner.AnimState:Show("HAT")
    owner.AnimState:Show("HAT_HAIR")
    owner.AnimState:Hide("HAIR_NOHAT")
    owner.AnimState:Hide("HAIR")
end

-- 卸載回調(diào)
local function onunequip(inst, owner)
    owner.AnimState:Hide("HAT")
    owner.AnimState:Hide("HAT_HAIR")
    owner.AnimState:Show("HAIR_NOHAT")
    owner.AnimState:Show("HAIR")
end
  • 按順序播放多個動畫

假如你有一組動畫需要連起來按順序播放,比如人物的一個完整的砍樹流程,有兩個動畫,一個是前搖chop_pre,一個是砍樹chop。這時候就需要考慮使用動畫隊列了。使用的方法很簡單,播放第一個動畫,然后把剩下的動畫推送到動畫隊列里。人物完整的砍樹流程動畫代碼如下:

    inst.AnimState:PlayAnimation("chop_pre") -- 播放前搖動畫
    inst.AnimState:PushAnimation("chop") -- 推送砍樹動畫
  • 播放完動畫就移除Prefab

在饑荒中,特效都是通過生成一個特效Prefab,播放特效動畫的方式來實現(xiàn)的。但很多時候我們只需要播放一次特效動畫,之后希望能移除這個Prefab。在動畫播放完成后,Prefab會自動推送一個animover事件。只要監(jiān)聽該事件,在播放完后直接移除Prefab即可,具體代碼就一行:inst:ListenForEvent("animover", function() inst:Remove() end),通常直接寫在描述函數(shù)里,網(wǎng)絡代碼片段的下方,也就是只在主機執(zhí)行這段代碼。

Physics-物理

Physics是為實體提供物理效果的組件,能夠使得實體擁有質(zhì)量、半徑、速度和碰撞屬性,進而實現(xiàn)各種物理運動。

  • 設置物理類型

官方提供了標準化組件,可以通過一行代碼來設置各種不同類型的物理實體的碰撞屬性和質(zhì)量、半徑等。
詳情可以參考游戲根目錄/data/scripts/standardcomponents.lua

這里講解實體物理類型的方法,這些方法不可在同一個Prefab中使用。使用方法是在描述函數(shù)中添加語句,需要寫在網(wǎng)絡代碼的上方。

物品欄物品(各種可以放進物品欄的小物品)
MakeInventoryPhysics(inst)
特點:可以通過inst.Physics:SetVel(x,y,z)來提供初速度,并且遵循重力、摩擦、碰撞等物理規(guī)律。

人物角色(人物,行走的生物)
MakeCharacterPhysics(inst, mass, rad)
其中,mass為質(zhì)量,rad為碰撞半徑,下面類似參數(shù)名也有同樣含義。
特點:無視摩擦力,無法越過障礙物(小型:漿果叢,一般:池塘、圍墻)

飛行生物(蚊子,蜜蜂)
MakeFlyingCharacterPhysics(inst, mass, rad)
特點:類似人物角色,但可以越過像池塘、漿果叢這樣的障礙物。

極小飛行生物(蝴蝶)
MakeTinyFlyingCharacterPhysics(inst, mass, rad)
特點:類似飛行生物,但不會和飛行生物發(fā)生碰撞(很多蝴蝶可以在同一個位置重疊,而蜜蜂不行)

巨型生物(各大BOSS)
MakeGiantCharacterPhysics(inst, mass, rad)
特點:類似人物角色,但會越過漿果叢等小型障礙物。

飛行巨型生物(龍蠅,蜂后)
MakeFlyingGiantCharacterPhysics(inst, mass, rad)
特點:類似巨型生物,但可以越過池塘這樣的一般障礙物

幽靈(阿比蓋爾,蝙蝠,格羅姆,幽靈,玩家的靈魂)
MakeGhostPhysics(inst, mass, rad)
特點:類似人物角色,但無視障礙物。

障礙物(圍墻,各種建筑,豬王等等)
MakeObstaclePhysics(inst, rad, height)
特點:無

小型障礙物(漿果叢,尸骨)
MakeObstaclePhysics(inst, rad, height)
特點:無

重型障礙物(各種可以背的石塊)
MakeHeavyObstaclePhysics(inst, rad, height)
特點:類似障礙物,需要結(jié)合組件heavyobstaclephysics使用

小型重型障礙物(knighthead,bishophead,rooknose)
MakeSmallHeavyObstaclePhysics(inst, rad, height)
特點:類似小型障礙物,需要結(jié)合組件heavyobstaclephysics使用

  • 改變物理屬性

我們可以在游戲中動態(tài)地改變Prefab的物理屬性,以實現(xiàn)某種物理上的視覺效果,比如與豬王交易時,豬王拋出的物品就具有一個斜向上的初始速度,比直接在地上生成物品顯得更為自然。

無視碰撞
RemovePhysicsColliders(inst)
移除所有碰撞效果,自由穿梭

停止運動
inst.Physics:Stop()
會將實體的速度設為0

設置/獲取運動速度
inst.Physics:SetMotorVel(x,y,z)
為實體設置運動速度,驅(qū)使Prefab移動,x,y,z為各個軸向的速度。該方法只對人物、生物類型有效。
此處的坐標系為相對坐標系,x軸正向為Prefab當前面向方向,y軸向上,z軸方向由x,y用右手系法則確定。直觀地說,大多數(shù)時候我們只需要讓Prefab按照當前方向前進,那么只需要設置第一個參數(shù)的值為目標速度,y,z均取0。
inst.Physics:GetMotorVel()
獲取實體的當前速度

設置初速度
inst.Physics:SetVel(x,y,z)
為Prefab設置初始速度,驅(qū)使Prefab移動,x,y,z為各個軸向的速度。該方法只對物品欄物品類型有效。
SetMotorVel不同,此方法的坐標系與世界地圖的坐標系一致。
此速度只是初始速度,物品欄物品的運動會受到重力、摩擦力、彈力等影響。

Light-光

這個組件能允許實體提供照明功能,有各種參數(shù)來設定照明效果,學習這個組件主要是了解光源的各種參數(shù)。常見的提供光源的代表Prefab有火把、火坑、礦燈等等。

安裝組件inst.entity:AddLight()

啟用/禁用
inst.Light:Enable(bool)
bool為true,啟用組件,能發(fā)光。bool為false,禁用組件,不能發(fā)光。

判斷是否啟用了組件:inst.Light:IsEnabled()

Radius - 半徑
這一參數(shù)代表光照的范圍,半徑越大,光照的范圍越遠,注意這個半徑是三維的,也就意味著如果你搞個在半空中的光源的話,在地面上的光半徑是小于實際半徑的。

設置半徑:inst.Light:SetRadius(radius)
獲取半徑:inst.Light:GetRadius()
radius就是半徑,單位與游戲中的距離一致,取值為任意正數(shù)

以下為不同半徑的展示。
其它參數(shù):intensity = 0.5,falloff = 0.5,RGB = (180 / 255, 195 / 255, 225 / 255)

radius = 1

radius = 10

Intensity - 光強
這里的光強并不是指光的強烈程度,而是指光線的集中程度。實際上,光強的提升是通過聚集更多的光線得到的,在其它參數(shù)相同的情況下,越高的光強,就意味著越集中的光線,過高的光強會使得光源附近的物體更黯淡。

可以類比生活中的聚光燈,越高的光強,就越集中在一點上。

設置光強:inst.Light:SetIntensity(percent)
獲取光強:inst.Light:GetIntensity()
percent為光強百分比,取值范圍為0到1之間。超出范圍的值,光強取0。
percent越接近1,光線越集中,中心位置越明亮,周圍也越暗;越接近0,光線越分散,中心位置越黯淡。當取為1時,會造成數(shù)據(jù)溢出,光線變成了一個四方形

以下為不同光強的展示
其它參數(shù):radius = 1,falloff = 0.5,RGB = (180 / 255, 195 / 255, 225 / 255)

intensity = 0.1
intensity = 0.3
intensity = 0.5
intensity = 0.7
intensity = 0.9
intensity = 0.9999
intensity = 1

Falloff - 衰減
在生活中,對于一個單一發(fā)散的光源,比如蠟燭,距離越遠,光線就越暗。我們就用光線的衰減速度來描述光線隨著光源距離而變暗的動態(tài)變化。衰減速度越慢,就意味著能以更高的光強傳播到遠處。四周就顯得更明亮。

設置衰減:inst.Light:SetFalloff(percent)
獲取衰減:inst.Light:GetFalloff()
percent為衰減百分比,取值范圍為0到1之間。大于1則取1,小于0則取0。衰減越高,光線的傳播距離就越小。

在實際應用中,percent不可取值過小,因為光線的實際照明半徑有一定的計算公式,取過小的衰減會造成實際照明半徑溢出。通常來說,取值不可小于0.05。

以下為不同衰減的展示
其它參數(shù):radius = 1,intensity = 0.5,RGB = (180 / 255, 195 / 255, 225 / 255)

falloff = 0.1
falloff = 0.2
falloff = 0.3
falloff = 0.5
falloff = 0.7
falloff = 1

實際光照半徑

光照的實際范圍是由光照半徑,光強,衰減三個因素共同決定的。系統(tǒng)提供了一個獲取光照實際范圍的方法GetCalculatedRadius。

使用方法:inst.Light:GetCalculatedRadius()

Colour - 顏色
組件還允許設置光照的顏色,可以設置紅,綠,藍三種光,取值為0到1之間,但實質(zhì)上,每個顏色的可能取值為256種,即 0/255 到 255/255。一般也按這個格式來寫具體參數(shù)。

設置顏色:inst.Light:SetColour(red,green,blue)
獲取顏色:inst.Light:GetColour()

雖然不同的顏色在游戲的視覺效果上有差異,但實質(zhì)光照效果是不變的,這可以通過調(diào)用GetCalculatedRadius方法確認

Network-網(wǎng)絡

這一個組件本身有很多方法,但大多數(shù)是底層通信邏輯,與游戲功能沒有直接聯(lián)系,所以我們只需要簡單地添加一句inst.entity:AddNetwork(),為實體添加Network組件即可。
有無Network的區(qū)別在于這個Prefab是否會被其它玩家看到。比如著名的幾何種植Mod,種植時呈現(xiàn)出來的網(wǎng)格線也是一個Prefab,但它無法被其它玩家看到,也不需要和主機進行數(shù)據(jù)交換,就是因為沒有添加Network組件。
對于自定義的物品,我們當然是希望其它玩家也能看到的,更重要的是要和主機進行數(shù)據(jù)交換,所以是一定要添加Network組件。

Component

Component是一類用lua語言編寫的組件,可以在components文件夾下看到源碼各個Component的源碼。與Entity組件最大的區(qū)別是,Component不僅能調(diào)用方法,還能儲存數(shù)據(jù),而無需依賴Prefab提供數(shù)據(jù)。這種設計能夠解耦功能與Prefab,使得同一個Component能夠被多個不同的Prefab使用,大大提高代碼的重用率。Component用到的數(shù)據(jù)由Component自行存取,邏輯上也更為清晰。比如,人物的血量就是一個Component,這個Component有兩個核心數(shù)據(jù):當前血量和最高血量,相關(guān)的一系列方法都圍繞著這兩個核心數(shù)據(jù)進行操作。這個方法與人物本身是無關(guān)的,除了應用在人物上,同樣也可以應用在任何Prefab上。

Component的內(nèi)容非常多,官方的Component已經(jīng)達到了300多個,其中不少更是數(shù)百行代碼的巨大組件,完全足夠再開一章講解。此處只介紹如何添加和使用一個Component。后續(xù)講解Component時會有更詳細的解釋。

添加Component只需要一行代碼,inst:AddComponent("component名"),component名就是components文件夾下的各個component文件的名字。

所有的Component都由實體的components表統(tǒng)一管理,引用一個名為XX的Component的格式為inst.components.XX
調(diào)用XXComponent的YY方法:inst.components.XX:YY(參數(shù)列表)
調(diào)用XXComponent的yy屬性:inst.components.XX.yy

完善Prefab

在上面的內(nèi)容中,主要是針對Prefab的組件功能進行了解釋,重點在于如何編寫描述函數(shù)內(nèi)的代碼。
但還有一部分對Prefab的描述性內(nèi)容,與Prefab的實際功能無關(guān),不寫在描述函數(shù)里。比如,Prefab在游戲中顯示的名字,人物檢查Prefab時的描述,以及讓Prefab變成可以制作的東西等等。本節(jié)關(guān)注的就是這些內(nèi)容。

描述

游戲中的許多描述性的內(nèi)容,是通過全局變量STRINGS表來儲存的。游戲通過一系列的機制來獲取相應的字符串,所以只要按照特別的約定,修改STRINGS表的內(nèi)容,就可以添加我們想要的描述了。

STRINGS.NAMES.LOTUS_UMBRELLA = “荷葉傘” -- 物體在游戲中顯示的名字
STRINGS.CHARACTERS.GENERIC.DESCRIBE.LOTUS_UMBRELLA = "這傘能擋雨嗎?" -- 物體的檢查描述
STRINGS.RECIPE_DESC.LOTUS_UMBRELLA = "荷葉做的雨傘" -- 物體的制作欄描述

名字

所有的Prefab在游戲中顯示的名字,都是由STRINGS.NAMES表儲存的。要添加我們自己的Prefab的描述性名字,只需要增加一個元素,key值取Prefab名的全大寫即可。比如荷葉傘的Prefab名為lotus_umbrella,則可以寫

STRINGS.NAMES.LOTUS_UMBRELLA = "荷葉傘"

人物描述

人物在檢查Prefab時,會說出一段描述性的文字。
游戲在取出描述文本時,首先檢查人物的特殊描述:STRINGS.CHARACTERS.人物Prefab名全大寫.DESCRIBE.物體Prefab名全大寫是否為nil,如果不為nil,就取這個文本。反之,則取通用描述:STRINGS.CHARACTERS.GENERIC.DESCRIBE.物體Prefab名。也就是說,你完全可以為一個Prefab設置多個人物描述。

同樣地,對于荷葉傘,可以添加通用描述:
STRINGS.CHARACTERS.GENERIC.DESCRIBE.LOTUS_UMBRELLA = "這傘能擋雨嗎?"
然后再對willow添加特殊描述
STRINGS.CHARACTERS.WILLOW.DESCRIBE.LOTUS_UMBRELLA = "荷葉傘?這不科學"

在制作人物MOD時,如果想為人物專屬物品添加人物專屬描述,可以把willow換成人物的Prefab名。

物品制作欄描述

和名字的設置方法是類似的,只是換了一張表。
代碼格式:STRINGS.RECIPE_DESC.物體Prefab名全大寫 = 描述字符串
仍拿荷葉傘舉例:STRINGS.RECIPE_DESC.LOTUS_UMBRELLA = "荷葉做的雨傘"

Recipe - 可制作

在游戲中,每一個可制造的物品都有一個Recipe。如果把游戲里的制作欄比作菜譜的話,那么一個Recipe就可以理解為菜單上的一道菜的描述說明。在制作欄上我們可以看到這個Prefab的名字,圖片和制作欄描述,以及制作需要的資源。還有另外一些隱藏的東西比如說所需的科技,一次制作能獲取幾個,誰能制作等等,這些都屬于Recipe的管轄范圍。

打開游戲根目錄/data/scripts/recipe.lua,可以看到Recipe的定義。構(gòu)造函數(shù)的參數(shù)有name, ingredients, tab, level, placer, min_spacing, nounlock, numtogive, builder_tag, atlas, image, testfn。在定義Recipe時,我們并不需要填完所有的參數(shù),只填我們需要的就可以了,不需要的參數(shù),用nil來占位,最后幾個連續(xù)的nil則可以直接省略。

參數(shù)簡略含義如下

  • name:Prefab名
  • ingredients:成分表
  • tab:物品欄分類
  • level:科技等級
  • placer:建筑物放置物,也就是制造建筑時顯示的那個圖像(實質(zhì)上也是個Prefab)
  • min_spacing:最小間隔
  • nounlock:是否可以離開制作臺制作--遠古物品只能在制作臺上制作。nil則可以離開制作臺
  • numtogive:制作數(shù)量,若填nil則為制作1個。
  • builder_tag:制作者需要擁有的Tag(標簽),填nil則所有人都可以做
  • atlas:制作欄圖片文檔路徑
  • image:制作欄圖片文件名,當名字與Prefab名相同時,可省略。
  • testfn:自定義檢測函數(shù),需要滿足該函數(shù)才能制作物品,不常用。

在上面的參數(shù)中,name, ingredients, tab, level這些項是必填的,只要填了這四項就可以讓物品可制作了。其它參數(shù)都是選填的。

成分表ingredients較特殊,其中元素必須是Ingredient對象,也就是一個成分。構(gòu)建一個成分十分簡單,只需要寫Ingredient(ingredienttype, amount, atlas, deconstruct)即可。其中ingredienttype是成分的prefab名或者特殊成分名(針對精神、血量等特殊成分),amount是所需數(shù)量,atlas是圖片文檔路徑。desconstruct很少使用,可以直接忽略。

對于MOD來說,官方提供了專門的MOD API,只需要使用這個MOD API來添加Recipe就行了,使用方法和Recipe的構(gòu)造函數(shù)是一致的。

一般物品

要制作一般物品,只需要name, ingredients, tab, level,再添加atlas,用于顯示圖片即可。
在modmain里寫下相關(guān)代碼,使得荷葉傘可制作

modmain.lua

AddRecipe("lotus_umbrella", {Ingredient("cutgrass", 1), Ingredient("twigs", 1)}, RECIPETABS.SURVIVAL, TECH.NONE, nil, nil, nil, nil, nil,"images/inventoryimages/lotus_umbrella.xml") 

代碼中用到了RECIPETABSTECH這兩項常數(shù),具體的值可以查閱游戲根目錄/scripts/constants.lua,如果要更換荷葉傘的制作歸類和科技等級,可以查閱這兩項常數(shù)的值。

建筑物

要制作建筑物,則比一般物品更復雜一些,需要先生成一個Prefab放置物(約定命名為Prefab名_placer,然后在添加Recipe時設置這個放置物Prefab。

下面通過講解制作一個荷花池(Prefab名為lotus_pond)來展示如何制作建筑物。

首先,需要在Prefab文件中,最后的return處添加語句,創(chuàng)建一個放置物:MakePlacer(name, bank, build, anim, onground, snap, metersnap, scale, fixedcameraoffset, facing, postinit_fn)。

  • name:放置物的Prefab名,一般約定為原Prefab名_placer
  • bank:放置物的Bank
  • build:放置物的Build
  • anim:放置物用于播放的動畫,一般約定為idle
  • onground:取值為truefalse,是否設置為緊貼地面。請參考前面AnimState的內(nèi)容
  • snap:取值為truefalse,這個參數(shù)目前無用,設置為nil即可
  • metersnap:取值為truefalse,與圍墻有關(guān),一般建筑物用不上,設置為nil即可。
  • scale:縮放大小
  • fixedcameraoffset:固定偏移
  • facing:設置有幾個面,參考AnimState的內(nèi)容
  • postinit_fn:特殊處理

雖然參數(shù)眾多,但對于一般的建筑物,只需要使用前5個參數(shù):name, bank, build, anim, onground

對荷花池來說,需要添加的Placer語句為:MakePlacer("common/lotus_pond_placer", "lotus_pond", "lotus_pond", "idle", true)

lotus_pond_placer

然后按照一般的Prefab制作流程制作一個Prefab。因為荷花池需要緊貼地面,所以需要一些額外的AnimState設置,代碼如下:

    inst.AnimState:SetOrientation(ANIM_ORIENTATION.OnGround)
    inst.AnimState:SetLayer(LAYER_BACKGROUND)
    inst.AnimState:SetSortOrder(3)

然后,給荷花池添加一個Recipe,這個Recipe與一般物品不同,需要設置placer,添加語句:AddRecipe("lotus_pond", {Ingredient("shovel", 2), Ingredient("cutstone", 2),Ingredient("poop", 6)}, RECIPETABS.FARM, TECH.SCIENCE_TWO, "lotus_pond_placer", 5, nil, nil, nil,"images/map_icons/lotus_pond.xml")

作業(yè)

本章內(nèi)容偏基礎知識,沒有太多可以實踐的東西,只需要做一個建筑 - 荷花池即可。要求能夠達到類似建造農(nóng)場的效果:可以控制建造的位置,并且緊貼地面。

相關(guān)素材已經(jīng)準備好了,直接編寫代碼即可。
動畫文件 - anim/lotus_pond.zip
圖片文檔 - images/map_icon/lotus_pond.xml

作業(yè)Mod在我的網(wǎng)盤下載,文件路徑為饑荒Mod指南/charpter_4_homework.zip,另外有答案文件,路徑為饑荒Mod指南/charpter_4_homework.zip

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

  • 這個系列教程很長,涉及到很多編程、游戲方面的概念。與其讓你云里霧里地看著看著就放棄,不如先教你如何快速入門,通過模...
    LongFei_aot閱讀 13,795評論 9 77
  • 饑荒分為單機版和聯(lián)機版,一般來說,內(nèi)容相同的MOD,聯(lián)機版相對單機版來說,總是要多一些對網(wǎng)絡數(shù)據(jù)的處理,總歸是要復...
    LongFei_aot閱讀 8,823評論 1 17
  • This article is a record of my journey to learn Game Deve...
    蔡子聰閱讀 4,129評論 0 9
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 15,688評論 4 61
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,347評論 25 708

友情鏈接更多精彩內(nèi)容