Unityでダークソウル風のロープを掴んで下りる機能を作る

今回は主人公キャラクターがロープ(滑車付きロープでもいいかも)を掴んで下りていく機能を作成します。

ダークソウル2?にあった板が重なり合ったステージで初心者には道案内の白が必要なエリアにあった機能を作ってみました。
わたくしもよく白になって道案内をしていました。

・・・やったことない人にはまったくわからないですね、やった事ある人もわからないかもしれませんが・・・。

とりあえずそのステージにあったロープを掴んで下りる機能を作成していきます。
PS3が壊れたのでどんなんだったか忘れていますが・・・・(^_^;)

まずはロープ等のゲームオブジェクトを作成します。

スポンサーリンク

ロープや坂、ロープを掴むエリアを作成

ロープはCubeのサイズを変更して作成しました。
ロープなのでCapsuleかCylinderで作る事をお勧めします・・・・・(T_T)作った後に気がつくおバカさん

ロープ1

上のように階層を作ります。

Ropeはロープ、SearchAreaは主人公がロープを掴んで降りる動作に入る場所、
MoveRopeは主人公の手の位置と主人公本体を移動させる為の目印で、これ自体を動かします。

MoveRopeの子要素にはIKで指定する右手(RightHand)、左手(LeftHand)の位置と、主人公本体が立つ位置basePositionをCubeを使って作ります。
最初に設定する位置や角度は後で調整するので大体で設定しておきます。

ロープ2

一部文字が切れてますが・・・・、上のような位置に設定します。

ロープ3

角度を変えると上のような感じになります。
わかりやすくする為にRightHand、LeftHandはMeshRendererのチェックを入れて表示しています。

主人公がロープを掴む動作をするエリアSearchAreaにスクリプトを設定

SearchAreaのゲームオブジェクトは上の画像のbaePositionの辺りにCubeを使って作成します。
このエリアと接触したらロープを掴み降りていく動作をさせます。

なので、SearchAreaオブジェクトにSearchCharaというスクリプトを作成し設定します。

キャラクターのHandIK(後で作成するロープを掴むIKを設定するスクリプト)をあらかじめpublicで記憶させておく事も出来ますが、

今回はOnTriggerEnterでSearchAreaに進入したキャラ(厳密にはコライダ)がPlayerタグが設定されたものだったらそのゲームオブジェクトに設定されているHandIKを取得します。

同じようにキャラクター操作スクリプトのMoveTestも取得します。

呼び出す関数は後で作成しますが、大まかに言うと

HandIKのOnFlag関数はロープを掴んで降りていく動作が開始された為フラグをOnにします。
HandIKのSetPositionはロープを掴んで降りていく時にキャラクターを決められた初期位置に移動させます。
MoveTestのSetStateはロープを掴んで降りていく時は通常の移動スクリプトを動作させない為キャラの状態を変化させます。

キャラクター操作スクリプトMoveTestでロープを掴んでいる時は操作しない

MoveTestスクリプトではキャラクターを移動させるスクリプトを組んでいますが、
ロープを掴んで下りていく間はその機能を無効化する必要があります。

ロープを下りていく間にキャラクターを操作出来ると空中を移動してしまいますからね・・・・(=_=)

MoveTestスクリプト自体は以前のものを使い、一部キャラクターの状態に関する処理を追加しています。

キャラクターの状態を表す値を列挙型で作成します。

キャラクター移動処理はState.normalの時だけ実行し、ロープを掴んで下りている時は動作しないようにします。
ロープを掴んでいる間は重力も働かせないようにします。

SearchCharaスクリプトで状態を変化させる時に呼ぶSetStateでは受け取った引数の状態に設定するだけです。

ロープを掴んでいるようにIKを使って両手の位置を固定する

SearchCharaで主人公キャラクターを検知した時に、
HandIKのOnFlagとSetPositionを呼び出してロープを下りる処理へと移行します。

Start関数内でdefaultPositionにikPos.positionを入れています。
これはMoveRopeの位置を動かした後に再度ロープを下りる動作をしたい時、MoveRopeを元の位置に戻さなければいけない為defalutPositionの値を使います。

Update関数でロープを掴んでいる時にMoveRopeの位置とキャラクターの位置を動かしますが、MoveRopeを動かすとキャラクターが最初に立っているbasePositionの位置も動くので、
キャラクターの位置はbasePositionの位置を入れるとMoveRopeと一緒に動きます。
Vector3.Lerpの第3引数であるTime.deltaTimeに少数をかけると移動が遅くなり、整数をかけると速くなります。

targetPositionはUnityの実行ボタンを押してキャラクターがロープを離す位置にMoveRopeを手動で動かして決めます。
移動させた位置をtargetPositionに設定します(キャラクターが降りる位置を確認しその位置をtargetPositionに設定してください)。

Unityの実行ボタンを押した後に移動させるのは初期位置を動かすのを防ぐためです。

またRightHand、LeftHandの位置や角度もUnityの実行時に位置と角度を調整します。

RightHand、LeftHandは実行時にインスペクタのTransformの歯車をクリックしCopyComponentを選択した後実行を解除し、Transformの歯車からPasteComponentValuesを選択し実行時の値をペーストします。

機能の確認をしてみる

これでロープを掴んで下りる機能が完成しました。

HandIKは

HandIKの設定

↑のように設定します。

それでは実行して確認してみましょう。

ロープ4

上のように手はロープを掴みキャラクターも移動します。
上半身のアップだけだと分かりづらいので全体を確認します。

ロープ5

上のようにロープを掴んで下りてくる動作が確認出来ました。

ロープを下りる時は専用のカメラに切り替えて主人公の頭の後ろから覗くような感じにするのもいいかもしれません。

ロープの移動で距離を計ったり、検知エリアを使ってロープを早く離す

キャラクターが目的地付近に来るとものすごくスピードが遅くなりロープを離すまでがもどかしい!!という方は

if(Vector3.Distance(ikPos.position, targetPosition) < 0.1)

としている0.1のところの数字を大きくすると目的地より前のスピードがある時にロープを離します。

これでは目的地の所で離さず手前で離してしまって本来の目的地とは違う場所になるので嫌だという方は
目的地との距離ではなく検知エリアを作りそこにキャラクターが入ったらロープを離すようにするといいかもしれません。

MoveTowardsを使って同じ速度でロープの移動を行う

もっと簡単に等速度でロープを降りたい時はVector3.MoveTowardsを使います。

ikPos.position = Vector3.Lerp(ikPos.position, targetPosition, Time.deltaTime);

とVector3.Lerpでなめらかに移動させている箇所を

ikPos.position = Vector3.MoveTowards(ikPos,position, targetPosition, Time.deltaTime * 5);

とします。

Time.deltaTimeのままだと遅いので5をかけました。

MoveTowardsを使うと、

MoveTowardsを使ったロープの移動サンプル

↑のように同じ速度で最初の位置から目的地の位置まで移動するようになります。

ちょっとだけダークソウル風のロープを降りる機能が出来ました!

アクションゲームでロープをつたって降りる機能を作りたい時は参考にしてみてください。