CardBoard SDKで 凝視で押すことの出来るボタン「GazeableButton」を作る

ボタンに視線を合わせるとゲージが溜まっていくボタンを作成したい.

Hierarchy

Canvas
+ GvrPointerGraphcRaycaster
+ Canvas(Render Mode World Space)
  Button
  + Event Trigger(OnReticlePointerEnter + OnReticlePointerLeave)
    Slider
  1. [UI] > [Canvas] を追加
    GraphicRaycasterの代わりにGvrPointerGraphicRaycasterを設定. Canvas のRender Modeを World Space にし、ワールド内の適当な場所に設置. そのままだと大きすぎるのでRect Transform をx,y,z 0.001程度に.

  2. ボタン: [UI] > [Button] を追加しCanvas以下に移動

  3. ゲージ: [UI] > [Slider] をボタン以下に追加

  4. ボタンに Event Trigger をアタッチ

  5. ボタンにスクリプト GazeableButton.cs をアタッチ

GazeableButton.cs

using DG.Tweening;
using UniRx;
using UniRx.Triggers;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
 
/// <summary>
/// 視線で押せるボタン
/// </summary>
[RequireComponent(typeof(Button))]
public class GazeableButton : MonoBehaviour {
    /// <summary>
    /// スライダー
    /// </summary>
    [SerializeField]
    private Slider slider;
    
    /// <summary>
    /// 注視する秒数
    /// </summary>
    [SerializeField]
    private float duration;
 
    private bool hover = false;
    private bool pressed = false;
 
    private float TimeCount { get; set; } = 0.0f;
 
    private float Value => TimeCount / duration;
 
    void Start() {
        var button = GetComponent<Button>();
        var rect = GetComponent<RectTransform>();
        
        this.UpdateAsObservable()
            .Subscribe(_ => {
                slider.value = Value;
                
                if (pressed) return;
                if (hover) {
                    TimeCount += Time.deltaTime;
                    rect.localScale = Vector3.one * (1.1f + Value * 0.2f);
                }
                if (!hover) {
                    TimeCount = 0;
                    rect.localScale = Vector3.one;
                }
                if (duration < TimeCount) {
                    // emulate click
                    var e = new PointerEventData(EventSystem.current);
                    ExecuteEvents.Execute(button.gameObject, e, 
                        ExecuteEvents.pointerClickHandler);
                    pressed = true;
                }
            });
    }
 
    public void OnReticlePointerEnter(BaseEventData e) {
        var rect = GetComponent<RectTransform>();
        rect.DOScale(Vector3.one * 1.1f, 0.1f);
        
        hover = true;
        pressed = false;
        TimeCount = 0;
    }
 
    public void OnReticlePointerLeave(BaseEventData e) {
        var rect = GetComponent<RectTransform>();
        rect.DOScale(Vector3.one, 0.1f);
        
        hover = false;
        pressed = false;
        TimeCount = 0;
    }
}

スライダーの値は 0f ~ 1f で設定できるので注視している間、時間をカウントしていき、それをボタン押下に必要な秒数 duration で割った値をスライダーの値に設定.
duration 秒以上注視するとボタンのクリックをエミュレートする. ボタンクリックのエミュレートは以下で可能.

var e = new PointerEventData(EventSystem.current);
ExecuteEvents.Execute(button.gameObject, e, ExecuteEvents.pointerClickHandler);

適宜ボタンのアニメーション等を設定.

  1. Event Trigger 設定
  • 視線のポインタがボタンに乗ったとき、出たときにPointerEnter(BaseEventData)/PointerExit(BaseEventData) が発火するのでEventTriggerに GazeableButton.OnReticlePointerEnter(BaseEventData), GazeableButton.OnReticlePointerLeave(BaseEventData) を設定