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

次作るもの

とりあえず次に作るものを書いておきます。「イライラ棒」「ロボットの操縦席」どちらも公共的なものを作るうえで実験といいますか、ポートフォリオ的なものになると思います。完成度というよりも、VRはこんなこと …

no image

Quest開発者はどうなる?

「Oculus Connect6」によって、Questが大幅に変わるような記事を見ました。「Oculus Link」 PC用ゲームができる。「 ハンドトラッキング 」手のモーションをトラッキングできる …

no image

Clothでの開発終了のお知らせ

ここに来て初めて知ってしまった・・・Clothがquestに対応していないことを・・・実機で試したら服が表示されていないことが発覚。次回作で服はヒラヒラ~という表現をしたかったのですが。演出の変更する …

no image

同級生VR2β版テスト中

同級生VRの第二弾を作成しています。まだ何という機能もないのですが、6DoFを活かしたゲームを作成している作品です。なかなかテストも大変で、何が一番大変かというと寝転がるところだったりして、それに対し …

no image

やっぱりUnityに戻ります

Unreal Engine 4の勉強を進めていましたが、期待していたブループリントも面倒だった・・・それと根本的にやりたいことがVRなので情報量を考えるとやっぱりUnityの方がよいかもしれない。とい …