포인터 — MRTK2
이 문서에서는 실제로 포인터 입력을 구성하고 응답하는 방법을 설명합니다. 상위 수준에서 여러 포인터를 제어하는 방법을 더 잘 이해하려면 포인터 아키텍처를 참조하세요.
포인터는 새 컨트롤러가 검색될 때 런타임에 자동으로 인스턴스화됩니다. 둘 이상의 포인터를 컨트롤러에 연결할 수 있습니다. 예를 들어 기본 포인터 프로필을 사용하면 Windows Mixed Reality 컨트롤러는 각각 일반 선택 및 텔레포트에 대한 선과 포물선 포인터를 모두 가져옵니다.
포인터 구성
포인터는 를 통해 MixedRealityPointerProfile
MRTK의 입력 시스템의 일부로 구성됩니다. 이 유형의 프로필은 MRTK 구성 검사기의 에 할당됩니다 MixedRealityInputSystemProfile
. 포인터 프로필은 커서, 런타임에 사용할 수 있는 포인터 유형 및 해당 포인터가 서로 통신하여 활성 상태인지 결정하는 방법을 결정합니다.
포인팅 익스텐트 - 포인터가 GameObject와 상호 작용할 수 있는 최대 거리를 정의합니다.
포인팅 레이캐스트 레이어 마스크 - 지정된 포인터가 상호 작용할 수 있는 가능한 GameObject 및 시도할 상호 작용 순서를 결정하는 LayerMasks의 우선 순위가 지정된 배열입니다. 이는 포인터가 다른 장면 개체 앞에 먼저 UI 요소와 상호 작용하도록 하는 데 유용할 수 있습니다.
포인터 옵션 구성
기본 MRTK 포인터 프로필 구성에는 다음 포인터 클래스 및 연결된 프리팹이 기본적으로 포함됩니다. 런타임 시 시스템에서 사용할 수 있는 포인터 목록은 포인터 프로필의 포인터 옵션 아래에 정의되어 있습니다. 개발자는 이 목록을 활용하여 기존 포인터를 다시 구성하거나, 새 포인터를 추가하거나, 포인터를 삭제할 수 있습니다.
각 포인터 항목은 다음 데이터 집합으로 정의됩니다.
컨트롤러 유형 - 포인터가 유효한 컨트롤러 집합입니다.
- 예를 들어 PokePointer 는 손가락으로 개체를 "찌르는" 역할을 하며 기본적으로 관절형 손 컨트롤러 유형만 지원하는 것으로 표시됩니다. 포인터는 컨트롤러를 사용할 수 있게 될 때만 인스턴스화되며, 특히 컨트롤러 유형 은 이 포인터 프리팹을 만들 수 있는 컨트롤러를 정의합니다.
손수 - 포인터가 특정 손(왼쪽/오른쪽)에 대해서만 인스턴스화될 수 있습니다.
참고
포인터 항목의 Handedness 속성을 None 으로 설정하면 목록에서 해당 포인터를 제거하는 대신 시스템에서 효과적으로 사용하지 않도록 설정합니다.
- 포인터 프리팹 - 지정된 컨트롤러 유형과 손수와 일치하는 컨트롤러가 추적되기 시작하면 이 프리팹 자산이 인스턴스화됩니다.
컨트롤러와 연결된 여러 포인터가 있을 수 있습니다. 예를 들어 (Assets/MRTK/SDK/Profiles/HoloLens2/)에서 DefaultHoloLens2InputSystemProfile
관절형 손 컨트롤러는 PokePointer, GrabPointer 및 DefaultControllerPointer (즉, 손 광선)와 연결됩니다.
참고
MRTK는 Assets/MRTK/SDK/Features/UX/Prefabs/Pointers에 포인터 프리팹 집합을 제공합니다. Assets/MRTK/SDK/Features/UX/Scripts/Pointers 또는 를 구현하는 다른 스크립트에 포인터 스크립트 중 하나가 포함된 경우 새 사용자 지정 프리팹을 IMixedRealityPointer
빌드할 수 있습니다.
커서 구성
응시 커서는 편집기 내의 GazeCursorPrefab
속성을 MixedRealityInputSystemProfile
통해 직접 구성할 수 있습니다. 다른 포인터에 사용되는 커서를 구성하려면 해당 BaseControllerPointer
의 필드에 사용되는 프리팹을 CursorPrefab
변경해야 합니다. 커서를 프로그래밍 방식으로 변경하려면 해당 IMixedRealityPointer
동작에서 BaseCursor
속성을 수정합니다.
커서 동작 구현 예제는 Assets/MRTK/SDK/Features/UX/Prefabs/Cursors 의 커서 프리팹을 참조하세요. 특히 DefaultGazeCursor 는 컨텍스트 상태에 따라 커서의 그래픽을 변경하는 강력한 구현을 제공합니다.
기본 포인터 클래스
다음 클래스는 위에서 설명한 기본 MRTK 포인터 프로필 에서 사용할 수 있고 정의된 기본 MRTK 포인터입니다. Assets/MRTK/SDK/Features/UX/Prefabs/Pointers 아래에 제공되는 각 포인터 프리팹에는 연결된 포인터 구성 요소 중 하나가 포함됩니다.
먼 포인터
LinePointer
기본 포인터 클래스인 LinePointer는 포인터 방향으로 입력 소스(즉, 컨트롤러)에서 선을 그리고 이 방향으로 단일 광선 캐스트를 지원합니다. 일반적으로 및 텔레포트 포인터와 같은 ShellHandRayPointer
자식 클래스는 주로 일반적인 기능을 제공하는 이 클래스 대신 인스턴스화되고 활용됩니다(또한 텔레포트가 끝날 위치를 나타내는 선을 그립니다).
Oculus, Vive 및 Windows Mixed Reality 같은 모션 컨트롤러의 경우 회전이 컨트롤러의 회전과 일치합니다. HoloLens 2 관절형 손과 같은 다른 컨트롤러의 경우 회전은 시스템에서 제공하는 손 포인팅 포즈와 일치합니다.
CurvePointer
CurvePointer 는 곡선을 따라 다단계 광선 캐스트를 허용하여 LinePointer 클래스를 확장합니다. 이 기본 포인터 클래스는 선이 지속적으로 포물선으로 구부러지는 텔레포트 포인터와 같은 곡선 인스턴스에 유용합니다.
ShellHandRayPointer
에서 LinePointer
확장되는 ShellHandRayPointer 구현은 MRTK 포인터 프로필의 기본값으로 사용됩니다. DefaultControllerPointer 프리팹은 클래스를 ShellHandRayPointer
구현합니다.
GGVPointer
GGV(응시/제스처/음성) 포인터라고도 하는 GGVPointer는 주로 응시 및 에어 탭 또는 응시 및 음성 선택 상호 작용을 통해 HoloLens 1 스타일 모양 및 탭 상호 작용을 지원합니다. GGV 포인터의 위치와 방향은 헤드의 위치와 회전에 의해 구동됩니다.
TouchPointer
TouchPointer는 Unity Touch 입력(예: 터치 스크린)을 사용합니다. 이는 화면을 만지는 행위가 카메라에서 장면의 잠재적으로 먼 위치로 광선을 캐스팅하기 때문에 '먼 상호 작용'입니다.
MousePointer
MousePointer는 원거리 상호 작용을 위해 화면을 월드 레이캐스트에 작동하지만 터치 대신 마우스를 사용합니다.
참고
마우스 지원은 기본적으로 MRTK에서 사용할 수 없지만 MRTK 입력 프로필에 형식 MouseDeviceManager
의 새 입력 데이터 공급자를 추가하고 를 데이터 공급자에 할당하여 MixedRealityMouseInputProfile
사용하도록 설정할 수 있습니다.
근거리 포인터
PokePointer
PokePointer는 "터치 가능한 거의 상호 작용"을 지원하는 게임 개체와 상호 작용하는 데 사용됩니다. 연결된 스크립트가 있는 GameObjects입니다 NearInteractionTouchable
. UnityUI의 경우 이 포인터는 NearInteractionTouchableUnityUIs를 찾습니다. PokePointer는 SphereCast를 사용하여 가장 가까운 터치 가능한 요소를 확인하고 누를 수 있는 단추와 같은 전원을 공급하는 데 사용됩니다.
구성 요소를 사용하여 GameObject를 NearInteractionTouchable
구성할 때 터치 가능하도록 설정해야 하는 단추 또는 기타 개체의 앞을 가리키도록 localForward 매개 변수를 구성해야 합니다. 또한 터치 가능 개체의 경계가 터치 가능한 개체의 경계와 일치하는지 확인합니다.
유용한 Poke 포인터 속성:
- TouchableDistance: 터치 가능한 표면과 상호 작용할 수 있는 최대 거리
- 시각적 개체: 손가락 끝 시각적 개체를 렌더링하는 데 사용되는 게임 개체입니다(기본적으로 손가락의 링).
- 선: 손끝에서 활성 입력 화면으로 그리는 선택적 선입니다.
- Poke Layer Masks - 포인터가 상호 작용할 수 있는 가능한 GameObjects와 시도할 상호 작용 순서를 결정하는 우선 순위가 지정된 LayerMasks 배열입니다. 또한 GameObject에는 포크 포인터와 상호 작용하기 위한 구성 요소도 있어야
NearInteractionTouchable
합니다.
SpherePointer
SpherePointer는 UnityEngine.Physics.OverlapSphere를 사용하여 상호 작용에 가장 NearInteractionGrabbable
가까운 개체를 식별합니다. 이 개체는 와 같은 ManipulationHandler
"잡기 가능한" 입력에 유용합니다. 기능 쌍과 PokePointer
/NearInteractionTouchable
마찬가지로 Sphere Pointer와 상호 작용하려면 게임 개체에 스크립트인 구성 요소가 NearInteractionGrabbable
포함되어야 합니다.
유용한 Sphere 포인터 속성:
- 구 캐스트 반경: 잡기 가능한 개체를 쿼리하는 데 사용되는 구의 반지름입니다.
- 개체 여백 근처: 개체가 포인터 근처에 있는지 검색하기 위해 쿼리할 구 캐스트 반지름 위쪽의 거리입니다. 총 근거리 개체 감지 반지름은 Sphere Cast Radius + Near Object Margin입니다.
- 근거리 개체 섹터 각도: 근처 개체를 쿼리하기 위한 포인터의 앞으로 축 주위 각도입니다. 쿼리 함수를
IsNearObject
원뿔처럼 만듭니다. Hololens 2 동작과 일치하도록 기본적으로 66도로 설정됩니다.
- 근거리 개체 평활 계수: 근거리 개체 감지를 위한 평활 계수입니다. 근거리 개체 반경에서 개체가 검색되면 쿼리된 반경이 Near Object Radius * (1 + Near Object Smoothing Factor)가 되어 민감도를 줄이고 개체가 검색 범위를 벗어나기 어렵게 만듭니다.
- 그랩 레이어 마스크 - 포인터가 상호 작용할 수 있는 가능한 GameObjects와 시도할 상호 작용 순서를 결정하는 우선 순위가 지정된 LayerMasks 배열입니다. GameObject에는 SpherePointer와 상호 작용하기 위한 도 있어야
NearInteractionGrabbable
합니다.참고
공간 인식 계층은 MRTK에서 제공하는 기본 GrabPointer 프리팹에서 사용하지 않도록 설정됩니다. 이는 공간 메시와 구 겹치는 쿼리를 수행하면 성능에 미치는 영향을 줄이기 위해 수행됩니다. GrabPointer 프리팹을 수정하여 사용하도록 설정할 수 있습니다.
- FOV에 없는 충돌체 무시 - 포인터 근처에 있을 수 있지만 실제로 시각적 FOV에는 없는 충돌체를 무시할지 여부입니다. 이렇게 하면 우발적인 잡기를 방지할 수 있으며, 잡을 수 있지만 볼 수 없는 경우 손 광선이 켜질 수 있습니다. Visual FOV는 성능상의 이유로 일반적인 frustum 대신 원뿔을 통해 정의됩니다. 이 원뿔은 반경이 반자 디스플레이 높이(또는 세로 FOV)와 같은 카메라의 프러스텀과 같은 가운데 및 방향을 지정합니다.
텔레포트 포인터
TeleportPointer
는 사용자를 이동하기 위해 작업을 수행할 때(즉, 텔레포트 단추를 누르면) 텔레포트 요청을 발생합니다.ParabolicTeleportPointer
는 사용자를 이동하기 위해 포물선 레이캐스트를 사용하여 작업을 수행할 때(즉, 텔레포트 단추를 누르면) 텔레포트 요청을 발생합니다.
혼합 현실 플랫폼에 대한 포인터 지원
다음 표에서는 일반적으로 MRTK의 공통 플랫폼에 사용되는 포인터 형식에 대해 자세히 설명합니다. 참고: 이러한 플랫폼에 다른 포인터 형식을 추가할 수 있습니다. 예를 들어 VR에 Poke 포인터 또는 Sphere 포인터를 추가할 수 있습니다. 또한 게임 패드가 있는 VR 장치는 GGV 포인터를 사용할 수 있습니다.
포인터 | OpenVR | Windows Mixed Reality | HoloLens 1 | HoloLens 2 |
---|---|---|---|---|
ShellHandRayPointer | Valid | Valid | Valid | |
TeleportPointer | Valid | Valid | ||
GGVPointer | Valid | |||
SpherePointer | Valid | |||
PokePointer | Valid |
코드를 통한 포인터 상호 작용
포인터 이벤트 인터페이스
다음 인터페이스 중 하나 이상을 구현하고 를 사용하여 GameObject Collider
에 할당된 MonoBehaviours는 연결된 인터페이스에 정의된 포인터 상호 작용 이벤트를 받습니다.
이벤트 | Description | Handler |
---|---|---|
포커스 변경/포커스 변경 전 | 포커스를 잃는 게임 개체와 포인터가 포커스를 변경할 때마다 얻는 개체 모두에서 발생합니다. | IMixedRealityFocusChangedHandler |
포커스 입력/종료 | 첫 번째 포인터가 들어갈 때, 그리고 마지막 포인터가 포커스를 잃을 때 포커스를 얻는 게임 개체에서 발생합니다. | IMixedRealityFocusHandler |
포인터 아래쪽/끌기/위쪽/클릭 | 포인터를 보고하기 위해 발생합니다. | IMixedRealityPointerHandler |
터치 시작/업데이트/완료됨 | 터치 활동을 보고하는 등의 PokePointer 터치 인식 포인터에 의해 발생합니다. |
IMixedRealityTouchHandler |
참고
IMixedRealityFocusChangedHandler
및 IMixedRealityFocusHandler
는 발생되는 개체에서 처리해야 합니다. 전역적으로 포커스 이벤트를 수신할 수 있지만 다른 입력 이벤트와 달리 전역 이벤트 처리기는 포커스에 따라 이벤트 수신을 차단하지 않습니다(이벤트는 전역 처리기와 포커스에 있는 해당 개체 모두에 의해 수신됨).
동작 중인 포인터 입력 이벤트
포인터 입력 이벤트는 일반 입력 이벤트와 비슷한 방식으로 MRTK 입력 시스템에서 인식되고 처리됩니다. 차이점은 포인터 입력 이벤트는 입력 이벤트를 발생시킨 포인터와 전역 입력 처리기에 의해서만 GameObject에서 처리된다는 것입니다. 일반 입력 이벤트는 모든 활성 포인터에 대해 포커스가 있는 GameObjects에 의해 처리됩니다.
- MRTK 입력 시스템은 입력 이벤트가 발생했음을 인식합니다.
- MRTK 입력 시스템은 등록된 모든 전역 입력 처리기에 입력 이벤트에 대한 관련 인터페이스 함수를 실행합니다.
- 입력 시스템은 이벤트를 발생시킨 포인터에 포커스가 있는 GameObject를 결정합니다.
- 입력 시스템은 Unity의 이벤트 시스템을 활용하여 포커스가 있는 GameObject에서 일치하는 모든 구성 요소에 대한 관련 인터페이스 함수를 실행합니다.
- 입력 이벤트가 사용된 것으로 표시된 경우 프로세스가 종료되고 더 이상 GameObjects가 콜백을 수신하지 않습니다.
- 예: 인터페이스
IMixedRealityFocusHandler
를 구현하는 구성 요소는 GameObject를 검색하여 포커스를 얻거나 잃습니다. - 참고: Unity 이벤트 시스템은 현재 GameObject에서 원하는 인터페이스와 일치하는 구성 요소가 없으면 부모 GameObject를 검색하기 위해 버블업됩니다.
- 예: 인터페이스
- 전역 입력 처리기가 등록되지 않고 일치하는 구성 요소/인터페이스를 사용하여 GameObject를 찾을 수 없는 경우 입력 시스템은 등록된 각 대체 입력 처리기를 호출합니다.
예제
다음은 포인터가 포커스를 취하거나 포커스를 벗어나거나 포인터가 개체를 선택할 때 연결된 렌더러의 색을 변경하는 예제 스크립트입니다.
public class ColorTap : MonoBehaviour, IMixedRealityFocusHandler, IMixedRealityPointerHandler
{
private Color color_IdleState = Color.cyan;
private Color color_OnHover = Color.white;
private Color color_OnSelect = Color.blue;
private Material material;
private void Awake()
{
material = GetComponent<Renderer>().material;
}
void IMixedRealityFocusHandler.OnFocusEnter(FocusEventData eventData)
{
material.color = color_OnHover;
}
void IMixedRealityFocusHandler.OnFocusExit(FocusEventData eventData)
{
material.color = color_IdleState;
}
void IMixedRealityPointerHandler.OnPointerDown(
MixedRealityPointerEventData eventData) { }
void IMixedRealityPointerHandler.OnPointerDragged(
MixedRealityPointerEventData eventData) { }
void IMixedRealityPointerHandler.OnPointerClicked(MixedRealityPointerEventData eventData)
{
material.color = color_OnSelect;
}
}
쿼리 포인터
사용 가능한 입력 원본(즉, 사용 가능한 컨트롤러 및 입력)을 반복하여 현재 활성 상태인 모든 포인터를 수집하여 연결된 포인터를 검색할 수 있습니다.
var pointers = new HashSet<IMixedRealityPointer>();
// Find all valid pointers
foreach (var inputSource in CoreServices.InputSystem.DetectedInputSources)
{
foreach (var pointer in inputSource.Pointers)
{
if (pointer.IsInteractionEnabled && !pointers.Contains(pointer))
{
pointers.Add(pointer);
}
}
}
기본 포인터
개발자는 포커스의 기본 포인터가 변경될 때 알림을 받도록 FocusProviders PrimaryPointerChanged 이벤트를 구독할 수 있습니다. 이는 사용자가 현재 응시 또는 손 광선 또는 다른 입력 소스를 통해 장면과 상호 작용하는지 식별하는 데 매우 유용할 수 있습니다.
private void OnEnable()
{
var focusProvider = CoreServices.InputSystem?.FocusProvider;
focusProvider?.SubscribeToPrimaryPointerChanged(OnPrimaryPointerChanged, true);
}
private void OnPrimaryPointerChanged(IMixedRealityPointer oldPointer, IMixedRealityPointer newPointer)
{
...
}
private void OnDisable()
{
var focusProvider = CoreServices.InputSystem?.FocusProvider;
focusProvider?.UnsubscribeFromPrimaryPointerChanged(OnPrimaryPointerChanged);
// This flushes out the current primary pointer
OnPrimaryPointerChanged(null, null);
}
PrimaryPointerExample
(Assets/MRTK/Examples/Demos/Input/Scenes/PrimaryPointer) 장면은 이벤트에 를 사용하여 PrimaryPointerChangedHandler
새 기본 포인터에 응답하는 방법을 보여 줍니다.
포인터 결과
포인터 Result
속성에는 포커스가 있는 개체를 결정하는 데 사용되는 장면 쿼리의 현재 결과가 포함됩니다. 모션 컨트롤러, 응시 입력 및 손 광선에 대해 기본적으로 만든 것과 같은 광선 캐스트 포인터의 경우 광선 캐스트 적중의 위치와 정상이 포함됩니다.
private void IMixedRealityPointerHandler.OnPointerClicked(MixedRealityPointerEventData eventData)
{
var result = eventData.Pointer.Result;
var spawnPosition = result.Details.Point;
var spawnRotation = Quaternion.LookRotation(result.Details.Normal);
Instantiate(MyPrefab, spawnPosition, spawnRotation);
}
장면(Assets/MRTK/Examples/Demos/Input/Scenes/PointerResult/PointerResultExample.unity)은 PointerResultExample
포인터 Result
를 사용하여 적중 위치에서 개체를 생성하는 방법을 보여 줍니다.
포인터 사용 안 함
포인터 사용 및 사용 안 함(예: 손 광선 사용 안 함)을 설정하려면 를 통해 PointerUtils
지정된 포인터 형식에 대한 를 설정합니다PointerBehavior
.
// Disable the hand rays
PointerUtils.SetHandRayPointerBehavior(PointerBehavior.AlwaysOff);
// Disable hand rays for the right hand only
PointerUtils.SetHandRayPointerBehavior(PointerBehavior.AlwaysOff, Handedness.Right);
// Disable the gaze pointer
PointerUtils.SetGazePointerBehavior(PointerBehavior.AlwaysOff);
// Set the behavior to match HoloLens 1
// Note, if on HoloLens 2, you must configure your pointer profile to make the GGV pointer show up for articulated hands.
public void SetHoloLens1()
{
PointerUtils.SetPokePointerBehavior(PointerBehavior.AlwaysOff, Handedness.Any);
PointerUtils.SetGrabPointerBehavior(PointerBehavior.AlwaysOff, Handedness.Any);
PointerUtils.SetRayPointerBehavior(PointerBehavior.AlwaysOff, Handedness.Any);
PointerUtils.SetGGVBehavior(PointerBehavior.Default);
}
자세한 예제는 및 TurnPointersOnOff
를 참조 PointerUtils
하세요.
편집기를 통한 포인터 상호 작용
에서 처리하는 IMixedRealityPointerHandler
포인터 이벤트의 경우 MRTK는 구성 요소 형식의 PointerHandler
추가 편의성을 제공하므로 Unity 이벤트를 통해 포인터 이벤트를 직접 처리할 수 있습니다.
포인터 익스텐트
원거리 포인터에는 장면의 다른 개체를 레이캐스트하고 상호 작용하는 정도를 제한하는 설정이 있습니다. 기본적으로 이 값은 10미터로 설정됩니다. 이 값은 HoloLens 셸의 동작과 일치하도록 선택되었습니다.
이는 프리팹의 ShellHandRayPointer
구성 요소 필드를 업데이트 DefaultControllerPointer
하여 변경할 수 있습니다.
포인터 익스텐트 - 포인터가 상호 작용하는 최대 거리를 제어합니다.
기본 포인터 익스텐트 - 포인터가 아무것도 상호 작용하지 않을 때 렌더링되는 포인터 광선/선의 길이를 제어합니다.