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;
}
}
}
}
ようやくゲーム作りに専念できそう。