OculusQuest技術

OculusQuestでゲームを作る話

未分類

ハンドトラッキング超苦労した!

投稿日:

OculusIntegration 12.0が発表されてUnityでもハンドトラッキングの自作アプリを作れるようになったのですが、何しろ仕様を確認するのに一苦労。
幸いネット上には色々な情報を流していただけるのでそれらも参考にして色々と試してみました。
まず、ハンドトラッキングで移動はどうしたらよいのか?
これは以前のエントリーでも書いたように両手の加速量で移動するようにしました。
ただ、トラッキング範囲が標準コントローラーより狭いのでイメージは脇を閉めてグーを上下するようなイメージで移動になるのかなと。
ソースは以下です。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class OVRPlayerHandMove : MonoBehaviour
{
    public GameObject CenterCamera;

    public GameObject LhandObj;
    public GameObject RhandObj;
    private OVRSkeleton LoVRSkeleton;
    private OVRSkeleton RoVRSkeleton;
    private OVRHand LoVRHand;
    private OVRHand RoVRHand;

    private Vector3 LhandPos;
    private Vector3 RhandPos;

    private Vector3 LlatestPos;
    private Vector3 RlatestPos;
    private float LHandAccel;
    private float RHandAccel;
    private float TotalAccel;

    float speed = 0.0f;
    float walkSpeed = 1.0f;

    // Start is called before the first frame update
    void Start()
    {
        LoVRSkeleton = LhandObj.GetComponent<OVRSkeleton>();
        RoVRSkeleton = RhandObj.GetComponent<OVRSkeleton>();

        LoVRHand = LhandObj.GetComponent<OVRHand>();
        RoVRHand = RhandObj.GetComponent<OVRHand>();
    }

    // Update is called once per frame
    void Update()
    {
        if (LoVRHand.IsTracked && RoVRHand.IsTracked) // && LoVRHand.HandConfidence == OVRHand.TrackingConfidence.High && RoVRHand.HandConfidence == OVRHand.TrackingConfidence.High)
        {
            //現在のボーンの位置
            LhandPos = LoVRSkeleton.Bones[(int)OVRSkeleton.BoneId.Hand_ThumbTip].Transform.position;
            RhandPos = RoVRSkeleton.Bones[(int)OVRSkeleton.BoneId.Hand_ThumbTip].Transform.position;

            //加速度を計算
            LHandAccel = ((LhandPos - LlatestPos) / Time.deltaTime).magnitude;
            RHandAccel = ((RhandPos - RlatestPos) / Time.deltaTime).magnitude;
            
            //左右の加速度合計
            TotalAccel = LHandAccel + RHandAccel;

            if ((LHandAccel >= walkSpeed && RHandAccel >= walkSpeed))
            {
                var moveDirect = CenterCamera.transform.rotation.eulerAngles.y;
                var moveQuate = Quaternion.Euler(0, moveDirect, 0);

                if(TotalAccel > 1.0f && TotalAccel < 20.0f)
                {
                    speed = (TotalAccel / 20.0f) * 7.0f;
                }
                
                transform.position += moveQuate * Vector3.forward * speed * Time.deltaTime;
            }

            //次回フレームとの比較用
            LlatestPos = LhandPos;
            RlatestPos = RhandPos;
        }
    }
}

もう一つ、カメラと同時にプレイヤーコントローラーが動く
CharacterCameraConstraint
ですが、なんだか大きく仕様が変わっているのか、そのまま割り当てるとエラーになっていましたので、改造しました。
これをOVRPlayerControllerに付けると同じような動きをします。
ソースは以下です。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;

public class OVRDynamicHeight : MonoBehaviour
{
    [Tooltip("This should be a reference to the OVRCameraRig that is usually a child of the PlayerController.")]
    public OVRCameraRig CameraRig;

    private CharacterController _character;
    private OVRPlayerController _playerController;

    private readonly Action _cameraUpdateAction;
    private readonly Action _preCharacterMovementAction;

    private bool DynamicHeight = true;
    public bool EnableCollision = true;

    public float CurrentDistance;

    OVRDynamicHeight()
    {
        _cameraUpdateAction = CameraUpdate;
        _preCharacterMovementAction = PreCharacterMovement;
    }
    
    void Awake()
    {
        _character = GetComponent<CharacterController>();
        _playerController = GetComponent<OVRPlayerController>();
    }

    void OnEnable()
    {
        _playerController.CameraUpdated += _cameraUpdateAction;
        _playerController.PreCharacterMove += _preCharacterMovementAction;
    }

    void OnDisable()
    {
        _playerController.PreCharacterMove -= _preCharacterMovementAction;
        _playerController.CameraUpdated -= _cameraUpdateAction;
    }

    // Start is called before the first frame update
    void Start()
    {
        
    }

    private void CameraUpdate()
    {
        if (DynamicHeight)
        {
            var cameraHeight = _playerController.CameraHeight;

            // If the new height is less than before, just accept the reduced height.
            if (cameraHeight <= _character.height)
            {
                _character.height = cameraHeight - _character.skinWidth;
            }
            else
            {
                // Attempt to increase the controller height to the height of the camera.
                // It is important to understand that this will prevent the character from growing into colliding 
                // geometry, and that the camera might go above the character controller. For instance, ducking through
                // a low tunnel and then standing up in the middle would allow the player to see outside the world.
                // The CharacterCameraConstraint is designed to detect this problem and provide feedback to the user,
                // however it is useful to keep the character controller at a size that fits the space because this would allow
                // the player to move to a taller space. If the character controller was simply made as tall as the camera wanted,
                // the player would then be stuck and unable to move at all until the player ducked back down to the 
                // necessary elevation.
                var bottom = _character.transform.position;
                bottom += _character.center;
                bottom.y -= _character.height / 2.0f + _character.radius;
                RaycastHit info;
                var pad = _character.radius - _character.skinWidth;
                if (EnableCollision && Physics.SphereCast(bottom, _character.radius, Vector3.up, out info, cameraHeight + pad,
                    _character.gameObject.layer, QueryTriggerInteraction.Ignore))
                {
                    _character.height = info.distance - _character.radius - _character.skinWidth;
                    var t = _character.transform;
                    var p = t.position;
                    p.y -= (cameraHeight - info.distance + pad);
                    t.position = p;
                }
                else
                {
                    _character.height = cameraHeight - _character.skinWidth;
                }
            }
        }

    }


    // Update is called once per frame
    void Update()
    {

    }

    void PreCharacterMovement()
    {
        if (_playerController.Teleported)
            return;

        // First, determine if the lateral movement will collide with the scene geometry.
        var oldCameraPos = CameraRig.transform.position;
        var wpos = CameraRig.centerEyeAnchor.position;
        var delta = wpos - transform.position;
        delta.y = 0;
        var len = delta.magnitude;
        if (len > 0.0f)
        {
            _character.Move(delta);
            var currentDelta = transform.position - wpos;
            currentDelta.y = 0;
            CurrentDistance = currentDelta.magnitude;
            CameraRig.transform.position = oldCameraPos;
            if (EnableCollision)
            {
                if (CurrentDistance > 0)
                {
                    CameraRig.transform.position = oldCameraPos - delta;
                }
                //OVRInspector.instance.fader.SetFadeLevel(0);
                return;
            }
        }
        else
        {
            CurrentDistance = 0;
        }

        // Next, determine if the player camera is colliding with something above the player by doing a sphere test from the feet to the head.
        var bottom = transform.position;
        bottom += _character.center;
        bottom.y -= _character.height / 2.0f;

        RaycastHit info;
        var max = _playerController.CameraHeight;
        if (Physics.SphereCast(bottom, _character.radius, Vector3.up, out info, max,
            gameObject.layer, QueryTriggerInteraction.Ignore))
        {
            // It hit something. Use the fade distance min/max to determine how much to fade.
            var dist = info.distance;
            dist = max - dist;
            if (dist > CurrentDistance)
            {
                CurrentDistance = dist;
            }
        }
    }
}

ようやくゲーム作りに専念できそう。

-未分類

執筆者:


comment

メールアドレスが公開されることはありません。

関連記事

no image

OculusQuestについて

VRについて、私が興味を持ったのは1990年代。 ちょっと調べてみると世にも奇妙な物語で「バーチャル・リアリティ」というストーリーが放送されたのが1991年となっているので、かなり前から言葉としてはあ …

no image

次のアイデア

次はどんなゲームにするか、大体の構想ができました。VRに関する本を読んでいたのですが、簡単に言うと「VRは体験するもの」という事が載っていました。 そうかもしれない と単純に思いました。というのも、私 …

no image

目線

アニメーションにIKを付けて using System.Collections; using System.Collections.Generic; using UnityEngine; public …

no image

questのバージョンアップしたら・・・

questのバージョンが11になったので同級生VRを起動したら挙動がおかしい。再度ビルドが必要かな・・・

no image

次は・・・

「わったしが遊んであげるから待っててねー!」 とりあえず、キャラクターは作成できたかな・・・あとはUnityで動くかどうか・・・Oculus側のアップデートとかあったからなあ。あとはキャラの影の設定と …