【Unity】ロープのスペシャリストになれるアセットObi Rope

この記事は「[Unity #2 Advent Calendar 2017](https://qiita.com/advent-calendar/2017/unity2)」の17日目の記事です。
昨日１6日目の記事は、[mikeneko](https://twitter.com/leiyasuzumori)さんの「[Tilemapの実装確認とフィールドマップを作る話](https://qiita.com/mikeneko/items/e5b6d93871bf2eaa66cd)」でした。
さて、私が今回記事にするものは、現在あるコンテンツで使っているUnityアセット「[Obi Rope](https://www.assetstore.unity3d.com/#!/content/55579?aid=1100l3oC8)」とゆう高品質なロープを扱えるアセットを紹介したいと思います。
https://www.assetstore.unity3d.com/#!/content/55579?aid=1100l3oC8
<img width="793" alt="スクリーンショット 2017-12-10 20.36.55.png" src="https://qiita-image-store.s3.amazonaws.com/0/158441/281e774e-62a6-e9d2-9f14-4b4757312b3e.png">
#はじめに
「[Obi Rope](https://www.assetstore.unity3d.com/#!/content/55579?aid=1100l3oC8)」について

>Obi Ropeを使用すると、形状や動作を完全に制御して、数秒でロープを作成できます。 ロープは、環境および互いに衝突することができ、剛体に取り付けることができる。
このシステムは、他のロープソリューションと異なり、剛体とジョイントに基づいていません。 これはObiパーティクルに基づいており、より軽量で詳細で無条件に安定します。
Windows、Mac、iOS、Androidで利用できます。
特徴：
- スプラインを使用した手続き的なスムースメッシュ生成。接線空間更新と法線マップのサポート ロープのジオメトリを手動で生成する必要はありません。
- 実行時にロープの長さを変更する。 - 引き裂き可能な/切断可能なロープ。 - クローズドループ。 - 2方向剛体相互作用。
モジュラーソルバー：パフォーマンスを浪費することはありません。ロープに必要な制約のみを使用してください。
モジュールごとに調整可能なソルバー反復回数。
- 曲げの制約。
- パーティクルごとのピン制約。
- エディタ内のシミュレーションプレビュー
- 使いやすいエディタパーティクルツール：選択、ブラシ選択、ペイントブラシ、プロパティスムージング...
- シミュレーション状態がシリアライズされます - >あなたのロープの途中でシミュレーションを保存する - >それらを既にウォームスタートさせてインスタンス化します。
- すべての標準Unity Colliderをサポートします。
- 自動カメラカリング：目に見えないロープはシミュレーションを更新しません。

[YouTube](https://youtu.be/vr0og2ApAmM)から抜粋

![giphy (1).gif](https://qiita-image-store.s3.amazonaws.com/0/158441/48764277-a827-4c07-5b78-541bc2b81707.gif)

![Dec-10-2017 20-58-17.gif](https://qiita-image-store.s3.amazonaws.com/0/158441/d5860806-0e7a-2deb-71a7-e3f91a3c7a91.gif)

![Dec-10-2017 20-58-52.gif](https://qiita-image-store.s3.amazonaws.com/0/158441/37af8485-93ff-eff5-f205-34c12cd9ef4e.gif)

![Dec-10-2017 20-59-33.gif](https://qiita-image-store.s3.amazonaws.com/0/158441/5062d33e-77b1-f6cd-a377-4486eda7082f.gif)

![Dec-10-2017 21-00-08.gif](https://qiita-image-store.s3.amazonaws.com/0/158441/99e82045-0644-2c4d-fda1-4de461dc824e.gif)

![Dec-10-2017 21-00-36.gif](https://qiita-image-store.s3.amazonaws.com/0/158441/80c53e91-5ee8-dfdd-9b65-32f9d56db6af.gif)

#他のアセットとの違い
[yanosen_jpさん](https://qiita.com/yanosen_jp/items/f5ed9eaefa2ec5bd089c)の記事から抜粋
>同じようなアセットはすでにアセットストアに幾つかあります（[Ultimate Rope Editor](https://www.assetstore.unity3d.com/#!/content/7279?aid=1100l3oC8))など）が、このアセットは紐と他のゲームオブジェクトとの衝突や変形時の力学計算がより正確で、他のアセットでは難しい滑車のような複雑な構造も作ることが可能です。
Obiは物理演算に（他のアセットではしばしば用いられる）RigidbodyやJointを使わず、パーティクルシステムを使って手続き的にメッシュを生成しています。そのため、紐の設定がやや独特になっています。
注意：現状ではAndroid, iOSでのビルドがうまくいきません。

[Ultimate Rope Editor](https://www.assetstore.unity3d.com/#!/content/7279?aid=1100l3oC8)も持ってますが、比べると最初のセットアップが独特ですが、こちらのアセットの方が慣れればあまり壊れないので扱いやすい印象です。
ロープのジョイント部分がオブジェクト化されていないので、もしジョイント部分参照したい場合はカスタマイズが必要です。それについても記事に書こうと思います。
ちなみに最近は　Android, iOSにも対応してるらしいのです。

#動作環境
Unity2017.1.0f3
Obi Rope - Extended　 3.2
今回の検証のPCはmac book proを使っていますが、このアセット結構重いので、なるべく高めのスペックを使ってください

#基本のロープの作り方

アセットをインポートしてきたら、
下記の画像の方法で Obi Ropeのオブジェクトをシーン上に生成します
<img width="546" alt="スクリーンショット_2017-12-10_21_38_31.png" src="https://qiita-image-store.s3.amazonaws.com/0/158441/c877ffb3-87f4-0e9f-e917-b27365f2cb92.png">
ちなみにObi Ropeのオブジェクトの中にあるコンポーネントでObi Solverとゆうコンポーネント（硬さとか決められる）がありますが、この設定は他のロープににも共有できるのでもし複数色々なロープをシーン上で使いたい場合は外して新規でObi Solverコンポーネントだけのオブジェクトを作りましょう
作り方は、下記の画像のように選んでObi Solverを選んで、
<img width="497" alt="スクリーンショット_2017-12-10_21_58_02.png" src="https://qiita-image-store.s3.amazonaws.com/0/158441/d2aebace-9f4c-6174-4f23-0fbd427c4b9f.png">
Obi Ropeオブジェクトの共有は、Obi RopeコンポーネントのSolverとゆうプロパティにObi Solverオブジェクトをドラックアンドドロップしてください
<img width="497" alt="スクリーンショット_2017-12-10_21_58_02.png" src="https://qiita-image-store.s3.amazonaws.com/0/158441/51713614-658f-c559-ac36-6ff7a7a906b8.png">

最初は特になにも描画されていない状態で生成されます
<img width="579" alt="スクリーンショット 2017-12-10 21.46.06.png" src="https://qiita-image-store.s3.amazonaws.com/0/158441/1e8f06d9-aa1f-9c8f-14b9-b8c07c768d14.png">
次に描画する為にObi RopeコンポーネントのInitializeボタンを押します
<img width="291" alt="スクリーンショット_2017-12-10_22_06_35.png" src="https://qiita-image-store.s3.amazonaws.com/0/158441/6e5e3494-743c-1c0b-d40f-c32480bd6ce5.png">
押したらマテリアルがない状態で生成されます
<img width="452" alt="スクリーンショット 2017-12-10 22.10.34.png" src="https://qiita-image-store.s3.amazonaws.com/0/158441/40d26f06-6973-4b04-acb3-cd6748f98bdc.png">
マテリアルの設定はMeshRendererのMaterialsに設定すればいいです。
今回は適当にアセット内にあるマテリアルを使用
もし表示しているUV値を調整したい場合はObi RopeコンポーネントのUV Scaleで調整しましょう
![Dec-10-2017 22-27-37.gif](https://qiita-image-store.s3.amazonaws.com/0/158441/0a6eeda1-3d77-09ce-6fb3-d591f488b8e5.gif)
一応これで基本のロープが完成してるのですが、今のままだと動作がわかりずらいので、ロープのジョイント部分を固定してロープの挙動を確かめます。
Obi RopeコンポーネントのEditorParticlsを選ぶと下記のようになります　ここの画面はジョイントの編集画面になります
<img width="1407" alt="スクリーンショット_2017-12-10_22_34_39.png" src="https://qiita-image-store.s3.amazonaws.com/0/158441/acc823eb-d1c4-c5c0-77fc-03298b3196dd.png">
ロープ中心部分にある青い点と両端の白い点もジョイント部分です。
固定したい部分をクリックして選ぶと青白の点になります。ちなみに両端の白い点をクリックした場合違いがわからないの注意してください　選択した状態でParticleEditorの赤い手のマークを選ぶとObi Handleオブジェクトがヒエラルキー上に生成されます
<img width="631" alt="スクリーンショット_2017-12-10_22_40_33.png" src="https://qiita-image-store.s3.amazonaws.com/0/158441/d8b69f32-0d9b-8bec-6deb-2baa675d0145.png">
で再生してObi Handleを動かして下記のようになればokです
![Dec-10-2017 22-53-47.gif](https://qiita-image-store.s3.amazonaws.com/0/158441/4d48d488-767c-98f5-debc-f60e45fb0978.gif)

#テクニック編
ここからは記事で紹介してないものやマニュアルにあまり載ってない事や小ネタなどを書いて行きたいと思います
##伸び縮みや硬さの設定
Obi Solverのプロパティで設定できます。
注意　作っている最中長くしてジョイントの数を増やすと余計に伸び縮みすることがあり再設定する必要が出ててくるのでなるべく最初にジョイントの数や長さを設定しておきましょう　長くなるほど設定が難しくなります
<img width="465" alt="スクリーンショット_2017-12-11_1_34_11.png" src="https://qiita-image-store.s3.amazonaws.com/0/158441/466c7c88-735f-b43e-66eb-246bda983bd4.png">

##当たり判定
基本Unityのコライダーでは判定ができずすり抜けてしまうので、判定したいオブジェクトはかならずUnityのコライダーとObiColliderコンポーネントをアタッチしておく必要あります
ちなみにレイヤー設定はObi SolverコンポーネントのcollisionLayersプロパティで変更可能
<img width="393" alt="スクリーンショット_2017-12-10_23_11_23.png" src="https://qiita-image-store.s3.amazonaws.com/0/158441/58efdfa7-9d5d-8503-1a15-80272d1298d3.png">

![Dec-10-2017 23-19-13.gif](https://qiita-image-store.s3.amazonaws.com/0/158441/2f8975c8-ce9f-a6a6-0ee6-527e2e99c8d1.gif)
####注意
・BoxColiderだと端の角ばった所や接地面が少ないと当たり判定が抜けてしまう可能性があるので、なるべく接地面が多い球体やカプセルを選んだ方が当たり判定が抜けにくい
・動いているオブジェクトに対しては当たり判定が抜けてしまいます（できるかもしれませんが今んところわからない　カスタマイズする必要あるかも）どうしても動いているオブジェクトに判定もたせたい場合は[Ultimate Rope Editor](https://www.assetstore.unity3d.com/#!/content/7279?aid=1100l3oC8)アセットをオススメします

##摩擦係数
例えば坂とか置いた場合、デフォルトだと設定されてなくツルツル滑ってしまうので、もし止めたいとかズルズル滑り落ちる動作をしたいのであればObiCollisionMaterialを設定してあげる必要があります。
やり方は、Projectウィンドウ→Create→Obi→ObiCollisionMaterialをProjectウィンドウに生成して
<img width="434" alt="スクリーンショット_2017-12-10_23_42_31.png" src="https://qiita-image-store.s3.amazonaws.com/0/158441/a21095d4-9bea-0afc-0e0d-c611b83974a8.png">
ObiRopeのCollisionMaterialプロパティにアタッチする ちなみにObiRopeを設定しなくてもObiColliderにもCollisionMaterialプロパティがあるので、坂側のプロパティにも設定する事も可能です。なので一括で設定した場合は坂側のプロパティに設定してもいいかもしれません
<img width="410" alt="スクリーンショット_2017-12-10_23_46_18.png" src="https://qiita-image-store.s3.amazonaws.com/0/158441/50650869-09a3-7311-77d3-437160120193.png">
元のObiCollisionMaterialのFrictionプロパティは多くなるほど摩擦係数が多くなります。傾斜によって調整すればいいと思います。
<img width="415" alt="スクリーンショット_2017-12-10_23_47_07.png" src="https://qiita-image-store.s3.amazonaws.com/0/158441/ab1e68d9-cb4c-4100-0536-0f15455cfab9.png">
動作はこんな感じ
![Dec-11-2017 00-01-53.gif](https://qiita-image-store.s3.amazonaws.com/0/158441/5cde64a8-1b56-cbd8-57ff-0cae3cdec48f.gif)

##ロープ同士繋げる
正直かなりわかずらいので丁寧にいきたいと思います
とりあえず２本ロープを１本にするロープ作りたいと思います
最初にObi Ropeを２つ作ります。　
Obi Solverコンポーネントは外して新しいObi Solverコンポーネント用のオブジェクトを作ってObi Solverコンポーネントを付与しておきます。
Obi RopeコンポーネントのSolverプロパティにアタッチしておきます。
２つのObi Ropeが１つのObi Solverコンポーネントを共有していることが前提です。
次に新しくゲームオブジェクトを作って、「ObiStitcher」と名前をつけて、ObiStitcherコンポーネントを付与しておきます。
ObiStitcherコンポーネントのプロパティに先ほど作ったObi Ropeをアタッチする
<img width="509" alt="スクリーンショット_2017-12-11_0_45_15.png" src="https://qiita-image-store.s3.amazonaws.com/0/158441/2a3dac49-cec0-77fa-a7ae-67d7098acb4a.png">
ObiStitcherコンポーネントのEditorを押すとわかりづらいのですがシーン画面のロープ２本の上部部分が白い線ができた事が確認できましたでしょうか。
<img width="385" alt="スクリーンショット_2017-12-11_0_55_52.png" src="https://qiita-image-store.s3.amazonaws.com/0/158441/ba5ca45b-df57-66c8-eecd-27ef6f9a13f3.png">
次にジョイントの繋げる場所を指定します
緑のロープの上部部分に白い四角のGUIがあるので下部部分まで移動してEditorのAddStitchを押すと白赤のロープの上部と緑のロープの下部が緑の線で繋がった事が確認ができます
ちなみに緑の線は白い四角が一番近いジョイントに繋がるようになっているようです
![Dec-11-2017 01-06-49.gif](https://qiita-image-store.s3.amazonaws.com/0/158441/e0cfa5ec-3d59-4419-97e3-68b386a4bded.gif)
<img width="404" alt="スクリーンショット_2017-12-11_1_19_12.png" src="https://qiita-image-store.s3.amazonaws.com/0/158441/7f0c5392-f08e-9614-2011-9e518f231791.png">

あとは再生すると
![Dec-11-2017 01-18-14.gif](https://qiita-image-store.s3.amazonaws.com/0/158441/8d67ac08-a9c4-35c4-dcf7-e2097e0a4d4f.gif)

##ロープをキャラクターが持つようにする
こちらは人型のキャラクターがどこからでもロープを持てるようにする想定で作ってきます。
最初にロープのオブジェクト構造を作成します。
いつも通りObi Ropeを作成します。
そしてEditorParticlsを押して、全ジョイントにObi Handleオブジェクトを作ります。
作ったObi HandleオブジェクトにSphereClliderを付与しときます。これで各ジョイントの当たり判定をとります
全Obi HandleオブジェクトをObi Ropeの子に入れます。Obi Handleはリネームしておきましょう。親の座標に近いほど数がすくなる感じで、
![Dec-11-2017 02-12-45.gif](https://qiita-image-store.s3.amazonaws.com/0/158441/35c9df39-e343-3378-eea6-2ddca3683df3.gif)
次に各ジョイントの座標に対してObi Handleを追従される必要があるのでスクリプトを書きます。
ObiRopeContllolerスクリプトを新規で作りObi Ropeオブジェクトにアタッチします。

```csharp:obiRopeController.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Obi;

public class obiRopeController : MonoBehaviour {

	//コライダーのサイズ
	public float colSize = 0.1f;

	private ObiTetherConstraints _ObiTetherConstraints;

	private List<SphereCollider> objs_col = new List<SphereCollider>();
	public List<ObiParticleHandle> ophs = new List<ObiParticleHandle>();
		
		
	// Awake is called when the script instance is being loaded.
	protected void Awake()
	{
		_ObiTetherConstraints = GetComponent<ObiTetherConstraints>();
		ObiParticleHandle[] os = GetComponentsInChildren<ObiParticleHandle>();
		for(int i = 0; i < os.Length; i++)
		{
			ObiParticleHandle o = os[i];
				
			o.enabled = false;
			SphereCollider sc = o.gameObject.GetComponent<SphereCollider>();
			sc.isTrigger = true;
			objs_col.Add(sc);

			ophs.Add( o);
		}
	}
	// Use this for initialization
	void Start () {
			
			
	}
	// Update is called once per frame
	void Update () {
		//各ジョイントの座標をObi Handleにセット
		for(int i = 0; i < _ObiTetherConstraints.Actor.particleIndices.Length; i++)
		{
			if(i < ophs.Count)
			{
				objs_col[i].radius = colSize;
				if(ophs[i].enabled == false )
				{
					ophs[i].transform.position = _ObiTetherConstraints.Actor.GetParticlePosition(i);
				}
					
			}
	
		}
			
	}
	
	//プレイヤー側から呼ぶ　ロープを持つ時に呼ぶ　引数はObi Handleの名前
	public void PickUpRope(string obiHandleObjName)
	{
		string[] s = obiHandleObjName.Split("_"[0]);	
		int index = int.Parse(s[1]);
		ophs[index].enabled = true;
	}
	
	//プレイヤー側から呼ぶ　ロープを離す時に呼ぶ　引数はObi Handleの名前
	public void ReleaseRope(string obiHandleObjName)
	{
		string[] s = obiHandleObjName.Split("_"[0]);	
		int index = int.Parse(s[1]);
		ophs[index].enabled = false;
	}
}
````
あとはプレイヤー側でObiHandleの当たり判定などをして上記のメソッドを呼んであげればいいと思います。
今回はプレイヤー側の処理は割愛させていただきます。
実際どんな感じになるかとゆうと、
![58a658d2-df29-4a17-90e8-d48355a5172f.gif](https://qiita-image-store.s3.amazonaws.com/0/158441/62493f44-8fd9-a4b3-f4f7-38918a8a7bae.gif)
取る動作は[FinalIK](https://www.assetstore.unity3d.com/#!/content/14290?aid=1100l3oC8)で実装しました　結構IK入れるととってる感があってリアルになりますね！

##最適化
基本のこのアセットはロープのジョイント部分に物理演算を使ってるので多くなるほど重くなります。
なるべくジョイントを多くならないように作りましょう。
設定方法
ObiRopeのプロパティResolutionで値を決めてInitializeを押せば設定可能です。多くなるほどジョイント間隔が狭くなってジョイントが増えます。少なくなるほど、間隔が広くなり、ジョイントが減ります。
注意　！
ジョイントが減りすぎると負荷は少ないですが、ロープぽくならないのでバランスの調整をする必要があります。
長くしてもジョイントの数は変わりません　極端な長さ調整は再設定が必要です。
<img width="270" alt="スクリーンショット_2017-12-11_2_56_16.png" src="https://qiita-image-store.s3.amazonaws.com/0/158441/a16c56b1-33de-2fba-d606-eb5b045069b7.png">

##最後に
Obi Ropeは今までのロープアセットに比べると設定項目が一杯あって、独特な設定あったりと習得難度がすごく高いアセットで毎日格闘してますw
ですが、それに見合う高品質なロープを制作できます。興味がございましたら、購入してもいいかもしれませんね。
まだまだ、色々機能あるので、また機会があれば更新したいと思います。

「[Unity #2 Advent Calendar 2017](https://qiita.com/advent-calendar/2017/unity2)」
明日18日目の記事は、[BURAI_VC2008](https://twitter.com/BURAI_VC2008)さんの「[夏コミで作ったLive2D売り子アプリ（ver 3.0：Cubism SDK for Unity](https://www.slideshare.net/BURAI_VC2008/live2d-cubism-sdk-for-unityver-30-83709028)）の話」です