訪問 session 中的 AR 功能組件
在運行中的 session 裏,可以通過 Assembly 屬性訪問各個功能組件。本文介紹瞭如何訪問這些組件,以及訪問時需要注意的事項。
開始之前
- 通過 ARSession 簡介 瞭解 session 的基本概念、組成和工作流程
- 瞭解如何 創建 session
編輯時或啓動前配置 AR 組件
有些時候,某些組件選項(比如 DesiredFocusMode)必需在組件啓動前配置,如果不想在 session 啓動後再手動配置並啓動組件,一個簡單的方法是在 session 組裝前對所有可能使用的 frame source 組件進行配置。組裝過程會保留這些組件中的一個或多個,並應用其配置。
這時可以使用 FindAnyObjectByType<T>() 或 GetComponent<T>() 等任何 Unity 基本方法找到組件,然後對組件進行配置。
附註
通過這種方法獲取到的 AR 組件是否會在運行時被包含在 session 中是不確定的。因此必需對所有可能的情況進行配置。
例如,下面的代碼展示了在 session 組裝前修改所有 frame source 組件的對焦模式的過程:
void Awake()
{
var allFrameSources = Session.GetComponentsInChildren<FrameSource>();
foreach (var source in allFrameSources)
{
if (source is CameraDeviceFrameSource)
{
((CameraDeviceFrameSource)source).DesiredFocusMode = autoFocus ? CameraDeviceFocusMode.Continousauto : CameraDeviceFocusMode.Medium;
}
else if (source is MotionTrackerFrameSource)
{
((MotionTrackerFrameSource)source).DesiredFocusMode = autoFocus ? MotionTrackerCameraDeviceFocusMode.Continousauto : MotionTrackerCameraDeviceFocusMode.Medium;
}
else if (source is ARCoreFrameSource)
{
((ARCoreFrameSource)source).DesiredFocusMode = autoFocus ? ARCoreCameraDeviceFocusMode.Auto : ARCoreCameraDeviceFocusMode.Fixed;
}
else if (source is ARKitFrameSource)
{
((ARKitFrameSource)source).DesiredFocusMode = autoFocus ? ARKitCameraDeviceFocusMode.Auto : ARKitCameraDeviceFocusMode.Fixed;
}
else if (source is AREngineFrameSource)
{
((AREngineFrameSource)source).DesiredFocusMode = autoFocus ? AREngineCameraDeviceFocusMode.Auto : AREngineCameraDeviceFocusMode.Fixed;
}
else if (source is ThreeDofCameraDeviceFrameSource)
{
((ThreeDofCameraDeviceFrameSource)source).DesiredFocusMode = autoFocus ? ThreeDofCameraDeviceFocusMode.Auto : ThreeDofCameraDeviceFocusMode.Fixed;
}
else if (source is InertialCameraDeviceFrameSource)
{
((InertialCameraDeviceFrameSource)source).DesiredFocusMode = autoFocus ? InertialCameraDeviceFocusMode.Auto : InertialCameraDeviceFocusMode.Fixed;
}
else if (source is ARFoundationFrameSource)
{
cameraManager.autoFocusRequested = autoFocus;
}
}
}
上述過程也可以在編輯器中完成,同樣需要對所有組件都進行配置:

其中 ARCoreARFoundationFrameSource 和 ARKitARFoundationFrameSource 兩個 frame source 對應的配置在 Main Camera 的組件上。
警告
通過這種方法獲取到的 AR 組件只能用於運行前的配置。
由於組裝過程會對 AR 組件進行篩選,通過場景樹獲取的 AR 組件可能並沒有被包含在 session 中,無法正常工作。
運行中使用組裝好的 AR 組件
session 中運行的 AR 組件是在組裝後才確定的。在組裝完成之前,任何 AR 組件都不能使用。組裝好的 AR 組件可以通過 Assembly 屬性訪問。
Assembly 在 session 的狀態 >= Assembled 的條件下可以使用。詳細來說,Assemble() 方法執行完成後,Assembly 屬性纔會被賦值,可以通過它訪問 session 組件。在 session 停止或損壞後,Assembly 屬性會被清空,無法再訪問組件。
可以在腳本中檢測 session 的 State 來判斷當時是否可以訪問 AR 組件:
if (Session.State >= ARSession.SessionState.Ready)
{
// Assembly 可以使用
}
else
{
// Assembly 不能使用
}
也可以通過訂閱 StateChanged 事件來獲取 session 的狀態變化,從而在合適的時間點訪問 AR 組件。一般來說爲了能捕獲到 Ready 狀態,需要在 session start 之前訂閱 StateChanged 事件,通常在 Awake() 中完成訂閱是安全的:
void Awake()
{
Session.StateChanged += (state) =>
{
if (Session.State == ARSession.SessionState.Ready)
{
// Assembly 可以使用,在這之後 Assembly 一直可以訪問,直至 session 停止或損壞
}
else if (Session.State < ARSession.SessionState.Ready)
{
// Assembly 不能使用,在這之後 Assembly 一直不可訪問,直至 session 重新啓動
}
else
{
// Assembly 可以使用,通常不需要處理
}
};
}
注意
如果通過 FindAnyObjectByType<T>() 或 GetComponent<T>() 等方法獲取到 AR 組件是一定會被包含到 session 中的,也可以在運行時中使用。
只是存儲這些組件的引用是安全的,但在使用這些組件時必須確保 session 處於運行狀態且這些組件被正確包含在 session 中,否則可能會引發異常或不可預期的行爲。
session 啓動前和停止後,這些組件是無法工作的。建議即使在這樣的用法中,也要關注 session 的 State 和 StateChanged 事件。
訪問 frame source 組件
可以使用 ARAssembly.FrameSource 屬性訪問 frame source 組件。在一個正常運行的 session 中,ARAssembly.FrameSource 有且只有一個。
在使用 session 時,通常需要通過訪問 ARAssembly.FrameSource 才能確定運行時實際使用的 frame source 組件類型,從而訪問該組件特有的屬性和方法。
例如,下面的代碼展示瞭如何根據 frame source 的不同使用不同的平面檢測方法:
void PlaceObject(Vector2 touchPosition)
{
if (Session.Assembly.FrameSource is MotionTrackerFrameSource)
{
Ray ray = Session.Assembly.Camera.ScreenPointToRay(touchPosition);
if (Physics.Raycast(ray, out var hitInfo))
{
TouchRoot.transform.position = hitInfo.point;
}
}
else if (Session.Assembly.FrameSource is ARFoundationFrameSource)
{
var raycastManager = Session.Assembly.Origin.Value.GetComponent<UnityEngine.XR.ARFoundation.ARRaycastManager>();
var hits = new List<UnityEngine.XR.ARFoundation.ARRaycastHit>();
if (raycastManager.Raycast(touchPosition, hits, UnityEngine.XR.ARSubsystems.TrackableType.PlaneWithinPolygon))
{
var hitPose = hits[0].pose;
TouchRoot.transform.position = hitPose.position;
}
}
}
訪問 frame filter 組件
可以使用 ARAssembly.FrameFilters 屬性訪問 frame filter 組件。在一個正常運行的 session 中,ARAssembly.FrameFilters 列表中的任何一個類型的組件都有可能有多個。
例如,下面的代碼展示瞭如何獲取 session 中的一個 MegaTrackerFrameFilter 並註冊對應的事件:
var megaTracker = session.Assembly.FrameFilters.Where(f => f is MegaTrackerFrameFilter).FirstOrDefault() as MegaTrackerFrameFilter;
if (megaTracker)
{
megaTracker.LocalizationRespond += (response) =>
{
};
}
訪問 camera 組件
可以使用 ARAssembly.Camera 屬性訪問 camera 組件。如果場景中有多個 camera,這時一個找到 AR 使用的攝像機的快捷方式。
例如,下面的代碼展示瞭如何獲取 session 中的 camera 並對場景中的物體進行射線檢測:
var ray = Session.Assembly.Camera.ScreenPointToRay(screenPoint);
if (Physics.Raycast(ray, out var hitInfo))
{
TouchRoot.transform.position = hitInfo.point;
};
訪問 origin 組件
可以使用 ARAssembly.Origin 屬性訪問 origin 組件。
例如,下面的代碼展示瞭如何獲取 session 中的 origin 並將一個代表當前攝像機位置和朝向的錐體顯示在場景中:
if (session.Assembly.Origin.OnSome)
{
GameObject frustum = Instantiate(CameraFrustumPrefab, session.Assembly.Camera.transform.position, session.Assembly.Camera.transform.rotation);
frustum.transform.SetParent(session.Assembly.Origin.Value.transform);
}
需要注意的是,這裏需要先判斷 ARAssembly.Origin 是否存在。
附註
ARAssembly.Origin 只在啓用了運動跟蹤功能的 session 中存在。
訪問 CameraImageRenderer 組件
可以使用 ARAssembly.CameraImageRenderer 屬性訪問 CameraImageRenderer 組件。
例如,下面這段代碼可以獲取物理相機圖像的 RenderTexture:
RenderTexture renderTexture;
void Awake()
{
Session.StateChanged += (state) =>
{
if (state == ARSession.SessionState.Ready && Session.Assembly.CameraImageRenderer.OnSome)
{
Session.Assembly.CameraImageRenderer.Value.RequestTargetTexture((_, texture) => renderTexture = texture);
}
};
}
需要注意的是,這裏需要先判斷 ARAssembly.CameraImageRenderer 是否存在。
附註
ARAssembly.CameraImageRenderer 只在由 EasyAR 進行畫面繪製的 session 中有效。一般來說,使用 AR Foundation 或頭顯時是無效的,這時物理相機畫面的繪製由 AR Foundation 或頭顯 SDK 完成。
訪問 FrameRecorder 組件
可以使用 ARAssembly.FrameRecorder 屬性訪問 FrameRecorder 組件。
例如,下面這段代碼可以啓動錄製,文件存儲位置取決於配置,默認會存儲在應用內存儲目錄中:
if (session.Assembly.FrameRecorder.OnSome)
{
var frameRecorder = session.Assembly.FrameRecorder.Value;
frameRecorder.enabled = true;
}
需要注意的是,這裏需要先判斷 ARAssembly.FrameRecorder 是否存在。
附註
ARAssembly.FrameRecorder 在少數情況下,比如使用 FramePlayer 時是不能使用的。
後續步驟
- 瞭解如何 獲取 session 的運行結果,這些結果中包含了 AR 組件的運行輸出
- 另外,您還可以通過下面這些示例來了解組件的訪問:
- Workflow_ARSession 示例 展示了各種組件的訪問和使用方法
相關主題
- 幀數據源 描述了 frame source 以及運行時的選取方式
- XR Origin 描述了 AR 場景中 origin 組件的用途
- Camera 描述了 AR 場景中 camera 組件的用途
- 錄製EIF文件 描述了 FrameRecorder 的詳細使用方法