ハンドトラッキングできれいに女の子を動かしてみよう！(エージェント編)

## デモ動画

### 大きい女の子と鏡に映った自分のアバターのデモ

https://youtu.be/NKznhSmi49o

### 左の一夜さんが同じ動きで、右の一夜さんが鏡の動きをするデモ

https://youtu.be/sZ9vm-F2QoQ

[前回](https://qiita.com/KayaTakashiro/items/146c2065d41cb7b7b706)の続きです。

# 目次

1. ダミーのミラーを用意しよう！
1. ダミーのダミーの回転と位置をスクリプトで動かそう！
1. 応用してみよう！！！

## 1. ダミーのミラーを用意しよう

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1353403/a89ac6e9-3ea5-a7af-a145-fb6603a6ac36.png)

まず、OVRCameraRigの直下に3Dモデルを持ってきます

つぎに、OVRCameraRigMirrorという空のオブジェクトを用意し、その下にアバターと同じ3Dモデルを持ってきます(VRIKのためです)、

OVRCamerarRigMiroorの直下にCenterEyeAnchorDummyMirrro, LeftEyeAnchorDummyMirror, RightEyeAnchorDummyMirrorをつくります。

原理としては、前回のLeftHandAnchorDummy, RightHandAnchorDummy, CenterEyeAnchorDummy についての、OVRCameraRigからの相対位置を　CenterEyeAnchorDummyMirrro, LeftEyeAnchorDummyMirror, RightEyeAnchorDummyMirror　に反映します。

## 2.ダミーの回転と位置をスクリプトで動かそう！

先にOVRCameraRigMirrorの下にControllerというスクリプトをアタッチするためのオブジェクトを用意しておきます。

次に、DummySymmetoryMirror.csを用意して、Controllerにアタッチします。

```DummySymmetoryMirror.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class DummySymmetoryMirror : MonoBehaviour
{
    [SerializeField] Transform CameraRig;

    [SerializeField] Transform CameraRigMirror;

    [SerializeField] Transform CenterEyeDummy;

    [SerializeField] Transform CenterEyeMirror;

    [SerializeField] Transform LeftHandAnchorDummy;

    [SerializeField] Transform LeftHandAnchorMirror;

    [SerializeField] Transform RightHandAnchorDummy;

    [SerializeField] Transform RightHandAnchorMirror;

    private Vector3 CenterPosition;

    private Quaternion Avatar2Agent;
    [SerializeField] bool isStarting;
    
    private float start_time;

    [SerializeField] Animator Animator;

    [SerializeField] float interval_time = 1.0f;

    [SerializeField] float gain = 1.0f;

    [SerializeField] bool isMirror;


    // Start is called before the first frame update
    void Start()
    {
        start_time = Time.time;

    }

    // Update is called once per frame
    void LateUpdate()
    {
        if(isStarting){
            if(isMirror){
                SetPosition(CenterEyeDummy, CenterEyeMirror, true);
                SetPosition(LeftHandAnchorDummy, RightHandAnchorMirror);
                SetPosition(RightHandAnchorDummy, LeftHandAnchorMirror);

                SetRotation(CenterEyeDummy, CenterEyeMirror, "head");
                SetRotation(LeftHandAnchorDummy, RightHandAnchorMirror, "left2right");
                SetRotation(RightHandAnchorDummy, LeftHandAnchorMirror, "right2left");
            }else{
                SetPosition(CenterEyeDummy, CenterEyeMirror, true);
                SetPosition(LeftHandAnchorDummy, LeftHandAnchorMirror);
                SetPosition(RightHandAnchorDummy, RightHandAnchorMirror);

                SetRotation(CenterEyeDummy, CenterEyeMirror);
                SetRotation(LeftHandAnchorDummy, LeftHandAnchorMirror);
                SetRotation(RightHandAnchorDummy, RightHandAnchorMirror);
            }

        }else{
            if(Time.time - start_time > interval_time){
                isStarting = true;
            }
        }
    }

    void SetPosition(Transform from, Transform to, bool isHead=false){
        var camera2from = from.position - CameraRig.position;
        if(isMirror){
            camera2from = new Vector3(-camera2from.x, camera2from.y, camera2from.z);
        }
        var toVec = (CameraRigMirror.rotation * Quaternion.Inverse(CameraRig.rotation)) * camera2from;
        to.position = CameraRigMirror.position + toVec * gain;
    }
    void SetRotation(Transform from, Transform to, string type = ""){
        var rotationX = from.localEulerAngles.x;
        var rotationY = from.localEulerAngles.y;
        var rotationZ = from.localEulerAngles.z;
        if(isMirror){
            switch (type)
            {
                case "head":
                    rotationY = -1 * rotationY;
                    rotationX -= 15.0f;
                    break;
                case "right2left":
                    rotationX -= 180.0f;
                    // rotationX = -1 * rotationX;
                    rotationY = -1 * rotationY;
                    break;
                case "left2right":
                    rotationX += 180.0f;
                    rotationY  = -1 * rotationY;
                    break;
            }
        }
        to.localRotation = Quaternion.Euler(new Vector3(rotationX, rotationY, rotationZ));
    }
}

```
簡単に解説するとSetPositionでCameraRigからの相対ベクトルを計算して、DummyのMirrorを配置しています。また、localRotationだけとってもいい気はするんですが、デバックに使えるので作りました。

gainが10倍の大きさの一夜さんや0.1倍の大きさの一夜さんを制御するのに必要な情報です。

isMirrorをtrueにすると鏡の状態になります。

### 3. 音ゲー作りました！

ちなみに、このやり方をみつかhttps://qiita.com/KayaTakashiro/items/146c2065d41cb7b7b706)の続きです。

# 目次

1. ダミーのミラーを用意しよう！
1. ダミーのダミーの回転と位置をスクリプトで動かそう！
1. 応用してみよう！！！

## 1. ダミーのミラーを用意しよう

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1353403/a89ac6e9-3ea5-a7af-a145-fb6603a6ac36.png)

まず、OVRCameraRigの直下に3Dモデルを持ってきます

つぎに、OVRCameraRigMirrorという空のオブジェクトを用意し、その下にアバターと同じ3Dモデルを持ってきます(VRIKのためです)、

OVRCamerarRigMiroorの直下にCenterEyeAnchorDummyMirrro, LeftEyeAnchorDummyMirror, RightEyeAnchorDummyMirrorをつくります。

原理としては、前回のLeftHandAnchorDummy, RightHandAnchorDummy, CenterEyeAnchorDummy についての、OVRCameraRigからの相対位置を　CenterEyeAnchorDummyMirrro, LeftEyeAnchorDummyMirror, RightEyeAnchorDummyMirror　に反映します。

## 2.ダミーの回転と位置をスクリプトで動かそう！

先にOVRCameraRigMirrorの下にControllerというスクリプトをアタッチするためのオブジェクトを用意しておきます。

次に、DummySymmetoryMirror.csを用意して、Controllerにアタッチします。

```DummySymmetoryMirror.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class DummySymmetoryMirror : MonoBehaviour
{
    [SerializeField] Transform CameraRig;

    [SerializeField] Transform CameraRigMirror;

    [SerializeField] Transform CenterEyeDummy;

    [SerializeField] Transform CenterEyeMirror;

    [SerializeField] Transform LeftHandAnchorDummy;

    [SerializeField] Transform LeftHandAnchorMirror;

    [SerializeField] Transform RightHandAnchorDummy;

    [SerializeField] Transform RightHandAnchorMirror;

    private Vector3 CenterPosition;

    private Quaternion Avatar2Agent;
    [SerializeField] bool isStarting;
    
    private float start_time;

    [SerializeField] Animator Animator;

    [SerializeField] float interval_time = 1.0f;

    [SerializeField] float gain = 1.0f;

    [SerializeField] bool isMirror;


    // Start is called before the first frame update
    void Start()
    {
        start_time = Time.time;

    }

    // Update is called once per frame
    void LateUpdate()
    {
        if(isStarting){
            if(isMirror){
                SetPosition(CenterEyeDummy, CenterEyeMirror, true);
                SetPosition(LeftHandAnchorDummy, RightHandAnchorMirror);
                SetPosition(RightHandAnchorDummy, LeftHandAnchorMirror);

                SetRotation(CenterEyeDummy, CenterEyeMirror, "head");
                SetRotation(LeftHandAnchorDummy, RightHandAnchorMirror, "left2right");
                SetRotation(RightHandAnchorDummy, LeftHandAnchorMirror, "right2left");
            }else{
                SetPosition(CenterEyeDummy, CenterEyeMirror, true);
                SetPosition(LeftHandAnchorDummy, LeftHandAnchorMirror);
                SetPosition(RightHandAnchorDummy, RightHandAnchorMirror);

                SetRotation(CenterEyeDummy, CenterEyeMirror);
                SetRotation(LeftHandAnchorDummy, LeftHandAnchorMirror);
                SetRotation(RightHandAnchorDummy, RightHandAnchorMirror);
            }

        }else{
            if(Time.time - start_time > interval_time){
                isStarting = true;
            }
        }
    }

    void SetPosition(Transform from, Transform to, bool isHead=false){
        var camera2from = from.position - CameraRig.position;
        if(isMirror){
            camera2from = new Vector3(-camera2from.x, camera2from.y, camera2from.z);
        }
        var toVec = (CameraRigMirror.rotation * Quaternion.Inverse(CameraRig.rotation)) * camera2from;
        to.position = CameraRigMirror.position + toVec * gain;
    }
    void SetRotation(Transform from, Transform to, string type = ""){
        var rotationX = from.localEulerAngles.x;
        var rotationY = from.localEulerAngles.y;
        var rotationZ = from.localEulerAngles.z;
        if(isMirror){
            switch (type)
            {
                case "head":
                    rotationY = -1 * rotationY;
                    rotationX -= 15.0f;
                    break;
                case "right2left":
                    rotationX -= 180.0f;
                    // rotationX = -1 * rotationX;
                    rotationY = -1 * rotationY;
                    break;
                case "left2right":
                    rotationX += 180.0f;
                    rotationY  = -1 * rotationY;
                    break;
            }
        }
        to.localRotation = Quaternion.Euler(new Vector3(rotationX, rotationY, rotationZ));
    }
}

```
簡単に解説するとSetPositionでCameraRigからの相対ベクトルを計算して、DummyのMirrorを配置しています。また、localRotationだけとってもいい気はするんですが、デバックに使えるので作りました。

gainが10倍の大きさの一夜さんや0.1倍の大きさの一夜さんを制御するのに必要な情報です。

isMirrorをtrueにすると鏡の状態になります。

### 3. 音ゲー作りました！

https://qiita.com/KayaTakashiro/items/c15f9e3ae3479989c7d1

ちなみに、このやり方をみつけるのに、失敗作を含めて50時間以上かかってます（笑）。

[Noesis](http://noe-sis.jp/)みてねー！