Unity3D
Unity
Unity #2Day 17

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

この記事は「Unity #2 Advent Calendar 2017」の17日目の記事です。
昨日16日目の記事は、mikenekoさんの「Tilemapの実装確認とフィールドマップを作る話」でした。
さて、私が今回記事にするものは、現在あるコンテンツで使っているUnityアセット「Obi Rope」とゆう高品質なロープを扱えるアセットを紹介したいと思います。
https://www.assetstore.unity3d.com/#!/content/55579?aid=1100l3oC8
スクリーンショット 2017-12-10 20.36.55.png

はじめに

Obi Rope」について

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

YouTubeから抜粋

giphy (1).gif

Dec-10-2017 20-58-17.gif

Dec-10-2017 20-58-52.gif

Dec-10-2017 20-59-33.gif

Dec-10-2017 21-00-08.gif

Dec-10-2017 21-00-36.gif

他のアセットとの違い

yanosen_jpさんの記事から抜粋

同じようなアセットはすでにアセットストアに幾つかあります(Ultimate Rope Editor)など)が、このアセットは紐と他のゲームオブジェクトとの衝突や変形時の力学計算がより正確で、他のアセットでは難しい滑車のような複雑な構造も作ることが可能です。
Obiは物理演算に(他のアセットではしばしば用いられる)RigidbodyやJointを使わず、パーティクルシステムを使って手続き的にメッシュを生成しています。そのため、紐の設定がやや独特になっています。
注意:現状ではAndroid, iOSでのビルドがうまくいきません。

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

動作環境

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

基本のロープの作り方

アセットをインポートしてきたら、
下記の画像の方法で Obi Ropeのオブジェクトをシーン上に生成します
スクリーンショット_2017-12-10_21_38_31.png
ちなみにObi Ropeのオブジェクトの中にあるコンポーネントでObi Solverとゆうコンポーネント(硬さとか決められる)がありますが、この設定は他のロープににも共有できるのでもし複数色々なロープをシーン上で使いたい場合は外して新規でObi Solverコンポーネントだけのオブジェクトを作りましょう
作り方は、下記の画像のように選んでObi Solverを選んで、
スクリーンショット_2017-12-10_21_58_02.png
Obi Ropeオブジェクトの共有は、Obi RopeコンポーネントのSolverとゆうプロパティにObi Solverオブジェクトをドラックアンドドロップしてください
スクリーンショット_2017-12-10_21_58_02.png

最初は特になにも描画されていない状態で生成されます
スクリーンショット 2017-12-10 21.46.06.png
次に描画する為にObi RopeコンポーネントのInitializeボタンを押します
スクリーンショット_2017-12-10_22_06_35.png
押したらマテリアルがない状態で生成されます
スクリーンショット 2017-12-10 22.10.34.png
マテリアルの設定はMeshRendererのMaterialsに設定すればいいです。
今回は適当にアセット内にあるマテリアルを使用
もし表示しているUV値を調整したい場合はObi RopeコンポーネントのUV Scaleで調整しましょう
Dec-10-2017 22-27-37.gif
一応これで基本のロープが完成してるのですが、今のままだと動作がわかりずらいので、ロープのジョイント部分を固定してロープの挙動を確かめます。
Obi RopeコンポーネントのEditorParticlsを選ぶと下記のようになります ここの画面はジョイントの編集画面になります
スクリーンショット_2017-12-10_22_34_39.png
ロープ中心部分にある青い点と両端の白い点もジョイント部分です。
固定したい部分をクリックして選ぶと青白の点になります。ちなみに両端の白い点をクリックした場合違いがわからないの注意してください 選択した状態でParticleEditorの赤い手のマークを選ぶとObi Handleオブジェクトがヒエラルキー上に生成されます
スクリーンショット_2017-12-10_22_40_33.png
で再生してObi Handleを動かして下記のようになればokです
Dec-10-2017 22-53-47.gif

テクニック編

ここからは記事で紹介してないものやマニュアルにあまり載ってない事や小ネタなどを書いて行きたいと思います

伸び縮みや硬さの設定

Obi Solverのプロパティで設定できます。
注意 作っている最中長くしてジョイントの数を増やすと余計に伸び縮みすることがあり再設定する必要が出ててくるのでなるべく最初にジョイントの数や長さを設定しておきましょう 長くなるほど設定が難しくなります
スクリーンショット_2017-12-11_1_34_11.png

当たり判定

基本Unityのコライダーでは判定ができずすり抜けてしまうので、判定したいオブジェクトはかならずUnityのコライダーとObiColliderコンポーネントをアタッチしておく必要あります
ちなみにレイヤー設定はObi SolverコンポーネントのcollisionLayersプロパティで変更可能
スクリーンショット_2017-12-10_23_11_23.png

Dec-10-2017 23-19-13.gif

注意

・BoxColiderだと端の角ばった所や接地面が少ないと当たり判定が抜けてしまう可能性があるので、なるべく接地面が多い球体やカプセルを選んだ方が当たり判定が抜けにくい
・動いているオブジェクトに対しては当たり判定が抜けてしまいます(できるかもしれませんが今んところわからない カスタマイズする必要あるかも)どうしても動いているオブジェクトに判定もたせたい場合はUltimate Rope Editorアセットをオススメします

摩擦係数

例えば坂とか置いた場合、デフォルトだと設定されてなくツルツル滑ってしまうので、もし止めたいとかズルズル滑り落ちる動作をしたいのであればObiCollisionMaterialを設定してあげる必要があります。
やり方は、Projectウィンドウ→Create→Obi→ObiCollisionMaterialをProjectウィンドウに生成して
スクリーンショット_2017-12-10_23_42_31.png
ObiRopeのCollisionMaterialプロパティにアタッチする ちなみにObiRopeを設定しなくてもObiColliderにもCollisionMaterialプロパティがあるので、坂側のプロパティにも設定する事も可能です。なので一括で設定した場合は坂側のプロパティに設定してもいいかもしれません
スクリーンショット_2017-12-10_23_46_18.png
元のObiCollisionMaterialのFrictionプロパティは多くなるほど摩擦係数が多くなります。傾斜によって調整すればいいと思います。
スクリーンショット_2017-12-10_23_47_07.png
動作はこんな感じ
Dec-11-2017 00-01-53.gif

ロープ同士繋げる

正直かなりわかずらいので丁寧にいきたいと思います
とりあえず2本ロープを1本にするロープ作りたいと思います
最初にObi Ropeを2つ作ります。 
Obi Solverコンポーネントは外して新しいObi Solverコンポーネント用のオブジェクトを作ってObi Solverコンポーネントを付与しておきます。
Obi RopeコンポーネントのSolverプロパティにアタッチしておきます。
2つのObi Ropeが1つのObi Solverコンポーネントを共有していることが前提です。
次に新しくゲームオブジェクトを作って、「ObiStitcher」と名前をつけて、ObiStitcherコンポーネントを付与しておきます。
ObiStitcherコンポーネントのプロパティに先ほど作ったObi Ropeをアタッチする
スクリーンショット_2017-12-11_0_45_15.png
ObiStitcherコンポーネントのEditorを押すとわかりづらいのですがシーン画面のロープ2本の上部部分が白い線ができた事が確認できましたでしょうか。
スクリーンショット_2017-12-11_0_55_52.png
次にジョイントの繋げる場所を指定します
緑のロープの上部部分に白い四角のGUIがあるので下部部分まで移動してEditorのAddStitchを押すと白赤のロープの上部と緑のロープの下部が緑の線で繋がった事が確認ができます
ちなみに緑の線は白い四角が一番近いジョイントに繋がるようになっているようです
Dec-11-2017 01-06-49.gif
スクリーンショット_2017-12-11_1_19_12.png

あとは再生すると
Dec-11-2017 01-18-14.gif

ロープをキャラクターが持つようにする

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

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
取る動作はFinalIKで実装しました 結構IK入れるととってる感があってリアルになりますね!

最適化

基本のこのアセットはロープのジョイント部分に物理演算を使ってるので多くなるほど重くなります。
なるべくジョイントを多くならないように作りましょう。
設定方法
ObiRopeのプロパティResolutionで値を決めてInitializeを押せば設定可能です。多くなるほどジョイント間隔が狭くなってジョイントが増えます。少なくなるほど、間隔が広くなり、ジョイントが減ります。
注意 !
ジョイントが減りすぎると負荷は少ないですが、ロープぽくならないのでバランスの調整をする必要があります。
長くしてもジョイントの数は変わりません 極端な長さ調整は再設定が必要です。
スクリーンショット_2017-12-11_2_56_16.png

最後に

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

Unity #2 Advent Calendar 2017
明日18日目の記事は、BURAI_VC2008さんの「夏コミで作ったLive2D売り子アプリ(ver 3.0:Cubism SDK for Unity)の話」です