アドカレ
KENTOのひとりアドカレ13日目の記事です。
https://qiita.com/advent-calendar/2025/kento
環境情報
| ツール/SDK | バージョン |
|---|---|
| Unity | 6000.0.62f1 |
| Meta XR Core SDK | 81.0.0 |
| Open XR Plugin | 1.16.0 |
| XR Interaction Toolkit | 3.0.9 |
| XR Hands | 1.7.1 |
事前準備は以下の通りです。
デモ
コントローラーでUIにホバーするデモです。Rayの衝突箇所にポインターが表示されます。
ハンドトラッキングで3Dオブジェクトにホバーするデモです。こちらもRayの衝突箇所にポインターが表示されます。

衝突箇所にポインターを出す
既にRay自体の実装はXRIにあるので、その処理に乗っかる形で実装可能です。ポインターの実装も実はXRIに用意されているのかもしれませんが、見つけられなかったので自前実装を行いました。
using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit.Interactors;
using UnityEngine.XR.Interaction.Toolkit.Interactors.Visuals;
/// <summary>
/// ポインター制御クラス。
/// </summary>
public class PointerController : MonoBehaviour
{
[SerializeField] private GameObject _pointer;
[SerializeField] private XRBaseInputInteractor _curveVisualObject;
private ICurveInteractionDataProvider _curveInteractionDataProvider;
private Transform _pointerTransform;
private void Awake()
{
_curveInteractionDataProvider = _curveVisualObject as ICurveInteractionDataProvider;
_pointerTransform = _pointer.transform;
_pointer.SetActive(false);
}
private void LateUpdate()
{
if (!_curveInteractionDataProvider.isActive)
{
_pointer.SetActive(false);
return;
}
var endPointType = _curveInteractionDataProvider.TryGetCurveEndPoint(
out var curveHitPoint,
snapToSelectedAttachIfAvailable: true,
snapToSnapVolumeIfAvailable: true);
if (endPointType == EndPointType.UI || endPointType == EndPointType.ValidCastHit)
{
UpdatePointerPosition(curveHitPoint);
_pointer.SetActive(true);
}
else
{
_pointer.SetActive(false);
}
}
private void UpdatePointerPosition(Vector3 hitPoint)
{
var curveOrigin = _curveInteractionDataProvider.curveOrigin.position;
var directionToHit = (hitPoint - curveOrigin).normalized;
_pointerTransform.position = hitPoint;
_pointerTransform.rotation = Quaternion.LookRotation(directionToHit);
}
}
あとはInspetorで適当に登録して使うだけです。今回のPointerはShpereのスケールを0.1にしたものです。

