

OVRCameraRigを追加していない場合は、次の手順に従ってください。








HandTrackingScript.csを実装するHandTrackingScriptクラスに、以下を追加します。 public Camera sceneCamera;
public OVRHand leftHand;
public OVRHand rightHand;
public OVRSkeleton skeleton;
private Vector3 targetPosition;
private Quaternion targetRotation;
private float step;
private bool isIndexFingerPinching;
private LineRenderer line;
private Transform p0;
private Transform p1;
private Transform p2;
private Transform handIndexTipTransform;
| 変数 | 説明 |
|---|---|
sceneCamera | シーンで使うカメラ |
leftHandとrightHand | 左手と右手のprefab |
skeleton | 骨組み(骨の位置と回転のデータの取得用) |
targetPositionとtargetRotation | リボンに取り付けた状態の立方体のアニメーションに使う位置と回転 |
step | アニメーション付きヘルプ( Time.deltaTime) |
line | リボンを表すLineRenderer |
p0、p1、p2 | リボンLineRendererの描画のための始点、屈曲点、終点の変形 |
handIndexTipTransform | 左手人差し指の指先の変形 |
Start()で立方体の初期位置をユーザーの正面に設定するStart()関数の中で、立方体の初期位置を定義し、LineRendererコンポーネントをlineに割り当てます。 void Start()
{
transform.position = sceneCamera.transform.position + sceneCamera.transform.forward * 1.0f;
line = GetComponent<LineRenderer>();
}
pinchCube()関数を作成し、以下の行を追加します。 void pinchCube()
{
targetPosition = leftHand.transform.position - leftHand.transform.forward * 0.4f;
targetRotation = Quaternion.LookRotation(transform.position - leftHand.transform.position);
transform.position = Vector3.Lerp(transform.position, targetPosition, step);
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, step);
}
B(t) = (1 - t)^2 * P0 + 2 * (1 - t) * t * P1 + t^2 * P2です。B、P0、P1、P2はVector3であり、それぞれ位置を表します。tは線の全長に対する割合を表し、0以上1以下の値です。t = 0.5なら、B(t)は点P0から点P2までの(P1を通る)曲線の途中であり、線の真ん中までが描画されます。B(0)を計算した後、セグメントごとに反復して段階的に描画していくことができます。HandTrackingScriptクラスに以下を追加します。 void DrawCurve(Vector3 point_0, Vector3 point_1, Vector3 point_2)
{
line.positionCount = 200;
Vector3 B = new Vector3(0, 0, 0);
float t = 0f;
for (int i = 0; i < line.positionCount; i++)
{
t += 0.005f;
B = (1 - t) * (1 - t) * point_0 + 2 * (1 - t) * t * point_1 + t * t * point_2;
line.SetPosition(i, B);
}
}
DrawCurve()関数では、線の始点(point_0)、屈曲点(point_1)、終点(point_2)の位置情報Vector3が必須パラメーターです。200セグメントのすべてがそれぞれレンダリングされるまでループ処理します。Update()関数に、以下を追加します。 void Update()
{
step = 5.0f * Time.deltaTime;
if (leftHand.IsTracked)
{
isIndexFingerPinching = leftHand.GetFingerIsPinching(OVRHand.HandFinger.Index);
if (isIndexFingerPinching)
{
line.enabled = true;
// Animate cube smoothly next to left hand
pinchCube();
// ...
}
else
{
line.enabled = false;
}
}
}
leftHand OVRHandオブジェクトがisTracked()関数によってトラッキングされているかどうかを調べます。トラッキングされている場合、trueが返されます。GetFingerIsPinching(OVRHand.HandFinger.Index)関数を呼び出し、その戻り値をboolean変数isIndexFingerPinchingに代入します。この関数の定義は、bool OVRHand.GetFingerIsPinching (HandFinger finger)です。HandFinger列挙で定義されている残りの指について詳しくは、OVRHandをご覧ください。line LineRendererを有効にし、ユーザーに対して表示されるようにします。ピンチ操作をしていない場合は、無効にします。pinchCube()ヘルパー関数を呼び出して、立方体の位置と向きを設定します(ユーザーがピンチ操作をしている場合)。Update()関数を以下のように修正します。(pinchCube()呼び出しの下に新たな行が複数挿入されていることに注意。) void Update()
{
step = 5.0f * Time.deltaTime;
if (leftHand.IsTracked)
{
isIndexFingerPinching = leftHand.GetFingerIsPinching(OVRHand.HandFinger.Index);
if (isIndexFingerPinching)
{
line.enabled = true;
pinchCube();
// New lines added below this point
foreach (var b in skeleton.Bones)
{
if (b.Id == OVRSkeleton.BoneId.Hand_IndexTip)
{
handIndexTipTransform = b.Transform;
break;
}
}
p0 = transform;
p2 = handIndexTipTransform;
p1 = sceneCamera.transform;
p1.position += sceneCamera.transform.forward * 0.8f;
DrawCurve(p0.position, p1.position, p2.position);
// New lines added above this point
}
else
{
line.enabled = false;
}
}
}
OVRSkeletonオブジェクトの骨すべてについて反復処理します。skeleton.Bonesは、骨のすべてのIDを保管するOVRBoneリストです。骨IDすべての定義については、OVRSkeletonの中のenum OVRSkeleton.BoneIdをご覧ください。OVRSkeleton.BoneId.Hand_IndexTip骨IDが検出されるかどうかを調べます。これは、左手人差し指の指先を表します。handIndexTipTransformに保管します。p0に、また左手人差し指の指先の変形をp2に保管します。p1に代入します。これが、LineRendererを曲げる点となります。DrawCurve()ヘルパー関数を呼び出して、リボンLineRendererを描画します。p1の定義に関する実験をしてみるようおすすめします。それに、手や立方体に関連したさまざまな異なる値を代入してみてください。
HandTrackingScript.csのコード全体を掲載します。using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class HandTrackingScript : MonoBehaviour
{
public Camera sceneCamera;
public OVRHand leftHand;
public OVRHand rightHand;
public OVRSkeleton skeleton;
private Vector3 targetPosition;
private Quaternion targetRotation;
private float step;
private bool isIndexFingerPinching;
private LineRenderer line;
private Transform p0;
private Transform p1;
private Transform p2;
private Transform handIndexTipTransform;
// Start is called before the first frame update
void Start()
{
// Set initial cube's position in front of user
transform.position = sceneCamera.transform.position + sceneCamera.transform.forward * 1.0f;
// Assign the LineRenderer component of the cube GameObject to line
line = GetComponent<LineRenderer>();
}
// Update is called once per frame
void Update()
{
// Define step value for animation
step = 5.0f * Time.deltaTime;
// If left hand is tracked
if (leftHand.IsTracked)
{
// Gather info whether left hand is pinching
isIndexFingerPinching = leftHand.GetFingerIsPinching(OVRHand.HandFinger.Index);
// Proceed only if left hand is pinching
if (isIndexFingerPinching)
{
// Show the Line Renderer
line.enabled = true;
// Animate cube smoothly next to left hand
pinchCube();
// Loop through all the bones in the skeleton
foreach (var b in skeleton.Bones)
{
// If bone is the hand index tip
if (b.Id == OVRSkeleton.BoneId.Hand_IndexTip)
{
// Store its transform and break the loop
handIndexTipTransform = b.Transform;
break;
}
}
// p0 is the cube's transform and p2 the left hand's index tip transform
// These are the two edges of the line connecting the cube to the left hand index tip
p0 = transform;
p2 = handIndexTipTransform;
// This is a somewhat random point between the cube and the index tip
// Need to reference as the point that "bends" the curve
p1 = sceneCamera.transform;
p1.position += sceneCamera.transform.forward * 0.8f;
// Draw the line that connects the cube to the user's left index tip and bend it at p1
DrawCurve(p0.position, p1.position, p2.position);
}
// If the user is not pinching
else
{
// Don't display the line at all
line.enabled = false;
}
}
}
void DrawCurve(Vector3 point_0, Vector3 point_1, Vector3 point_2)
/***********************************************************************************
# Helper function that draws a curve between point_0 and point_2, bending at point_1.
# Gradually draws a line as Quadratic Bézier Curve that consists of 200 segments.
#
# Bézier curve draws a path as function B(t), given three points P0, P1, and P2.
# B, P0, P1, P2 are all Vector3 and represent positions.
#
# B = (1 - t)^2 * P0 + 2 * (1-t) * t * P1 + t^2 * P2
#
# t is 0 <= t <= 1 representing size / portion of line when moving to the next segment.
# For example, if t = 0.5f, B(t) is halfway from point P0 to P2.
***********************************************************************************/
{
// Set the number of segments to 200
line.positionCount = 200;
Vector3 B = new Vector3(0, 0, 0);
float t = 0f;
// Draw segments
for (int i = 0; i < line.positionCount; i++)
{
// Move to next segment
t += 0.005f;
B = (1 - t) * (1 - t) * point_0 + 2 * (1 - t) * t * point_1 + t * t * point_2;
line.SetPosition(i, B);
}
}
void pinchCube()
// Places and rotates cube smoothly next to user's left hand
{
targetPosition = leftHand.transform.position - leftHand.transform.forward * 0.4f;
targetRotation = Quaternion.LookRotation(transform.position - leftHand.transform.position);
transform.position = Vector3.Lerp(transform.position, targetPosition, step);
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, step);
}
}