植物 是一個參考體驗,玩家在那裡種植種子,以便能夠稍後收穫並出售結果。
該項目專注於您可能在 Roblox 開發體驗時遇到的常見使用案例。 在適用當的時候,您會找到關於交易、妥協和各種實現選擇的理由,以便您可以做出最佳的決定。
取得檔案
- 導航到 植物 體驗頁面。
- 點擊 ⋯ 按鈕和 編輯在 Studio 。
使用案例
植物 覆蓋以下使用案例:
- 會話資料和玩家資料持續性
- UI 視窗管理
- 客戶端-伺服器網路
- 第一次使用者體驗 (FTUE)
- 硬貨和軟幣購買
此外,這個項目解決了許多體驗的窮凡問題,包括:
- 與玩家關聯的地方的自訂
- 管理玩家角色的移動速度
- 創建跟隨角色的對象
- 檢測角色在哪個世界的區域
注意,這個體驗有很多使用案例太小、太緊縮,或不能示示解決任何有趣的設計挑戰的解決方案;這些都不包含在這個文件中。
項目結構
在創建體驗時,首先決定要如何結構項目,其中包括在資料模型中放置特定實例的位置和如何組織和結構客戶端和服務器代碼的入口點。
資料模型
下表描述資料模型實例中哪些容器服務。
服務 | 類型的實例 |
---|---|
Workspace | 包含靜態模型代表 3D 世界,具體包括不屬於任何玩家的世界。您不需要在執行階段時創建立、創作、修改或摧毀這些例子,因此在此留下它們。 也有一個空 Folder , 其中玩家的農場模型將在執行階段時添加。 |
Lighting | 大氣和照明效果。 |
ReplicatedFirst | 包含最小限度的實例子集,必要於顯示載入屏幕並初始化遊戲。 在 ReplicatedFirst 中放置的實例子集越多,代碼在 ReplicatedFirst 中的等待時間就越執行。 在 源 文件夾中存在載入屏幕使用者GUI。 在 項目 中,存在載入屏幕代碼和需要等待遊戲載入剩餘部分的代碼。 在 start 中,是項目側的所 |
ReplicatedStorage | 為客戶和服務伺服器的所有實例提供存儲容器。
|
ServerScriptService | 包含一個 Script 作為項目入口,用於項目內的所有伺服器端代碼。 |
ServerStorage | 作為儲存容器,為所有不需要複製到客戶端的實例。 在 集合箱 文件夹裡存在一個樣板 農場 模型。複製此模型會在 0> Class.Workspace0> 中放置,當玩家加入遊戲時,即可對所有玩家重複。0> 3> 在 |
SoundService | 包含遊戲中使用 Sound 的對象。在 SoundService 下,這些 Sound 對象沒有位置,並且不在 3D 空間中模擬。 |
入口點
大多數項目會在整個代碼基地上重用 ModuleScripts ,可以從任何地擁有輸入。 ModuleScripts 是重用的
對於 植物 微遊戲,一種不同的方法是通過單個 LocalScript 實現,該入口點是所有客戶端代碼的入口點,並且單個 Script 是所有伺服器代碼的入口點。您的項目的正確方法取決於您的需求,但單個入口提供更大的控制過
下列列表分別描述兩種方法的交易效果:
- 單個 Script 和單個 LocalScript 覆蓋伺服器和客戶端代碼。
- 更大的控制程序開始不同系統的順序,因為所有程式碼都從單個指令碼中初始化。
- 可以在系統之間以引用方式傳輸對象。
高層系統架構
項目中的頂層系統在下方詳細介紹。這些系統與其他系統相較較複雜,而且在許多情況下其功能是在其他類別的階層中抽象的。
這些系統中的每一個都是「單一」,因為它們是由相關的客戶或伺服器 start 指定的非即時化類別。你可以閱讀更多關於 單一模式 後,在此指南中。
伺服器
下列系統與伺服器關聯。
系統 | 說明 |
---|---|
網路 | 建立所有 Class.RemoteEvent 和 Class.RemoteFunction 實例。 > 0> 建立方法發送和聆聽來自客戶端的訊息。0> >0>
|
PlayerDataServer |
|
市場 | 處理客戶端的軟幣交易。 > 顯示一個方法來出售收集到的植物。 > |
碰撞群組管理器 |
|
農場經理服務器 |
|
玩家物件容器 |
|
標記玩家 |
|
FtueManagerServer 服務器 |
|
角色重生器 |
|
客戶
下列系統與客戶端關聯。
系統 | 說明 |
---|---|
網路 | 等待服務器建立所有 Class.RemoteEvent 和 Class.RemoteFunction 實例。 0> >0> 1> 顯示方 |
玩家資料客戶端 | 在記憶體中儲存本地玩家的資料。 在此顯示方法和信號來查詢和訂閱玩家資料的變更。 在此顯示方法和信號來查詢和訂閱玩家資料的變更。 在此顯示方法和信號來查詢和訂閱玩家資料的變更。 在此顯示方法和信號來查詢和����� |
市場客戶 |
|
本地跳躍管理器 |
|
農場經理客戶 | 聆聽特定 Class.CollectionService 標籤被應用到實例上,並在這些實例上創建「零件」來提供對這些實例的行為。一個「零件」是指一個在 Class.CollectionService 標籤添加到實例並被移除時被摧毀的類 |
UI設置 | 初始化所有 UI 層。 設定某些層只能在遊戲世界的物理區域中顯示。 針對特定鏡頭效果,在啟用菜單時啟用遊戲世界的特殊攝影機效果。 連接特定鏡頭效果,在啟用菜單時啟用遊戲世 |
FtueManagerClient |
|
角色衝刺 |
|
客戶端-伺服器通信
大多數 Roblox 體驗都涉及客戶端和伺服器之間的通信。這可能包括客戶端要求伺服器執行某些操作,並且伺服器在客戶端上重複更新。
在此項目中,客戶端與服務器通信盡可能擷取最小限度的使用,並且限制 RemoteEvent 和 RemoteFunction 對象的使用,以減少追蹤的特殊規則數量。此項目使用以下方法,以優先權順序:
透過玩家資料系統來複製
玩家數據系統 允許資料與玩家聯絡,在儲存會話之間持續存在。 此系統提供從客戶端到服務器的複製,並提供一些 API 可以用來查詢資料並訂閱變更,使其成為從服務器到客戶端的複製的理想選擇。
舉例來說,而不是發射一個 bespoke UpdateCoins RemoteEvent 告訴客戶有多少錢,你可以呼叫以下內容,並讓客戶透過 PlayerDataClient.updated 事件訂閱。
PlayerDataServer:setValue(player, "coins", 5)
當然,這只適用於從伺服器到客戶端複製和值之間的持續,但這適用於項目中的驚人數量的情況,包括:
- 目前的 FTUE 階段
- 玩家的道具欄
- 玩家持有的金幣數量
- 玩家的農場狀態
透過屬性重複
在需要將服務器的重複值傳送給特定 Instance 的客戶端,或者在需要重複狀態的客戶端指定的 Class.Instance 的服務器,您可以使用 屬性 。 Roblox 會自動重複屬性值,因此您不需要維護任何代碼路徑來重複狀態與對象關聯的
這對在執行階段時創建的例子非常有用,因為在新的例子上設置的屬性將會在它的父級為數據模型時重複原子。這樣就不需要在 RemoteEvent 或 StringValue 上寫代碼來等待額外數據的重複。
您也可以直接從資料模型中閱取屬性,從客戶端或服務伺服器,使用 GetAttribute() 方法,並訂閱變更使用 GetAttributeChangedSignal() 方法。在 植物 項目中,此方法用於,之其中之一,重複
使用標籤來重複
CollectionService 允許您將字串標籤應用於 Instance 。這很有用於審核實例並將該審核複製到客戶端。
舉例來說,CanPlant 標籤會在伺服器上套用,表示給定的花盆可以接收植物。
直接傳送通訊至網路模組
如果沒有以前的選項適用,您可以通過 網路 模組使用自訂網路呼叫。這是項目中唯一允許客戶端與伺服器通信的選項,因此是最有用的選項來傳輸客戶端請求並收到服務器回應。
植物 使用直接網路呼叫來處理各種客戶端請求,包括:
- 灑水植物
- 種植種子
- 購買物道具
這種方法的缺點是,每個個人訊息都需要一些自訂設定,這可能會增加項目的複雜性,雖然這在可能的情況下已被避免,特別是對於服務器與客戶端通信的情況。
類和單一代碼
在 植物 項目中,像 Roblox 上的實例一樣,可以建立和摧毀Class。其類語法是由 嚴格類型檢查 協助持的 Lua 方法啟發的。
即時化
項目中的許多類別與一個或多個 Instances 關聯。一個指定類別的對象使用 new() 方法創建,與 Roblox 使用 Instance.new() 創建類似。
此模式通常用於有類型在資料模型中有物理代表的對象,並且類別擴展其功能。一個好的例子是 BeamBetween ,它在兩個指
相應的實例
如上所述,這個項目中的許多類別都有資料模型代表,與類別相對應的實例。
而不是在一個 class 對象被初始化時創建這些實例,代碼通常會選擇 Clone() 一個預製版本
結構
雖然在 Lua 使用 金屬表 來傳承,但項目選擇允許類別通過 組合 來延伸互相。當通過組合來連接類別時,「子」對象會在 new() 方法的「子」對象會被啟用作為成員。
有關此示例的實際操動作,請參閱 CloseButton 類,其包括 Button 類。
清除
與 Instance 可以使用 Destroy() 方法一樣,可以摧毀的類別也可以摧毀。項目類的摧毀方式為 destroy() 與下帶 1>
destroy 方法的角色是摧毀任何由對物件創建的實例、連接斷開、並在任何子對象上呼叫 destroy 。這對於連接非常重要,因為有啟用連接的實例不會清除由 Lua 垃圾收集器清除的實例,即使沒有對該實例或連接的參考。
單次
單據是指 Singletons 的名字,這是一個只能存在的對物件的類別。它們是項目的對應 Roblox 的 服務 。相反於儲存引
單據是由 new() 返回的對象,其方法和狀態與方法和狀態一起直接返回。當單據不是 instantiated 時,ModuleScript 語法不是使用,方法和狀態分別以點 ( ) 而不是 colon ( 2> ) 來
嚴格類型檢查
Luau 支持傾斜輸入,這意味著您可以在一些或所有的代碼中添加任意類型的定義。在此項目中, strict 類型檢查是用於每個指令碼的。這是 Roblox 的腳本分析工具的最小權限選項,因此在執行階段中發生類型錯誤的機率最高。
輸入的Class語法
在 Lua 中建立類別的建立方法是 很好文書化 ,但它不適合強力 Luau 輸入。在 Lua 中,最簡單的方法來取得類別的類型是方法 typeof() :
type ClassType = typeof(Class.new())
這種情況下會很有用,但當您的類別初始化時使用只有在執行階段時存在的值,例如 Player 對象,或者假設在 idiomatic Lua 類語言中宣稱方法將始終是一個 self 對象。此外,在 type 預虛擬引擎可以做的假設是,將類別 self 的方法宣稱會
為了支持嚴格類型的 infer,Plant 項目使用一種解決方案,其中一些可能感覺非常非直觀:
- self 的定義與類型宣告和構建器中重複。這會導致一個可維護的負擔,但警告會被標記,如果兩個定義與對方失去同步。
- 類方法以點表示,因此 self 可以明確表示為 ClassType 類型。方法仍然可以使用 colon 作為預期。
--!嚴格
local MyClass = {}
MyClass.__index = MyClass
export type ClassType = typeof(setmetatable(
{} :: {
property: number,
},
MyClass
))
function MyClass.new(property: number): ClassType
local self = {
property = property,
}
setmetatable(self, MyClass)
return self
end
function MyClass.addOne(self: ClassType)
self.property += 1
end
return MyClass
在邏輯守衛後拋射類型
在寫作時,值的類型不會在 optionalParameter 後縮小。例如,在 number 下, optional Parameter 的類型不會縮小為 2>number2>。
--!嚴格
local function foo(optionalParameter: number?)
if not optionalParameter then
return
end
print(optionalParameter + 1)
end
為了補償這點,新變數在這些守護者具體Cast類型後建立。
--!嚴格
local function foo(optionalParameter: number?)
if not optionalParameter then
return
end
local parameter = optionalParameter :: number
print(parameter + 1)
end
穿過資料模型階層
在一些情況下,代碼碼需要穿過樹狀結執行階段中創建時的數據模型階層。這會呈現一些類型檢查的挑戰。在寫代碼時,不能將一個類輸入的數據模型階層作為類型定義。因結果,寫代碼時,僅有幾個類型資訊可用於一個數據模型個體、實例構。因此,在寫
一個挑戰的方法是將 any 投擲,然後精煉。例如:
local function enableVendor(vendor: Model)
local zonePart: BasePart = (vendor :: any).ZonePart
end
這種方法的問題是,它會影響可閱取性。 相反,項目使用了一個通用模組 getInstance 來穿過資料模型階層,其中發生錯誤 any 內部。
local function enableVendor(vendor: Model)
local zonePart: BasePart = getInstance(vendor, "ZonePart")
end
隨著類型引擎的資料模型進化,這種情況可能會不再需要。
使用者介面
植物 包括多種複合且簡單的 2D 使用者介面。這些包括非互動頭部顯示 (HUD) 項目,例如硬幣計數器和商購物等。
介面方法
您可以將 Roblox UI 稍微比較於 HTML DOM,因為它是一個描述用戶應該查看的內容的對象階級。創建和更新 Roblox UI 的方法是由 強制性 和 宣言式 方式分為兩個主要方法。
方法 | 優點和缺點 |
---|---|
強制性 | 在強制方法中,UI 與任何其他狀態層級都 |
宣言 | 在宣言方法中,將所需的狀態UI 的實例宣告為所需的狀態,並且將這種狀態的有效實現抽象為圖書館,例如 Roact</ |
植物 使用了一種 強制 的方法來探討在 Roblox 上顯示變形的方式。這與一種宣言式的方法不太可能。一些重複的 UI 結構和邏輯也被抽象成可重用的 零件 以避免對 Roblox 的常見陷阱。
高級建築
層和零件
在 植物 中,所有 UI 結構都是 Layer 或 Component。
- Layer 定義為層級最高的單一包裝預製 UI 結構在 ReplicatedStorage 中。一層可能包含一些組件,或者它可能完全包含自己的論理。例子的層級是庫存選單或頭頂顯示的金幣指示器。
- Component 是一個可重用的 UI 元素。當新的零件對象啟用時,它會從 ReplicatedStorage 中複製一個預製的樣板。零件可以在零件中自己包含其他零件。零件的範例是一個一般按鈕類或一個物品列的概念。
檢視處理
一個常見的 UI 管理問題是檢視處理。這個項目有一系列選單和 HUD 項目,其中一些聽取用戶輸入,並且需要仔細管理,當它們顯示或啟用時。
植物 以其 UIHandler 系統來處理當 UI 層可視或不可視時是否可視。遊戲中的所有植物都分類為 HUD 或 0>Menu0> ,視線管理由以下規則來管理:
- 啟用的狀態 Menu 和 HUD 層可以切換。
- 啟用 HUD 層級只顯示如果 Menu 層級啟用。
- 啟用 Menu 層存在於堆疊中,且只有一個 Menu 層顯示在時間。當啟用 Menu 層時,它會被插入到堆疊的前端,並且顯示。當啟用 1> Menu1> 層時,它會被從堆
這種方法直觀,因為它允許選單以歷史的方式瀏覽。如果從另一個選單開啟一個選單,關閉新選單會重新顯示舊選擇。
單一的 UI 層會自動註冊在 UIHandler 並且提供信號,當它的可見性變更時會發生。
進一步閱讀
從這個徹底的 Plant 計劃項目的全景中,您可能會想要探索這些指南,這些指南會進一步探索相關概念和主題。