【OculusGo】Pointerで物体の回転・移動・加減速して遊んでみた♬

<a href="http://www.youtube.com/watch?feature=player_embedded&v=VuGjchztJJI" target="_blank"><img src="http://img.youtube.com/vi/VuGjchztJJI/0.jpg" 
 </a>
※画像をクリックするとYouTube動画につながります

前回のレーザーポインターの反射はできないが、物体の回転・移動・加減速ができたので、ちょっとアプリにして遊んでみた。

###やったこと
（１）環境等は前回のとおり
（２）C#Scriptの変更；拡大・縮小について
（３）回転について
（４）移動・加減速について
（５）遊んでみる
###（１）環境等は前回のとおり
HierarchyにParticlesというのを追加した。なんとなく賑やか。。。設定は以下のとおり
※これを打ち落としてやろうとしたが、物体として認識できないので、あきらめた。
![particles.jpg](https://qiita-image-store.s3.amazonaws.com/0/233744/124b1160-61d5-5454-f6cb-c4832d605c54.jpeg)
Hierarchyに「Create-3DObject-Capsule」を追加した。
動かす都合があるので、AddComponetでPhysics-Rigidbodyを追加した。
設定は以下のとおり
![capsule.jpg](https://qiita-image-store.s3.amazonaws.com/0/233744/632f7a6f-2d25-0c30-43b0-a9a09aaf53c8.jpeg)
Cubeはほぼ前回のとおりだが、動かすForceの関係でMassを20（大き目）に設定している。
###（２）C#Scriptの変更；拡大・縮小について
以下の参考の最後の記事
「６：Oculus Goコントローラーのボタンでレーザーポインター先のオブジェクトを操作する」
のとおりである。今回は、ここから他の動作を追加した。
【参考】
[【Unity / Oculus Go】Oculus Goコントローラー表示方法とボタン取得方法 ](http://rikoubou.hatenablog.com/entry/2018/06/04/193607)

```cs
/* ---以下追加した部分--- */
// ヒットしたオブジェクトを取得
GameObject obj = hitInfo.collider.gameObject;
// ヒットしたオブジェクトのScaleを取得
Vector3 scale = obj.transform.localScale;

if (OVRInput.GetDown(OVRInput.Button.PrimaryIndexTrigger)) {
	// トリガーボタンを押した時
	Vector3 maxScale = new Vector3(5f,5f,5f);
	// スケールの各値が5より小さい場合は0.1大きくする
	if (scale.sqrMagnitude < maxScale.sqrMagnitude) {
		obj.transform.localScale = new Vector3 (scale.x +.1f, scale.y + 0.1f, scale.z + 0.1f);
	}
} else if (OVRInput.GetDown(OVRInput.Button.PrimaryTouchpad)) {
	// タッチパッドボタンを押した時
	Vector3 minScale = new Vector3(0.5f,0.5f,0.5f);
	// スケールの各値が0.5より大きい場合は0.1小さくする
	if (scale.sqrMagnitude > minScale.sqrMagnitude) {
		obj.transform.localScale = new Vector3 (scale.x - 0.1f, scale.y - 0.1f, scale.z - 0.1f);
	}
}
/* ---追加した部分ここまで--- */
```
特に解説しないが、わかりやすいコードだ。
###（３）回転について
これは、以前やったTorqueを与えるのを今度は物体にじかにやった見た。
これ案外簡単で、以下のコードを追加しただけである。

```cs
// objを回転させる（Vector３に基づいて）
obj.transform.GetComponent<Rigidbody>().AddTorque(new Vector3(0, 1f, 5f));
```
【参考】
・[Unity－OculusGo連携。。。VR動画で遊んでみた♬マウスで操作。。出来た！](https://qiita.com/MuAuan/items/1e387dcae3bd609e7691)
・[Rigidbody.AddTorque](https://docs.unity3d.com/ScriptReference/Rigidbody.AddTorque.html)
public void AddTorque(float x, float y, float z, ForceMode mode = ForceMode.Force); 
のとおりなので、rigidbodyに対して、（ｘ、ｙ、ｚ）に沿って、ForceModeに基づいて力を与えて回転する。
※今回も回転についてはこのForceModeはデフォルトを使っています
###（４）移動・加減速について
これも基本は回転と同じですが、回転はその場で回転するだけですが、移動しちゃうので加減が難しいところ。。。
コードは以下を追加しました。

```cs
// objのポジションを変更する
obj.transform.localPosition = new Vector3(hitInfo.point.x, hitInfo.point.y, hitInfo.point.z);

// objに与える力を定義する。WorldSpaceの（0，0，0）に向かうベクトルに比例した力としている
m_NewForce = new Vector3(-hitInfo.point.x , -hitInfo.point.y , -hitInfo.point.z+5);
                        
// objに力を加える
obj.transform.GetComponent<Rigidbody>().AddForce(m_NewForce, ForceMode.Impulse);
```
移動に関しては

```cs
obj.transform.GetComponent<Rigidbody>().MovePosition(transform.position + 10*transform.forward * Time.deltaTime);
```
もできると思いますが、今回は上記でやりました。
（今回はある意味ワープみたいな。。。ことになっています）

問題は加減速です。
コメントに記載したとおりですが、引数を＋にしたりするとあっという間に飛んで行ってしまいます。ということで、中心力として記述しています。

また、ForceMode.Impulseで指定しています。
ほかのものも含めての説明は以下の通りです。

ForceMode.Acceleration is ForceMode.VelocityChange but mutiplied by Time.fixedDeltaTime；速度・Δtとして直接変化させる（Massに非依存）
ForceMode.VelocityChange just changes velocity by force parameter；直接速度を変化させる。（Mass非依存）
ForceMode.Impulse changes accelerations by force / mass；加速度（F/m）により変化させる（物理的にわかりやすい）
ForceMode.Force changes acceleration by force / mass * Time.fixedDeltaTime；単位時間にかかった加速度による速度変化（F・Δt/m）に基づいて変化させる。単位時間当たりの速度変化になっている。

【参考】
・[Difference between ForceMode.Force/Acceleration/Impulse/VelocityChange?](https://answers.unity.com/questions/696068/difference-between-forcemodeforceaccelerationimpul.html)
###（５）遊んでみる
今回はだいぶゲームっぽくなった。。。物体は動き出すと急に生きているように見えるものだ！
そして、物体二つをコントロールして視野の中に入れておくのは案外難しい。。。
※特にMassとForceのバランスが大切だとわかる

[Unity;OculusGo で物体に回転・位置移動・加速などのポインター操作で遊んでみた♬](https://youtu.be/VuGjchztJJI)
<a href="http://www.youtube.com/watch?feature=player_embedded&v=VuGjchztJJI" target="_blank"><img src="http://img.youtube.com/vi/VuGjchztJJI/0.jpg" 
 </a>
※画像をクリックするとYouTube動画につながります

###まとめ
・今回はポインター操作で物体を自由に動かすアプリを作成して遊んでみた

・次回は物体間の相互作用として自立的に組織化される太陽系みたいのを作ってみたい
###おまけ；全体のコード

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

public class LaserPointer : MonoBehaviour
{
    [SerializeField]
    private Transform _RightHandAnchor;

    [SerializeField]
    private Transform _LeftHandAnchor;

    [SerializeField]
    private Transform _CenterEyeAnchor;

    [SerializeField]
    private float _MaxDistance = 100.0f;
    private float _MinDistance = 0.0f;

    [SerializeField]
    private LineRenderer _LaserPointerRenderer;

    Vector3 m_NewForce;

    private Transform Pointer
    {
        get
        {
            // 現在アクティブなコントローラーを取得
            var controller = OVRInput.GetActiveController();
            if (controller == OVRInput.Controller.RTrackedRemote)
            {
                return _RightHandAnchor;
            }
            else if (controller == OVRInput.Controller.LTrackedRemote)
            {
                return _LeftHandAnchor;
            }
            // どちらも取れなければ目の間からビームが出る
            return _RightHandAnchor;   //_CenterEyeAnchor;
        }
    }

    void Update()
    {
        var pointer = Pointer;
        if (pointer == null || _LaserPointerRenderer == null)
        {
            return;
        }

        // コントローラーのトリガー
        if (OVRInput.Get(OVRInput.Button.PrimaryIndexTrigger))
            {
            // コントローラー位置からRayを飛ばす
            Ray pointerRay = new Ray(pointer.position, pointer.forward);

            // レーザーの起点
            _LaserPointerRenderer.SetPosition(0, pointerRay.origin);

            RaycastHit hitInfo;
            if (Physics.Raycast(pointerRay, out hitInfo, _MaxDistance))
            {
                // Rayがヒットしたらそこまで
                _LaserPointerRenderer.SetPosition(1, hitInfo.point);

                /* ---以下追加した部分--- */
                // ヒットしたオブジェクトを取得
                GameObject obj = hitInfo.collider.gameObject;
                // ヒットしたオブジェクトのScaleを取得
                Vector3 scale = obj.transform.localScale;

                if (OVRInput.GetDown(OVRInput.Button.PrimaryIndexTrigger))
                {
                    // トリガーボタンを押した時
                    Vector3 maxScale = new Vector3(10f, 10f, 10f);
                    // スケールの各値が5より小さい場合は0.1大きくする
                    if (scale.sqrMagnitude < maxScale.sqrMagnitude)
                    {
                        obj.transform.localScale = new Vector3(scale.x + 0.1f, scale.y + 0.1f, scale.z + 0.1f);
                    }
                }
                else if (OVRInput.GetDown(OVRInput.Button.PrimaryTouchpad))
                {
                    // タッチパッドボタンを押した時
                    Vector3 minScale = new Vector3(0.2f, 0.2f, 0.2f);
                    // スケールの各値が0.2より大きい場合は0.1小さくする
                    if (scale.sqrMagnitude > minScale.sqrMagnitude)
                    {
                        // スケールの各値が0.2より大きい場合は0.1小さくする
                        obj.transform.localScale = new Vector3(scale.x - 0.1f, scale.y - 0.1f, scale.z - 0.1f);

                        // objのポジションを変更する
                        //obj.transform.localPosition = new Vector3(hitInfo.point.x, hitInfo.point.y, hitInfo.point.z);
                        
                        // objを回転させる（Vector３に基づいて）
                        obj.transform.GetComponent<Rigidbody>().AddTorque(new Vector3(0, 1f, 5f));
                        
                        // objに与える力を定義する。WorldSpaceの（0，0，0）に向かうベクトルに比例した力としている
                        m_NewForce = new Vector3(-hitInfo.point.x , -hitInfo.point.y , -hitInfo.point.z+5);
                        
                        // objに力を加える
                        obj.transform.GetComponent<Rigidbody>().AddForce(m_NewForce, ForceMode.Impulse);
                    }
                }
                /* ---追加した部分ここまで--- */

                //Laserの反射はうまくいかない（描画方法が分からない）
                //Vector3 incomingV = hitInfo.point - pointerRay.origin;
                //Vector3 reflectV = Vector3.Reflect(incomingV, hitInfo.normal);
                //_LaserPointerRenderer.SetPosition(2, reflectV);

            }
            else
            {
                // Rayがヒットしなかったら向いている方向にMaxDistance伸ばす
                _LaserPointerRenderer.SetPosition(1, pointerRay.origin + pointerRay.direction * _MaxDistance);
            }
        }
        else
        {
            // コントローラー位置からRayを飛ばす
            Ray pointerRay = new Ray(pointer.position, pointer.forward);
            // レーザーの起点
            _LaserPointerRenderer.SetPosition(0, pointerRay.origin);
            // コントローラーのトリガー無しの場合、Rayが向いている方向にMinDistance（０）
            _LaserPointerRenderer.SetPosition(1, pointerRay.origin + pointerRay.direction * _MinDistance);
        }
    }
}
```