この記事は「Unity #2 Advent Calendar 2017」の17日目の記事です。
昨日16日目の記事は、mikenekoさんの「Tilemapの実装確認とフィールドマップを作る話」でした。
さて、私が今回記事にするものは、現在あるコンテンツで使っているUnityアセット「Obi Rope」とゆう高品質なロープを扱えるアセットを紹介したいと思います。
https://www.assetstore.unity3d.com/#!/content/55579?aid=1100l3oC8
#はじめに
「Obi Rope」について
Obi Ropeを使用すると、形状や動作を完全に制御して、数秒でロープを作成できます。 ロープは、環境および互いに衝突することができ、剛体に取り付けることができる。
このシステムは、他のロープソリューションと異なり、剛体とジョイントに基づいていません。 これはObiパーティクルに基づいており、より軽量で詳細で無条件に安定します。
Windows、Mac、iOS、Androidで利用できます。
特徴:
- スプラインを使用した手続き的なスムースメッシュ生成。接線空間更新と法線マップのサポート ロープのジオメトリを手動で生成する必要はありません。
- 実行時にロープの長さを変更する。 - 引き裂き可能な/切断可能なロープ。 - クローズドループ。 - 2方向剛体相互作用。
モジュラーソルバー:パフォーマンスを浪費することはありません。ロープに必要な制約のみを使用してください。
モジュールごとに調整可能なソルバー反復回数。 - 曲げの制約。
- パーティクルごとのピン制約。
- エディタ内のシミュレーションプレビュー
- 使いやすいエディタパーティクルツール:選択、ブラシ選択、ペイントブラシ、プロパティスムージング...
- シミュレーション状態がシリアライズされます - >あなたのロープの途中でシミュレーションを保存する - >それらを既にウォームスタートさせてインスタンス化します。
- すべての標準Unity Colliderをサポートします。
- 自動カメラカリング:目に見えないロープはシミュレーションを更新しません。
YouTubeから抜粋
#他のアセットとの違い
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のオブジェクトをシーン上に生成します
ちなみにObi Ropeのオブジェクトの中にあるコンポーネントでObi Solverとゆうコンポーネント(硬さとか決められる)がありますが、この設定は他のロープににも共有できるのでもし複数色々なロープをシーン上で使いたい場合は外して新規でObi Solverコンポーネントだけのオブジェクトを作りましょう
作り方は、下記の画像のように選んでObi Solverを選んで、
Obi Ropeオブジェクトの共有は、Obi RopeコンポーネントのSolverとゆうプロパティにObi Solverオブジェクトをドラックアンドドロップしてください
最初は特になにも描画されていない状態で生成されます
次に描画する為にObi RopeコンポーネントのInitializeボタンを押します
押したらマテリアルがない状態で生成されます
マテリアルの設定はMeshRendererのMaterialsに設定すればいいです。
今回は適当にアセット内にあるマテリアルを使用
もし表示しているUV値を調整したい場合はObi RopeコンポーネントのUV Scaleで調整しましょう
一応これで基本のロープが完成してるのですが、今のままだと動作がわかりずらいので、ロープのジョイント部分を固定してロープの挙動を確かめます。
Obi RopeコンポーネントのEditorParticlsを選ぶと下記のようになります ここの画面はジョイントの編集画面になります
ロープ中心部分にある青い点と両端の白い点もジョイント部分です。
固定したい部分をクリックして選ぶと青白の点になります。ちなみに両端の白い点をクリックした場合違いがわからないの注意してください 選択した状態でParticleEditorの赤い手のマークを選ぶとObi Handleオブジェクトがヒエラルキー上に生成されます
で再生してObi Handleを動かして下記のようになればokです
#テクニック編
ここからは記事で紹介してないものやマニュアルにあまり載ってない事や小ネタなどを書いて行きたいと思います
##伸び縮みや硬さの設定
Obi Solverのプロパティで設定できます。
注意 作っている最中長くしてジョイントの数を増やすと余計に伸び縮みすることがあり再設定する必要が出ててくるのでなるべく最初にジョイントの数や長さを設定しておきましょう 長くなるほど設定が難しくなります
##当たり判定
基本Unityのコライダーでは判定ができずすり抜けてしまうので、判定したいオブジェクトはかならずUnityのコライダーとObiColliderコンポーネントをアタッチしておく必要あります
ちなみにレイヤー設定はObi SolverコンポーネントのcollisionLayersプロパティで変更可能
####注意
・BoxColiderだと端の角ばった所や接地面が少ないと当たり判定が抜けてしまう可能性があるので、なるべく接地面が多い球体やカプセルを選んだ方が当たり判定が抜けにくい
・動いているオブジェクトに対しては当たり判定が抜けてしまいます(できるかもしれませんが今んところわからない カスタマイズする必要あるかも)どうしても動いているオブジェクトに判定もたせたい場合はUltimate Rope Editorアセットをオススメします
##摩擦係数
例えば坂とか置いた場合、デフォルトだと設定されてなくツルツル滑ってしまうので、もし止めたいとかズルズル滑り落ちる動作をしたいのであればObiCollisionMaterialを設定してあげる必要があります。
やり方は、Projectウィンドウ→Create→Obi→ObiCollisionMaterialをProjectウィンドウに生成して
ObiRopeのCollisionMaterialプロパティにアタッチする ちなみにObiRopeを設定しなくてもObiColliderにもCollisionMaterialプロパティがあるので、坂側のプロパティにも設定する事も可能です。なので一括で設定した場合は坂側のプロパティに設定してもいいかもしれません
元のObiCollisionMaterialのFrictionプロパティは多くなるほど摩擦係数が多くなります。傾斜によって調整すればいいと思います。
動作はこんな感じ
##ロープ同士繋げる
正直かなりわかずらいので丁寧にいきたいと思います
とりあえず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をアタッチする
ObiStitcherコンポーネントのEditorを押すとわかりづらいのですがシーン画面のロープ2本の上部部分が白い線ができた事が確認できましたでしょうか。
次にジョイントの繋げる場所を指定します
緑のロープの上部部分に白い四角のGUIがあるので下部部分まで移動してEditorのAddStitchを押すと白赤のロープの上部と緑のロープの下部が緑の線で繋がった事が確認ができます
ちなみに緑の線は白い四角が一番近いジョイントに繋がるようになっているようです
##ロープをキャラクターが持つようにする
こちらは人型のキャラクターがどこからでもロープを持てるようにする想定で作ってきます。
最初にロープのオブジェクト構造を作成します。
いつも通りObi Ropeを作成します。
そしてEditorParticlsを押して、全ジョイントにObi Handleオブジェクトを作ります。
作ったObi HandleオブジェクトにSphereClliderを付与しときます。これで各ジョイントの当たり判定をとります
全Obi HandleオブジェクトをObi Ropeの子に入れます。Obi Handleはリネームしておきましょう。親の座標に近いほど数がすくなる感じで、
次に各ジョイントの座標に対してObi Handleを追従される必要があるのでスクリプトを書きます。
ObiRopeContllolerスクリプトを新規で作りObi Ropeオブジェクトにアタッチします。
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の当たり判定などをして上記のメソッドを呼んであげればいいと思います。
今回はプレイヤー側の処理は割愛させていただきます。
実際どんな感じになるかとゆうと、
取る動作はFinalIKで実装しました 結構IK入れるととってる感があってリアルになりますね!
##最適化
基本のこのアセットはロープのジョイント部分に物理演算を使ってるので多くなるほど重くなります。
なるべくジョイントを多くならないように作りましょう。
設定方法
ObiRopeのプロパティResolutionで値を決めてInitializeを押せば設定可能です。多くなるほどジョイント間隔が狭くなってジョイントが増えます。少なくなるほど、間隔が広くなり、ジョイントが減ります。
注意 !
ジョイントが減りすぎると負荷は少ないですが、ロープぽくならないのでバランスの調整をする必要があります。
長くしてもジョイントの数は変わりません 極端な長さ調整は再設定が必要です。
##最後に
Obi Ropeは今までのロープアセットに比べると設定項目が一杯あって、独特な設定あったりと習得難度がすごく高いアセットで毎日格闘してますw
ですが、それに見合う高品質なロープを制作できます。興味がございましたら、購入してもいいかもしれませんね。
まだまだ、色々機能あるので、また機会があれば更新したいと思います。
「Unity #2 Advent Calendar 2017」
明日18日目の記事は、BURAI_VC2008さんの「夏コミで作ったLive2D売り子アプリ(ver 3.0:Cubism SDK for Unity)の話」です