下列系統是我們想要建立遊戲玩法的基礎Duvall Drive 的神秘。
遊戲狀態管理器
遊戲狀態管理器 / 遊戲狀態客戶 是體驗中最複雜的系統,因為它處理:
- 在大廳內啟動玩家,開始倒計時將玩家傳送到主要遊戲區域,並將玩家傳送到保留伺服器。
- 複製可疑的房間、並行傳輸它們、並將玩家傳送到特定的 CFrame 坐標。
- 抓取並放置密封機制。
- 鎖定和解鎖門。
- 正在初始化最終傳送到大廳並播放完成切場動畫。
我們將它實現為一個簡單的狀態機器(更新功能),狀態位於 DemoConfig (GameState枚)中。一些狀態處理初始載入/保留伺服器傳送,而其他狀態處理查找任務、啟動謎題並解決任務。注意,除了鎖定之外,我們嘗試不要有任務相關代碼在GameStateManager
遊戲狀態通常是服務器側面的,但當客戶端需要做某些事情,例如顯示倒數,傳承或禁用流媒體暫停 UI,服務器和客戶端 (GameStateClient) 通過遠程事件稱為 GameStateEvent 。 與大多數情況相同,事件載入的參數為「輸入置遊戲事件」(Config.GameEvents),其後是遊戲特
傳送遊戲狀態
有一組 3 個遊戲狀態,分別為 Warmup、InFlight 和 Cooldown,負荷在整個時效過程
隨著播放的處理, InFlight 執行,並且保持稍微閃爍的黑暗畫面。當兩個 <
當我們將玩家傳送回房間的正常狀態時,發生類似的 Warmup、InFlight 和 Cooldown 切換場景, TeleportWarmupBack 、 TeleportInFlightBack 和 TeleportCo
照明和氣氛遊戲狀態
我們知道要求每個房間的正常和�
鎖定門鎖遊戲狀態
事件管理器
EventManager 允許我們使用鑰匙框,例如:
- 交警察案件的屬性和屬性。
- 正在執行指令碼。
- 播放音音訊。
- 攝影機震動。
我們通常會使用基於軌道的 UI 的工具,但在此示範中,我們手動輸入了鑰匙和屬性名稱。 事件管理器 系統由多個腳本和一個事件/函數組成,包括:
- EventManager - 整體論理來創建和停止事件,以及伺服器端的行動。
- EventManagerClient - 客戶端操作。
- EventManagerModule - 共用於服務器和客戶端的程式碼。
- EventManagerConfig - 小型文件,包含幾個命令宣言。
- EventManagerDemo - 在遊戲指定的指令碼中定義所有此示範的實際事件。
- EventManagerEvent , EventManagerFunc - 遠程事件和可以從客戶端或伺服器側程式碼執行和停止事件的功能。這是其他系統設置、執行和停止事件的方式。
每個事件都有名稱、一個區段包含可選擇冷卻時間、功能在開始或結束時執行、事件參數和區段與斷點(在時間過程中處理任何數值或屬性的集合)、腳本(在鍵條框上處理註冊腳本)、攝影機搖動和播放音訊。
插件
插件讓對象屬性和屬性的變更完全從一個值到另一個值而不是在雙重跳過關卡框之間,例如下列代碼示例所示:TextLabel.TextTransparency 對象定義的 TextLabel
interpolants = {objectParam = "TextLabel",property = "TextTransparency",keys = {{value = 1},{time = .5, value = 0},{time = 2.25, value = 0},{time = 3, value = 1}}}
雖然我們可以定義哪些對象屬性或屬性屬於哪些像在下列代碼示例中, 我們想要能夠重用相同的事件在不同的「對象群」上, 以便能夠與客戶端上的流媒體和在運行時創建的對象一起工作。
object = workspace.SomeFolder.SomeModel
為了實現此目標,我們允許對對象名稱進行參考,並在事件開啟動時傳送名稱參數。要找到名稱的對象,我們允許指定一個名為 "根" 的參數,該參數會在事件開始時讓對象在此根下找到。例如,在下列代碼示例中, EventManager 嘗試在
params = {["RootObject"] = workspace.Content.Interior.Foyer["Ritual-DemoVersion"],},interpolants = {objectName = "Wander",attribute = "TimeScale",keys = {{value = 0.2}}}
我們允許將參數傳入事件的參數區,並且在事件開始時執行的腳本可以變更現有參數,或者在" param"欄中增加更多參數。 在下一個例子中,我們有 isEnabled 參數,其預
params = {isEnabled = false},interpolants = {{objectName = "FocuserGlow",property = "Enabled",keys = {{valueParam = "isEnabled"}}}
參數允許我們參照不存在的對象。例如,在以下代碼示例中,一個名為體驗件開始時執行的函數會創建一個物件,並在參數中設定 BlackScreenObject 輸入以指向已創建的物件。
{objectParam = "BlackScreenObject",property = "BackgroundTransparency",keys = {{value = 0},{time = 19, value = 0},{value = 1},}}
執行事件、事件實例和連接到觸發器
要執行事件,我們會或使用從客戶端使用遠程事件,或者從服務伺服器使用功能。 在下一個示例中,我們傳送了一些參數到 RootObject 和 isEnabled 事件。內部上,一個事件說明的實例被建立了,參數解決到實際對象,並且返回了一個 id 對應的事個體、實例。
local params = {RootObject = workspace.Content.Interior.Foyer["Ritual-DemoVersion"]["SealDropoff_" .. missionName],isEnabled = enabled}local eventId = eventManagerFunc:Invoke("Run", {eventName = "Ritual_Init_Dropoff", eventParams = params} )
我們可以停止執行事件,並且使用「停止」來呼叫函數:
eventManagerFunc:Invoke("Stop", {eventInstId = cooldownId} )
有「化妝」效果的(不是為所有玩家提供模擬)的客戶端,可以執行此類客戶端,這可能會導致較滑順的解釋。 在事件說明中,我們可以提供一個預設值對於所有客戶端,因此在伺服器 = 真的情況下,預設值是客戶端。 每個客戶端都可以通過設置自己的在伺服器上來覆蓋它。
要將運行中的事件連接到触發器,我們使用幫助功能 ConnectTriggerToEvent 或 ConnectSpawnedTriggerToEvent ,其中後者找到啟動器名稱。要允許使用不同的啟動器來啟動相同的事件,我們可以將 eventManagerFunc
事件參數
除了從指令碼中傳送的自訂事件參數外,當創建事件時可以選擇傳輸的其他資料包括玩家、回潮(當事件結束時呼叫)和回潮參數。一些事件只適用於一個玩家(對於發生在客戶端的事件),而其他事件則適用於全部 所有有人。要使它們只適用於一個玩家,我們使用 onlyTriggeredPlayer =
minCooldownTime 和 maxCooldownTime 可以定義事件的冷卻時間。perPlayerCooldown = true但我們在此示例中並未使用。如果需要冷��
呼叫指令碼
我們可以在 Scripts 區的特定鍵框中呼叫 Class.Script|Scripts。例如:
scripts = {{startTime = 2, scriptName = "EnablePlayerControls", params = {true}, onServer = false }}
在上一個範例中, 啟用玩家控制 Script 需要與事件管理器模組註冊,例如:
emModule.RegisterFunction("EnablePlayerControls", EnablePlayerControls)
RegisterFunction 必須在客戶端指令碼中呼叫為客戶端的函數,並在伺服器指令碼中呼叫為 onServer = true 。 功能本身會獲得事件實例和參數傳送,但在此情況下,只有一個參數傳送了真值。
local function EnablePlayerControls(eventInst, params)
播放音樂
我們在 聲音 區域的鑰匙框中對 非位置音頻 的播放有限制,例如:
sounds = {{startTime = 2, name = "VisTech_ethereal_voices-001"},}
注意事件結束回撥時會發生火災,但音效可能會在之後仍在播放。
攝影機震動
我們可以在 cameraShakes 區域中定義攝影機震動,例如:
cameraShakes = {{startTime = 15, shake = "small", sustainDuration = 7, targets = emConfig.ShakeTargets.allPlayers, onServer = true},}
「目標」只適用於啟動事件的玩家,例如所有Player 或玩家範圍內的玩家。 我們使用了第三方腳本來檢視攝影機震動,震動已預定: eventManagerDemo.bigShake 和 eventManagerDemo.smallShake。sustainDuration 也可
任務論理
有 7 個任務總共,只有 6 個使用封印。大多數任務都有共參數,但有些只適用於有封印的任務和傳送到腐敗房間。每個任務都有一個 在 DemoConfig 裡的入口,並且在 Config.Missions 地圖上有一組參數:
- MissionRoot : 一個夾名為所有非腐朽版本的物件的文件夾。
- 門 :鎖定直到玩家撿起一個印記。
- SealName / SolvedSealName : 非穩定的密碼和穩定的密碼名稱。
- SealPlaceName : 放置印章的地方。
- 放置印章位置預留名稱 : 位置預留對象在位置放置印章。
- TeleportPositionsName : 用於移動到腐敗房間時,以及回到正常區域時,玩家的傳送位置和方向的旋轉位置的描述。 同樣的名稱在兩個情況中都使用。
- CorruptRoomName : 根目錄 (相對於 ServerStorage) 中的房間名稱。 當任務開始時,會在暫時存儲中複製此房間,並且在任務結束時會毀滅。
- MissionCompleteButtonName : 在有損的房間中的詐騙按鈕,可立即完成任務。這是為了 測試目的。
- 作弊鑰匙 : 與數字相同的作弊鑰匙或 CtrlShift[數字] 。
一些任務流程在 GameStateManager 指令碼中,因為海豹和門提供主要遊戲流程,但大多數任務的特定邏輯都在 MissionsLogic 和 MissionsLogicClient 指令碼中,這些指令碼定義了幾種任務的
- 使用鎖上的鑰匙 - 第一個開門的任務。這種類型由 LockName 、 KeyName 定義。
- 匹配項目 - 4 個任務匹配項目。這種類型由 MatchItems 定義。
- 使用多層布料來穿上模特兒 - 1 個任務在閣樓有玩家收集三個項目。此類型由 DressItemsTagList 定義。
- 點擊項目完成 - 1 個任務有此輸入,其定義由 ClickTargetName 。
每個任務類型都有自己的 StartMissionFunc 和 CompleteMissionFunc 。開始功能通常從 2>MatchItem</
匹配項目的論理允許 "使用" (在持有時按一下) 標有 PuzzlePieceXX 標籤的項目。 有一些選項可用作參數在 MatchItems 圖中 (如果項目需要被應用在順序, 如果只有一個需要被應用的
正在抓取
我們開發了一個簡單的抓取系統來持有物體,通過將物體附在角色的右手臂上。抓取是在 <
在每個框架上,我們檢查是否有一個抓取嘗試正在進行中。如果玩家位於 reachDist 內,我們開始玩 ToolHoldAnim 。當玩家位於 maxGrabDistance 內時,客戶端發出要求向服務器發出實際抓取模型的請求 ( 1> player1> 功能)。
伺服器端的指令碼有 2 個主要功能:
- 拖曳 - 處理客戶端要求拖曳模型的請求。
- 發行 - 處理要求釋放已抓取的模型。
每個玩家持有的資訊都保留在 playerInfos 地圖中。 在抓取功能中,我們檢查這個模型是否被其他玩家抓取。如果是這樣,發送「EquipWorldFail」給客戶,並且取消抓取嘗試。 注意我們需要處理玩家抓取不同部分的同一 Model
如果允許抓取,則會創建兩個 Attachments ,其中一個在右手上,另一個在對象上使用物件
要發行已抓取的模型,客戶端指令連接到 HUD 屏幕匯總伺服器中的 按鈕在 GrabReleaseButton 按鈕。在服務伺服器上,發行刪除 Connected 和 Attachments ,恢復