Unity + FinalIK（VRIK） + Oculus Quest2 で unitychan を一通り動かす

© Unity Technologies Japan/UCL

<iframe width="560" height="315" src="https://www.youtube.com/embed/wNB9KdhaR0g" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

## 概要
Oculus Quest2, Unity, VRIK を使用して、最低限3Dモデルを動かせるようにしました。
VRIKの調整が若干手間だったので記録として残しておきます。

- やったこと（IK などのモデル操作周りは既存のライブラリや OSS 頼りです
  - Oculus + VRIK での上半身操作
  - OculusFinger で指の操作
  - 移動、モデルの向き変更（x軸）、ジャンプ
- やってないこと
  - Animationの適用（ WALK や JUMP ）

### 1. Hierarchy構成

![hierarchy_writed.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/391719/631e5d13-bbc7-70ed-7191-ba9421b33a2a.png)

|  |  |
-- | --
 **①** | ・**OVRCameraRig** と**３Dモデル**をwrapするオブジェクト<br> ・モデルとカメラの向きの変更や高さの変更（ジャンプするとき）を一緒に行うので親要素を用意しました。
 **②** | ・VRIKに**HeadTarget**として設定するオブジェクト。
 **③** | ・左手の**Position**調整用のオブジェクト。<br>・**Rotation**と**Position**を同一オブジェクトで設定してしまうと**Positionを任意の位置に移動しづらくなる（後述）**ので**親オブジェクトでPosition**, **子オブジェクトにRotation**を設定するようにしました
 **④** | ・左手の**Rotation**調整用のオブジェクト<br>・VRIKの**LeftArm**の**Target**とに設定する。
 **⑤** | ・右手の**Position**調整用のオブジェクト<br>・③に同じ。
 **⑥** | ・右手の**Rotation**調整用のオブジェクト<br>・VRIKの**RightArm**の**Target**に設定する。
 **⑦** | ・腰の位置の調整用のオブジェクト<br>・VRIKの**Previs**の**Target**に設定する。
 **⑧** | ・VRIKに設定する両足のオブジェクトの親オブジェクト<br>・足の間の距離を変えずに位置調整する用に作成。
 **⑨** | ・右足<br>・VRIKの**RightLeg**の**Target**に設定する。
 **⑩** | ・左足<br>・VRIKの**LeftLeg**の**Target**に設定する。

↓３Dモデル（unity chan）にアタッチしているVRIK
![vrik1_writed.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/391719/0bb3239e-0548-bf31-3e8e-9fa3094fc845.png)
![vrik2_writen.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/391719/5d2a42d8-1b54-3e20-6045-f5c857e0d20b.png)
![vrik3_writen.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/391719/dc946c97-90ad-8cd2-3ec3-5c4f57bc7ef9.png)



### 2. VRIKの調整
#### 2-1 ②頭、⑦腰、⑨右足、⑩左足
これらに関しては VRIK の[公式Tutoreal](https://www.youtube.Rightcom/watch?v=6Pfx7lYQiIA)を参考に、単純に Position, Rotation を調整していく方法で問題ありませんでした


#### 2-2 ⑥右手、⑧左手の調整
これらも **2-1** の手順である程度対応可能ですが、2点問題がありました。

##### 問題1 OVRCameraRigのLeftControllerAnchorとRightControllerAnchorの位置が3Dモデルの手の位置と遠すぎてうまく調整できない
![unity_chan_1_1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/391719/6f933a68-86ce-1df5-e794-3ce68ed23398.gif)
<font color="grey">※ RightHandAnchorを選択した状態</font>

この問題を Right/LeftHandTarget のPosition修正で対応できればいいですが、
それだけだとOVRCameraRigの Right/LeftHandAnchor の位置は遠いままであり、
コントローラーを回転させたときAnchorの位置を起点にTargetが回転してします。

![unity_chan_2_1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/391719/915081fe-0fe9-ab6b-7381-f7bfcfa93d6c.gif)
<font color="grey">※ RightHandTarget を選択した状態</font>


いろいろいじった挙句、最終的には OVRCameraRig のScaleを0.5にする(OVRCameraRigを小さくする)ことで解決しました。
（一応unitychanのスケールを変更しても解決できますが、全てのオブジェクトのスケールに影響してしまうので↑の方が無難です
（他解決策があれば教えてください🙏 

![ovr_camera_rig.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/391719/049b832d-ec84-acdd-2191-bc190e42e18e.png)


##### 2-2-2 Positionで調整しようとしてもRotationを指定した後だと任意の位置に動かしづらい

Hierarchy 構成の説明でも触れましたが Rotation をかけてしまっている状態だと Position も回転がかかっている状態になってしまいます。
![unity_chan_3.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/391719/ff0bfc9a-b808-5882-690b-edc0f356f8df.png)
なので親要素を用意してそちらでPositionを調整しました。
![unity_chan_4.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/391719/6bb0a1ba-ce4f-d3f9-ccd3-751f2eb7ff45.png)

### 3.指の操作
指の操作ですが下記で公開されているコードを使用させていただきました。🙏 
各指の第一ジョイント（ボーン）にアタッチすれば使用できます。
https://eyln.hatenablog.com/entry/2016/12/19/003435

![left_hand.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/391719/3a997030-bf38-84a0-b3cb-b77d2a13b27f.png)![rightHand.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/391719/7e61f544-f6d4-c483-da3b-3cf7717a966c.png)

### 4.移動・回転・ジャンプ
こちらは下記で簡単に対応しました。

#### 移動
キャラクターを回転させるので Position ではなく現状の向き（ transform.forward, transform.right ）に対して値を加算しています。

```C#
// gameObject = unitychan と OVRCameraRig をwrapしているオブジェクト

    void Walk()
    {
        Vector2 stickR = OVRInput.Get(OVRInput.RawAxis2D.RThumbstick);
        if (!stickR.Equals(Vector2.zero))
        {
            Vector3 changeForward = gameObject.transform.forward * -(stickR.y / 20);
            Vector3 changeEight = gameObject.transform.right * -(stickR.x / 20);
            gameObject.transform.Position += changeForward + changeEight;
        }
    }
```

#### 回転
unitychan と OVRCameraRig を同時に回転させる必要があるのと
カメラを unitychan の頭に埋もれないようにしたり、 HeadTarget の位置調整をしているため、
unitychan と OVRCameraRig の親要素 = GameObject を unitychan のy軸を中心に回転させています。

<font color="grey">（上下左右に視点を回転できると一瞬で酔うので左右のみに限定しました…</font>

```C#
// character = unitychan

    void Rotate()
    {
        Vector2 stickL = OVRInput.Get(OVRInput.RawAxis2D.LThumbstick);

        if (!stickL.Equals(Vector2.zero))
        {
            Vector3 center = character.transform.Position;
            gameObject.RotateAround(center, Vector3.up, stickL.x);
        }
    }
```

#### ジャンプ
落下速度とかを考えず単純にｙを加算・減算してます。

```C#
    void Jump()
    {
        float height = gameObject.transform.Position.y - initialHeight;
        bool isOverLimit = height >= jumpHeight;

        // isJumping flag change
        if (isOverLimit)
        {
            isJumping = false;
        }
        if (height <= 0 && OVRInput.Get(OVRInput.RawButton.Y))
        {
            isJumping = true;
        }

        // jumping
        if (isJumping && !isOverLimit)
        {
            gameObject.transform.Position += new Vector3(0, 0.05f, 0);
        }
        // falling
        if (!isJumping && height > 0)
        {
            gameObject.transform.Position -= new Vector3(0, 0.05f, 0);
        }
    }
```

## おわりに
改善点としてはやはり移動時、ジャンプ時の動きが微妙なのでアニメーションを追加で解決したいです。
（今回はVRIKの設定を中心に最低限の動きをさせることを目指しました。）



---



<font color="grey">参考にさせていただいた記事・サイト<font>
- https://www.youtube.Rightcom/watch?v=6Pfx7lYQiIA
- https://eyln.hatenablog.com/entry/2016/12/19/003435
- https://qiita.com/nyu___nS/items/b4658afc849f8906afb0
- https://unity-chan.com/

<font color="grey">使用させていただいたモデル<font>
- https://assetstore.unity.com/packages/3d/characters/unity-chan-model-18705?locale=ja-JP