Help us understand the problem. What is going on with this article?

Oculus Go 開発メモ - レーザーポインタとuGUI編

More than 1 year has passed since last update.

はじめに

レーザーポインタを表示して、uGUI部品を操作したいときがある。

基本的には、Oculus GoのコントローラからレーザーポインタによりUIを操作するまでの記事のとおりにやればOK。

気をつけるべきなのは、上記の記事で使用している"Assets > OVRInputSelection > InputSystem"以下にあるスクリプトと、"Assets > Oculus > VR > Scripts"以下にある同姓同名のスクリプトとは互換性がないので、混ぜて使用しないようにしないといけない。
この記事では"Assets > OVRInputSelection > InputSystem"以下にあるスクリプトしか使わない。

以下では、上記記事の内容のとおりに構築済みであることを前提にする。

レーザーの長さを調節する

レーザーの長さをUI部品までの距離に調節したかった。

考え方としては、"Assets > OVRInputSelection > OVRInputModule"を改造して、レーザーが何かのUIにヒットした時にはその座標を、ヒットしていないときにはヒットしていないことを通知する機能があれば良いだろう。

具体的には、以下のようなコードを追加する。

OVRInputModule.cs(抜粋)
// UIにヒットしたときのイベント
public delegate void RayHitDelegate(Vector3 hitPosition, Vector3 hitNormal);
// ヒットしなかったときのイベント
public delegate void RayNoHitDelegate();

public RayHitDelegate OnSelectionRayHit;
public RayNoHitDelegate OnNoRayHit;

また、これらのイベントをどこかで呼び出す必要がある。
具体的には、GetGazePointerData()を以下のように書き換える。

OVRInputModule.cs(抜粋)
        protected MouseState GetGazePointerData()
        {
            // Get the OVRRayPointerEventData reference
            OVRRayPointerEventData leftData;
            GetPointerData(kMouseLeftId, out leftData, true );
            leftData.Reset();


            leftData.worldSpaceRay = OVRInputHelpers.GetSelectionRay(activeController, trackingSpace);
            leftData.scrollDelta = GetExtraScrollDelta();

            //Populate some default values
            leftData.button = UnityEngine.EventSystems.PointerEventData.InputButton.Left;
            leftData.useDragThreshold = true;
            // Perform raycast to find intersections with world
            eventSystem.RaycastAll(leftData, m_RaycastResultCache);
            var raycast = FindFirstRaycast(m_RaycastResultCache);
            leftData.pointerCurrentRaycast = raycast;
            m_RaycastResultCache.Clear();

            OVRRaycaster ovrRaycaster = raycast.module as OVRRaycaster;
            bool noHit = true;
            // We're only interested in intersections from OVRRaycasters
            if (ovrRaycaster) 
            {
                // The Unity UI system expects event data to have a screen position
                // so even though this raycast came from a world space ray we must get a screen
                // space position for the camera attached to this raycaster for compatability
                leftData.position = ovrRaycaster.GetScreenPosition(raycast);


                // Find the world position and normal the Graphic the ray intersected
                RectTransform graphicRect = raycast.gameObject.GetComponent<RectTransform>();
                if (graphicRect != null)
                {
                    // Set are gaze indicator with this world position and normal
                   // Vector3 worldPos = raycast.worldPosition;
                    //Vector3 normal = GetRectTransformNormal(graphicRect);

                    if (OnSelectionRayHit != null) {
                        noHit = false;
                        OnSelectionRayHit(raycast.worldPosition, raycast.worldNormal);
                    }
                }
            }
            OVRPhysicsRaycaster physicsRaycaster = raycast.module as OVRPhysicsRaycaster;
            if (physicsRaycaster)
            {
                leftData.position = physicsRaycaster.GetScreenPos(raycast.worldPosition);

                if (OnSelectionRayHit != null) {
                    noHit = false;
                    OnSelectionRayHit(raycast.worldPosition, raycast.worldNormal);
                }
            }

            if(noHit){
                OnNoRayHit();
            }

            // Stick default data values in right and middle slots for compatability

            // copy the apropriate data into right and middle slots
            OVRRayPointerEventData rightData;
            GetPointerData(kMouseRightId, out rightData, true );
            CopyFromTo(leftData, rightData);
            rightData.button = UnityEngine.EventSystems.PointerEventData.InputButton.Right;

            OVRRayPointerEventData middleData;
            GetPointerData(kMouseMiddleId, out middleData, true );
            CopyFromTo(leftData, middleData);
            middleData.button = UnityEngine.EventSystems.PointerEventData.InputButton.Middle;


            m_MouseState.SetButtonState(UnityEngine.EventSystems.PointerEventData.InputButton.Left, GetGazeButtonState(), leftData);
            m_MouseState.SetButtonState(UnityEngine.EventSystems.PointerEventData.InputButton.Right, UnityEngine.EventSystems.PointerEventData.FramePressState.NotChanged, rightData);
            m_MouseState.SetButtonState(UnityEngine.EventSystems.PointerEventData.InputButton.Middle, UnityEngine.EventSystems.PointerEventData.FramePressState.NotChanged, middleData);
            return m_MouseState;
        }

また、レーザーポインタでイベントを通知してもらう必要があるので、"Assets > OVRInputSelection > InputSystem > OVRPointerVisualizer"を、以下のように書き換える。

OVRPointerVisualizer.cs
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.EventSystems;

namespace ControllerSelection {

    public class OVRPointerVisualizer : MonoBehaviour {

        [Header("(Optional) Tracking space")]
        [Tooltip("Tracking space of the OVRCameraRig.\nIf tracking space is not set, the scene will be searched.\nThis search is expensive.")]
        public Transform trackingSpace = null;
        [Header("Visual Elements")]
        [Tooltip("Line Renderer used to draw selection ray.")]
        public LineRenderer linePointer = null;
        [Tooltip("Fallback gaze pointer.")]
        public Transform gazePointer = null;
        [Tooltip("Visually, how far out should the ray be drawn.")]
        public float rayDrawDistance = 500;
        [Tooltip("How far away the gaze pointer should be from the camera.")]
        public float gazeDrawDistance = 3;

        [HideInInspector]
        public OVRInput.Controller activeController = OVRInput.Controller.None;

        [SerializeField] private OVRInputModule inputModule;
        private bool gazePointerVisibility = false;

        void Awake() {
            if (trackingSpace == null) {
                Debug.LogWarning("OVRPointerVisualizer did not have a tracking space set. Looking for one");
                trackingSpace = OVRInputHelpers.FindTrackingSpace();
            }
            if(gazePointer != null){
                // gazePointerにコライダが付いていると動作に支障をきたすのでオフっておく。
                var collider = gazePointer.GetComponent<Collider>();
                if(collider != null){
                    collider.enabled = false;
                }
            }
            if(inputModule == null){
                inputModule = EventSystem.current.currentInputModule as OVRInputModule;
            }
        }

        void HandleSelectionRayHit(Vector3 hitPosition, Vector3 hitNormal){
            activeController = OVRInputHelpers.GetControllerForButton(OVRInput.Button.PrimaryIndexTrigger, activeController);
            Ray ray = OVRInputHelpers.GetSelectionRay(activeController, trackingSpace);
            SetPointerVisibility();

            if (linePointer != null) {
                linePointer.SetPosition(0, ray.origin);
                linePointer.SetPosition(1, hitPosition);
            }

            if (gazePointer != null) {
                gazePointer.gameObject.SetActive(true);
                gazePointer.position = hitPosition;
            }
        }

        void HandleNoRayHit(){
            activeController = OVRInputHelpers.GetControllerForButton(OVRInput.Button.PrimaryIndexTrigger, activeController);
            Ray ray = OVRInputHelpers.GetSelectionRay(activeController, trackingSpace);
            SetPointerVisibility();

            if (linePointer != null) {
                linePointer.SetPosition(0, ray.origin);
                linePointer.SetPosition(1, ray.origin + ray.direction * rayDrawDistance);
            }

            if (gazePointer != null) {
                gazePointer.gameObject.SetActive(gazePointerVisibility);
                gazePointer.position = ray.origin + ray.direction * gazeDrawDistance;
            }
        }

        void OnEnable() {
            SceneManager.sceneLoaded += OnSceneLoaded;
            if(inputModule != null){
                inputModule.OnSelectionRayHit += HandleSelectionRayHit;
                inputModule.OnNoRayHit += HandleNoRayHit;
            }
        }

        void OnDisable() {
            SceneManager.sceneLoaded -= OnSceneLoaded;
            if(inputModule != null){
                inputModule.OnSelectionRayHit -= HandleSelectionRayHit;
                inputModule.OnNoRayHit -= HandleNoRayHit;
            }
        }

        void OnSceneLoaded(Scene scene, LoadSceneMode mode) {
            if (trackingSpace == null) {
                Debug.LogWarning("OVRPointerVisualizer did not have a tracking space set. Looking for one");
                trackingSpace = OVRInputHelpers.FindTrackingSpace();
            }
        }

        public void SetPointerVisibility() {
            if (trackingSpace != null && activeController != OVRInput.Controller.None) {
                if (linePointer != null) {
                    linePointer.enabled = true;
                }
                if (gazePointer != null) {
                    gazePointer.gameObject.SetActive(false);
                    gazePointerVisibility = false;
                }
            }else{
                if (linePointer != null) {
                    linePointer.enabled = false;
                }
                if (gazePointer != null) {
                    gazePointer.gameObject.SetActive(true);
                    gazePointerVisibility = true;
                }
            }
        }

    }
}

あとは、OVR Pointer VisualizerのInput Module欄にEventSystemのOVRInputModuleを突っ込んでおけば動くはず。

スクリーンショット 2018-06-06 14.20.14.png

JunSuzukiJapan
基本、未来の自分あてに備忘録として書いてます。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした