Fusion VR Escape Room
您的遊戲圈會員資格提供所有所需的範例、SDK及支援,以在非常快速的時間內建立及啟動成功的多重玩家遊戲。對於非遊戲,我們的行業圈提供您完整的套件加上獨家的授權選項。
概述
Escape Room 範例展示了一個方法,說明如何在玩家及環境之間開發 物理互動。
核心 提供了基礎的互動及移動,同時 選用的模組 提供額外的功能及互動類型作為範例執行方式。
所有模組都有它們自己的資料夾,並且如果不需要的話可以移除。請注意,在這個範例中提供的主要場景(/Scenes/EscapeRoom Full.scene)使用了所有模組及元件,並且在刪除模組時它可能中斷。
在這份文檔的結尾單獨地說明模組。
模組:
- 物件提取
- 拍立得
- 白板
- 觀察器
- UI模組
- 槽模組
- 射擊模組
技術資訊
- 這個範例使用 伺服器/託管模式 拓撲,
- 專案已用Unity 2020.3.37f1開發,
在您開始之前
為了運行範例:
在PhotonEngine儀表板中建立一個Fusion應用程式帳號,並且將它貼上到即時設定(可從Fusion選單中到達)中的
App Id Fusion
欄位之中。在PhotonEngine儀表板中建立一個Voice應用程式帳號,並且將它貼上到即時設定中的
App Id Voice
欄位之中。然後載入
Start
場景並且按下Play
。然後,您可以啟動完整的場景或只測試一個特定的模組。
下載
版本 | 發佈日期 | 下載 | ||
---|---|---|---|---|
1.1.3 | 2022年10月20日 | Fusion VR密室逃脫1.1.3組建5 |
處理輸入
中繼任務
- 傳送:按下A或X按鈕,以顯示一個指標。在釋放時,您將傳送到任何已接受的目標
- 觸碰:簡單地將您的手/手指放到一個按鈕上以切換它
- 拿取:首先將您的手放在物件上,並且使用控制器拿取按鈕來拿取它
- 使用:按下選擇按鈕以使用已拿取的物件
請注意,應該支援HTC Vive控制器及Valve Index控制器。
桌面
鍵盤
- 行走:WASD
- 上/下:空白鍵和左Shift
滑鼠
- 旋轉:持續按下滑鼠右鍵並且移動滑鼠,以旋轉檢視點
- UI:使用左鍵以按下一個UI按鈕
- 拿取及使用(3D筆):將滑鼠放在物件上,並且使用滑鼠左鍵來拿取它。然後您可以使用鍵盤空白鍵來使用它
核心
範例的核心提供基於物理的與Rigidbodies
及Articulation bodies
的互動。
剛體用於在世界中的任何可拋擲的物件,同時Articulation Bodies
用於機械式系統如槓桿、轉盤、門及抽屜。
相機不是基於物理,而是只基於使用者輸入。已包含傳送移動(TeleportHandler.cs
)
互動工作流程概述:
- 輪詢及發送輸入:
- (
XRInput.cs
、LocalController.cs
、PCInput.cs
)
- (
- 使用剛體及力量,基於輸入來更新頭及手位置:
- (
PlayerSystem.cs
、XRObject.cs
)
- (
- 手讀取輸入並且傳送它到手工具及任何
IControllerInputReceiver
:- (
Hand.cs
、IControllerInputReceiver
)
- (
- 手工具及其他元件回應輸入:
- (
HandTool.cs
、TeleportHandler.cs
、InstantCameraInteractable.cs
)
- (
- 手工具檢查一個熱點是否在範圍之內,並且基於輸入來拿取/放開:
- (
HotspotCollector.cs
、Hotspot.cs
、HighlightBase.cs
)
- (
- 當開始/停止一個互動時,熱點告知可互動基礎及所有可互動物:
- (
InteractableBase.cs
、GrabbableBase.cs
)
- (
- 可拿取基礎使用力量來追蹤手位置:
- (
GrabbableBase.cs
、GrabbableRigidbody.cs
、GrabbableArticulation.cs
)
- (
輸入:
發送輸入
LocalInputBase.cs
它獨立於Fusion而作為一個DontDestroyOnLoad
物件存在,並且在遊戲階段之間持續存在。負責收集輸入並且發送它到Runner
。
- 這個的主要執行方式是
XRInput
。 PCInput
是一個針對更快速的迭代的偵錯輸入方法,但只有有限的功能。
接收輸入
PlayerSystem.cs
它透過XRObject.cs
來處理基本的玩家物件的定位(頭及手)。
XRObject更新剛體力量以追蹤輸入。
Hand.cs
它讀取控制器輸入並且傳送其到任何已註冊的東西,以便透過IControllerInputReceiver
來接受輸入
它針對基本的互動來明確傳送輸入到HandTool.cs
。
- 希望接收輸入的系統可以執行
IControllerInputReceiver
並且透過手來註冊(比如TeleportHandler.cs
) - 已拿取物件可以暫時註冊(比如
InstantCameraInteractable.cs) -> Hand.AddInputBehaviour() / Hand.RemoveInputBehaviour()
互動
手工具:
- 針對拿取/放下來讀取輸入
- 使用
HotspotCollector
以找到Hotspots
並且強調顯示它們 - 拿取/放下
Hotspots
熱點收集器:
- 基於圖層及標籤來尋找半徑之內的
Hotspots
- 篩選器順序:
Layer, Tag, HighlightPriority, Distance
- 強調顯示被懸停的熱點(
HotspotCollector -> Hotspot -> HighlightBase
)
熱點:
它代表互動點,HandTools
可以透過它與物件互動。
- 每個熱點一隻手,如果一個物件需要可被多隻手拿取,請新增更多熱點。如果需要的話,這些熱點可以相同。
- 傳送開始/停止互動調用到在相同遊戲物件上的
IInteractable
以及上層中的InteractableBase
。
可互動基礎:
針對可互動物件的基礎層級。
- 取得開始/停止互動調用
針對可拿取物件的物件層級:
Root
:InteractableBase, Rigidbody, NetworkRigidbody, Highlight, NetworkObject, BodyProperties
Visuals
Highlight Visuals
Collision
Hotspot
:Hotspot, GrabbableRigidbody / AttachmentRigidbody, Collider (Trigger)
主體屬性
指派一個BodyPropertyCollection
(可指令碼物件)以控制物件被拿取時的行為方式。
主體屬性集合
它含有BodyPropertyData
的一個陣列,其取決於在同一時間有多少手拿取物件(0:沒有拿取,1:一隻手拿取,n:n隻手拿取),來應用於已拿取物。
在這個方式下,物件可以安排成,對於一隻手而言太大或太重,但是對於兩隻手或多隻手而言很容易處理。
- BodyPropertyData:
Mass
Drag
AngularDrag
JointFrictionArticulation
(只適用於Articulation bodies
)UseGravity
OverrideInertia
(如果設定,慣性將被設定為InertiaWhenGrabbedScale * Vector3.one
)InertiaWhenGrabbedScale
(控制物件抵抗旋轉改變的程度)VelocityExtrapolation
(當向物件施加新的力量時,目前的速度被抵消的程度,也作為力量本身的乘數)TorqueExtrapolation
(當向物件施加新的力量時,目前的角速度被抵消的程度)
可拿取基礎(在熱點上):
當拿取物件時,它將物件追蹤到手的位置。
根據主體類型,來選擇適當的執行方式:
GrabbableRigidbody
GrabbableArticulation
附加剛體(在熱點遊戲物件上):
附加剛體物件到一個HandTool
。這用於需要非常快速的輕量物件。(比如白板筆、拍立得照片、手槍)
為了讓這個順利工作,物件需要一個特定的架構:
Root
Visuals
Collision/Logic
Colliders
Hotspot
:AttachmentRigidbody, Trigger Collider
,
在拿取時,Collision
及Visuals GameObjects
被直接地附加到Hand
,並且如同它們是它的一部分一樣。
聚焦點
視覺效果確認,一個可互動對象在手的範圍之內,並且可以與之互動。
值提供器/讀取器
一個value provider
是一個一般性元件,以將資訊從一個系統傳輸到另一個系統,並且在沒有專用元件的情況下建立簡單的邏輯鏈。
示例:
- 從一個連接主體來讀取一個值(
ArticulationBodyValueReaderFloat.cs
) - 將它和一個閾值比較(
ValueLogicIntCompare.cs
) - 使用該值以驅動其他東西(
ArticulationBodyDriverSetLimits.cs
)
串流材質管理器
針對模組所需。系統使用OnReliableData
回調來透過網路來發送材質資料。需要與Runner
位於同一個遊戲物件上。
忽略碰撞
有時候需要在特定物件之間使用Physics.IgnoreCollision
API來忽略碰撞。
IgnoreCollision.cs
:靜態忽略。在遊戲遊玩時不會改變。附有指令碼的遊戲物件上的碰撞器忽略所有清單中的碰撞器。NetworkColliderCollection.cs
:可以忽略的碰撞器的群組。NetworkIgnoreCollision.cs
:使用AddIgnore()
及RemoveIgnore()
以忽略針對所有已指派的NetworkColliderCollections
的本機碰撞器。
連接主體
這個範例大量使用Articulation bodies
。它們是一個穩定的方法來在Unity中模擬已連線主體,並且在這裡用於槓桿、按鈕、轉盤及抽屜之類的東西。
NetworkArticulationBody
的執行方式在這個範例中不是完整的功能。
請參照https://docs.unity3d.com/2020.3/Documentation/ScriptReference/ArticulationBody.html
核心元件
NetworkArticulationBodyRoot
放置於Root
上NetworkArticulationBody
放置於所有下層Articulation bodies
上。NetworkedArticulationDrive
將Articulation body
的驅動屬性連線。ArticulationBodyValueReaderSingleAxis
可以讀取將在值提供器系統中使用的一個Articulation body
的值。ArticulationBodySingleAxisSoftSnap
動態地設定驅動屬性以貼齊到特定點。比如:轉盤上的凹口、抽屜上的緩衝關閉、槓桿上的固定有效位置。
本機內插補點
NetworkArticulationBody
繼承於NetworkTransform
並且提供一個穩定的本機內插補點。為了讓這個工作如預期進行,需要設定Interpolation targets
,並且必須鏡像Articulation bodies
的階層。
架構
Root
:NetworkObject, ArticulationBody, NetworkArticulationBodyRoot, InteractableBase
Visuals
:跟隨相同於Articulation system
的階層。Child
:ArticulationBody, NetworkArticulationBody
,(選擇性:BodyProperties* ArticulationBodySoftSnap, ArticulationBodyValueReaderSingleAxis
)Child
:(選擇性,與上述相同的元件)Hotspot
:Hotspot, Trigger Collider, GrabbableArticulation
在Hotspot
之下至少需要BodyProperties
元件一次。各個Hotspot
將在其上層中搜尋第一個。
連接一個連接主體到一個剛體
如果一個Articulation
需要被附加到一個rigidbody
,它需要透過一個Joint
(比如一個ConfigurableJoint
)來完成。
已知問題
- 請勿使用
Articulation bodies
上的Compute Parent Anchor
:當客戶端不是在初始狀態時,它們將在客戶端上有所不同。 Articulation bodies
中的錯誤導致對於連接階層的不同的索引。我們需要它們在客戶端之間保持一致,以適當地同步它們。在NetworkArticulationBodyRoot.Spawned()
中執行暫時性修復。這個將重新排序它們為正確的順序。- 這個「駭客修復」遺憾地中斷連接任何在
rigidbodies
及articulation bodies
之間可設定的接點(比如射擊模組中的手槍)。因此針對這個物件,停用重新排序。
- 這個「駭客修復」遺憾地中斷連接任何在
模組
物件拉取
按住拿取按鈕,指向物件並向上輕彈手,以將物件拉向您。
要求:
- 在
HandTool
上的ObjectPullCollector
預製件 - 在一個
Hotspot
上的ObjectPullBody
元件,也需要一個GrabbableBase
元件(GrabbableRigidbody
)
拍立得
拍立得顯示了更複雜的可互動物件(額外輸入)以及OnReliableData
回調,以透過網路發送更大的資料。
InstantCameraInteractable
:透過擷取相機轉譯材質來拍照。針對該材質來繁衍一個列印成品。InstantCameraPrintout
:等待材質到達,直到它使用一個著色器淡出。此外,它被重新發送給在一開始拍照之後加入的客戶端。InstantCameraPicture.shader
:簡單的表面著色器,以在接收照片之後淡出照片
白板
可在上面繪畫的白板。
WhiteboardSurface
:可在上面繪畫的表面。將其目前的狀態儲存為一個Rendertexture
。WhiteboardMarker
:可互動物。查看表面(邊界)並且告知其在關閉時畫一個筆劃。
著色器
WhiteboardFill
:以一個給定的材質來初始化白板WhiteboardStroke
:在轉譯材質上畫一條直線WhiteboardStrokeEraser
:在轉譯材質上擦除出一條直線
觀察器
滑鼠/鍵盤控制器,以觀察VR玩家及與世界互動。
要求:
- 觀察器使用一個不同的輸入裝置及玩家預製件(請參見
StartModulebutton.cs
) - 新增
IObserverable
元件到您希望操控的物件ObserverableGrabRigidbody
以拖動剛體ObservableArticulationBodyButton
新增一個持續的力量(可以被切換)ObservableArticulationBodyDrawer
新增一個在一段時間內持續的力量並且保持布林值狀態(當再次被按下時翻轉力量)ObservableArticulationBodyLever
在按住時新增一個力量ObservableValueProviderOverride
直接地設定浮點值(透過覆寫切換)。必須被插入到邏輯鏈之中,以產生一個效果。
將使用CreateObserverUi()
來針對所有IObserverable
元件自動地建立浮動UI視窗。在這裡可以執行自訂UI。在UI_ObserverItem
預製件中定義預設元件。
觀察器輸入
可觀察物件的操控直接地被作為輸入來發送。(請參見ObserverInput / ObserverInputHandler.cs
)
輸入的組成包含:
NetworkBehaviourId
以識別被修改的元件。Value
(目前只有浮點、布林值及向量3)
UI模組
基本的已連線游標及UI互動。它使用Unity的「已追蹤裝置射線投射器」及「輸入系統UI輸入模組」以在本機註冊畫布互動並且發送其為輸入。
為與PC裝置使用,也需要呈現「圖形射線投射器」。
LocalController.OnInputUi()
- 儲存被指向的畫布到
InputDataController.CanvasBehaviour
(NetworkBehaviourId
) - 儲存與畫布碰撞的指標的世界位置到
InputDataController.CursorPosition
- 如果指標命中附有一個
NetworkedUiButton
元件的遊戲物件,它被儲存到InputDataController.UiInteractionBehaviour
(NetworkBehaviourId
)
- 儲存被指向的畫布到
UiPointerHandler
- 讀取輸入並且發送指標位置及互動到相應的
NetworkedCanvas
及NetworkedUiButton
- 讀取輸入並且發送指標位置及互動到相應的
槽模組
可開槽的系統用於將物件平滑地引導到預定位置/方向。想一想:插槽、按鍵、拼圖塊、磁帶匣等等。
為了完成這個,GrabbableBase.cs
有各種`elegates及回調,以覆寫物件在被拿取時想要到達的目標位置。
RotationDelegate GetRotationMethod
GrabAndTargetPositionDelegate GetGrabAndTargetPositionMethod
UnityAction PreTrackCallback
UnityAction PostTrackCallback
可開槽的
Slottable
元件覆寫這些回調,並且取決於與Slot
相關的目前目標,來改變所需的位置及旋轉。
PreTrack
:檢查是否距離一個被影響的槽足夠近,並且計算兩個要素以供之後使用_slotFactorHand
:手到槽的距離。這用於決定物件應該採用的目標位置及旋轉。_slotFactorObject
:物件到槽的距離。這用於決定物件是否被放入槽內並且應該被放開,物件是否仍然可以被旋轉(>RotationLockThreshold
)
PostTrack
:檢查物件是否應該被卸除。
物件要素是物件的實際位置,而手要素是在應用任何力量之前根據手的輸入為止來確定。
Slottable
取決於_slotFactorHand
及_slotFactorObject
,來取得所需位置/旋轉形式Slot
。
額外元件:
GrabbableRidgidbody
HotspotCollector
以找出有效的槽。- (選擇性)
NetworkColliderCollection
:指派在靠近Slot
時忽略的colliders
(如果設定的話,針對槽中設定的碰撞器,以及與熱點互動的手來忽略)
槽
Slot
元件含有物件在範圍內應有的行為的資料。
Curves
決定從物件位置到所需槽位置的位置/旋轉的改變的突然程度。
需要至少一個標記為觸發程序的碰撞器,且該碰撞器具有正確的圖層,以便由Slottables HotspotCollector
拾取。
HotspotsToIgnoreHandCollisionWhenGrabbed
可用於忽略拿取熱點的手。
當空間很緊密,並且您希望忽略拿取Slot
的手時,這將很有用。
示例:一隻手抓著手槍,彈匣和彈匣槽離手很近,因此在正常情況下彈匣會與手碰撞。
也在這裡新增手柄的熱點以忽略手碰撞。
UseLineForRange
可以設定為使用線段來作為可開槽的目標,而非使用一個點。
DropType
決定當一個物件被成功地放入槽時,應該如何處理物件。
Kinematic
:用於將物件放入靜態等級幾何的槽。物件成為運動學的,並且除非再次被拿取,否則無法移動。Destroy
:物件被取消繁衍- (
TeleportOffsite
:還沒有被良好的測試,可用於在剛體上被放入槽的物件,這樣在再次拿取它們時可以立即重新定位它們。將需要在接收端有一些魔法,以轉譯可開槽物到位置上。)
Slots
決定Slottable
是否可以被放入槽,其檢查各種參數:
SlotType
符合該槽- 沒有已出現其他
Slottable
(或是否已設定AllowMultipleObjects
) - 可以在(
Slot.CanSlot()
)中新增更多限制
元件:
NetworkIgnoreCollision
(選擇性):可以針對已放入槽的物件來忽略被指派的碰撞器。這可以用於讓Slottable
可以穿過牆,而手本身或其他不符合槽的物件無法穿過。
射擊模組
如何完成類似於槍的可互動物的範例執行方式。提供兩個示例。
散彈槍
- 一次發射多顆子彈
- 使用
GrabbableRigidbody
的較大的物理主體 - 重新裝填單一彈殼
手槍
- 使用連接主體的手動扳機
- 重新裝填完整的彈匣
- 使用
AttachmentRigidbody
拿取
須知事項
場景設定
Connector.cs
及LocalRigSpawner.cs
在每個場景中出現。如果在場景中沒有繁衍或預先定義或本機裝置,它將繁衍一個(xr或pc)並且建立一個執行器及連線。
針對Start.scene
,執行器以單一玩家模式開始。這樣做,我們可以完整存取到我們的遊戲碼,而不需要額外的工作。
手及物件視覺效果內插補點:
在不同的情況下,NetworkRigidobdy
的常規內插補點在Render()
中被覆寫
- 如果手沒有拿取或碰撞任何東西,則設定本機控制器位置。這避免了使用力量(
XRObject.cs, UpdateRender()
)來移動手物件時發生的任何延遲。 - 如果手正在拿取一個物件,針對視覺效果手位置,該物件有實際授權。手的位置是相對於被拿取物件的位置,並考慮當互動開始時的初始位移(
GrabbableBase.cs Render()
)。 - 如果放開物件,視覺效果手被拉回實際位置(
HandTool.cs Render()
)
常見問題
我的可拿取物件到處飛來飛去/怪異地旋轉:檢查下列設定,以確保力量不會積聚或振蕩。在遊玩時可以在編輯器中調整它們。
BodyProperties
:各個可拿取物件有一個BodyProperties
元件,其中設定及同步物理屬性。屬性本身是可指令碼的物件,並且可以在相似的物件上重複使用。調整值以取得您希望物件有的重量及感覺。當物件失去控制時,它可能是位置或旋轉元件振蕩。在GrabbableBase
元件中,您可以切換是否要追蹤位置和/或旋轉,以找出造成問題的原因並且相應地進行調整。- 位置:質量、拖曳。使用重力k、速度外推
- 旋轉:質量、角拖曳、慣性、扭力外推
GrabbableRidgidbody
,「將質量中心設定為拿取點」:在Grabbable Rigidbody
元件中,您可以切換是否將質量中心設定為在拿取時的拿取點。這對於物件的旋轉特性有重大的影響,並且可能需要不同的BodyProperties
。如果具有相同的BodyProperties
的類似物件的行為不同,這可能是問題的來源。