UnityのJoint機能を使って敵にゲームオブジェクトが付くようにする

今回はUnityのJoint機能を使ってゲーム内の敵にゲームオブジェクトが付くようにしたいと思います。

具体的には動いている敵に主人公がヤリを投げて敵に当たったら敵にヤリがささったままになるようにしたいと思います。

この機能を作成するにはヤリが敵に当たった時にヤリを敵キャラクターのボーンの子要素にするという方法もありますが、

今回はUnityのジョイント機能を使ってヤリを敵に付くようにしたいと思います。

Joint機能でくっつくようにすれば外部から力を加えた時に自然に取れるようにすることも出来ます。

ジョイント機能を使うにはヤリにJointコンポーネント、敵にRigidbodyが取り付けられている必要があります。

Jointコンポーネントは用途に応じていくつかの種類がありますが、今回は一番シンプルなFixedJointコンポーネントを使用します。

スポンサーリンク

主人公が飛ばすヤリを作成する

まずは主人公が飛ばすヤリを作成していきましょう。

今回はUnityの3D ObjectのCubeで疑似的なヤリを作成します。

ヒエラルキー上で右クリック→3D Object→Cubeを選択し、名前をSpearとします。

Spearのインスペクタの設定

SpearのScaleを調整しヤリっぽい形にします。

Box ColliderのIs Triggerのチェックが入っていますが、これは後でスクリプトから操作できるようにする為、
現時点では無視してください。

Add Componentを押してPhysics→Rigidbodyを取りつけます。

Use Gravityにチェックを入れヤリに重力が働くようにします。

Collision DetectionをContinuous Dynamicにし、速い移動で他のゲームオブジェクトをすり抜けないようにします。

ヤリが敵と接触した時の処理を行うCollisionEnemyを作成する

Collision Enemyという新しいスクリプトを作成し取りつけます。

↑のようにCollisionとTriggerのモード状態を作成し、ヤリを他のオブジェクトと衝突させるか、侵入だけを行うかを指定出来るようにします。

モードをインスペクタで設定できるようにし、それによってコライダのIs Triggerの設定を変更するようにします。

CollisionであればIs Triggerのチェックを外し、TriggerであればIs Triggerのチェックを入れます。

衝突ありとなしはほぼ同じ処理なので衝突ありの処理だけ見ていきます。

衝突した相手がEnemyタグを持つゲームオブジェクトだった時にヤリにfixedJointが設定されていなければ
FixedJointコンポーネントをヤリに取りつけます。

そのあとconnectedBodyに衝突した敵のRigidbodyを設定します。

Joint系のコンポーネントはconnectedBodyに相手方のRigidbodyを設定すると相手方に付くようになります。

このくっついている状態を解除する為に設定するのが

breakForce(力)とbreakTorque(回転)です。

今回のスクリプトではインスペクタでこれらの値を設定することが出来ます。

インスペクタでInfinityと設定すればどんだけ力や回転が加わってもヤリが敵から外れることはありません。

また、スクリプトでInfinityを設定したい時は

Mathf.Infinity

と設定します。

またヤリが敵以外と衝突した時にfixedJointがヤリに取り付けられていない、つまりヤリを投げたけど敵じゃない他のゲームオブジェクトに接触した時は
ヤリを削除しています。

ヤリが敵に付いている時はfixedJointがnullではないはずなのでここで削除はされません。

これでヤリのゲームオブジェクトの作成が終わったので、ゲームオブジェクトをProjectsタブにドラッグ&ドロップしてプレハブ化しておきます。

ヒエラルキー上のSpearは削除するか非表示にしておいてください。

主人公がヤリを投げる場所を作成する

次に主人公キャラがヤリを出現させる場所を作成しましょう。

ヤリを出現させる場所

↑のように主人公キャラクターの子要素にCreate Emptyで空オブジェクトを作成しSpearPointという名前にします。

Localにして、SpearPointの青矢印が前方を向くように調整します。

ヤリを出現させる場所の位置

↑のように主人公より少し前の位置からヤリを出現させるようにします。

SpearPointのインスペクタ

SpearPointにはThrowSpearという新しいスクリプトを作り取りつけます。

publicでspearを宣言しインスペクタでヤリのゲームオブジェクトを設定します。

キーボードのTキーが押されたらヤリオブジェクトをSpearPointの位置と角度の場所に生成し、
ヤリオブジェクトのRigidbodyを取得しヤリが向いている方向に力を加えます。

敵キャラクターの作成

次に敵キャラクターを作成します。

敵キャラクターのAnimatorにはIdle(ただ立っている状態)とWalk(歩いている状態)を作り、アニメーションパラメータのSpeedが0.1以上になったらWalk状態に遷移するようにしてあります。

敵のインスペクタの設定

↑が敵キャラクターのインスペクタです。

Rigidbodyを取りつけIs Kinematicにチェックを入れます。

敵キャラクターはCharacterControllerの機能を使って動かしますので、CharacterControllerを取りつけてコライダのサイズを調整してください。

敵キャラクターを移動させるスクリプトZombieControllerは以下のようになります。

敵キャラクターの移動に関しては割愛しますが、移動するポイントをいくつか設定しておきその場所を巡回するように移動します。

これでZombieControllerの作成が完了したので、さきほどの画像を参考にインスペクタで巡回する位置と敵キャラクターのスピードを設定してください。

敵にヤリが付くかどうか確認する

機能が完成したので主人公キャラクターを動かしてTキーを押してヤリを前方に飛ばし敵に当ててみましょう。

今回のサンプルではヤリのCollisionEnemyスクリプトのmodeをCollisionにしておこなってみました。

ヤリが敵に刺さるか確認するサンプル

↑のように敵にヤリが刺さりましたが、主人公と接触するとヤリが消えたり、ヤリは他のゲームオブジェクトと衝突判定がされる為か敵キャラクターが変な方向に移動してしまっています。

ヤリの登場位置を調整する

ヤリの登場位置SpearPointは主人公よりだいぶ前に設置していました。

これはヤリの中心位置がSpearPointの位置になっている為、主人公の体の近くにするとヤリの中心が主人公の辺りになり主人公を突き抜けて半分の位置で登場するからです。

そこでヤリを少し変更してSpearPointの位置を主人公の体の辺りにしても大丈夫なようにしましょう。

Spearの子要素に空オブジェクトを作成

↑のようにSpearの子要素に空オブジェクトを作成します。

空オブジェクトをドラッグし親子関係を解除

空オブジェクトをドラッグし親子関係を解除し↑のようにします。

GameObjectの位置をSpearの原点に移動

空オブジェクトを移動させSpearの原点に移動させます。

Spearを空オブジェクトの子要素にする

最後にSpearを空オブジェクトの子要素にします。
空オブジェクトGameObjectをProjectsタブのAssetsフォルダにドラッグ&ドロップしプレハブ化します。

SpearPointに設定しているThrowSphearのSpearにさきほど作成したGameObjectをドラッグ&ドロップします。

Spearゲームオブジェクトに設定していたコライダやRigidbody、CollisionEnemyスクリプトコンポーネントをGameObjectに移す必要があります。
ここら辺は割愛します・・・・(^_^;)

あとはキャラクターのSpearPointの位置をキャラクターの近くに寄せるだけでOKですが、スクリプトで接触した相手がEnemy以外のタグだった時に
ヤリを削除しているので、スクリプトにPlayer以外の時という条件を加えるか、SpearPointをキャラクターと接触しない程度の位置にしておく必要があります。

スクリプトを変更するならCollisionEnemyスクリプトの接触判定で、

↑のように修正します。

終わりに

CollisionEnemyのmodeをTriggerにした時にbreakForceやbreakTorqueをInfinityにしないとくっつかないですね・・・(^_^;)

またCollisionの場合はヤリが外れた時にものすごいどっかに吹っ飛んでいますね・・・・。

ヤリはある程度時間が経ったら削除していくといいかもしれません。

ちなみにJointコンポーネントはbreakForceやbreakTorque以上の力が加えられて外れた時はコンポーネント自体が削除されますので、

自分でコンポーネントを削除する必要はありません。

スポンサーリンク

記事をシェアして頂ける方はこちら

フォローして頂くとやる気が出ます

コメント

  1. 匿名 より:

    有難う御座います。
    頑張ってみます。

  2. 匿名 より:

    すいません、教えて欲しいことがあるのですが、親子関係の無効化って出来ますか?
    解除では駄目なんです…。

    無効化で無くとも、それに近い事ができれば良いのですが…。
    とにかく、親オブジェクトと子オブジェクトを別々に動かしたいんです。
    何か有りますでしょうか?

    長文での質問、失礼しました。

    • 具体的な事がわからないのであれなんですが、
      親子関係の相対的な移動をしたくないのなら親子関係にしなくていいと思うんですが、必ず親子関係にしないといけないんでしょうか?
      通常は親の移動に合わせたいけどある時は個別に動かしたいのならば

      を使って状況に応じて親子関係にしたり解除したりが出来ます。

      SetParentで第2引数にtrueを指定すれば(指定しない場合のデフォルトがtrueですが)、親子関係にした時、解除した時の位置等がWorld Spaceを考慮した状態になっているので子が変な場所に飛んだりしません。

      また、親子の状態のまま個別に動かす方法も考えてみました。

      親のゲームオブジェクトに以下のようなスクリプトを取りつけます。

      名前をMoveとします。

      ゲームオブジェクトの現在地から移動分の値を引いてdataに保存します。

      子のゲームオブジェクトは以下のようなスクリプトを取りつけます。

      LateUpdate関数内で自身の親がインスペクタで設定した親だったらMoveスクリプトのGetData関数を呼び出して自身の位置に足します。

      こうする事で親要素の移動値分を自身の位置に換算するので、親のゲームオブジェクトの移動が子に影響しないようになりました。

      LateUpdateにしたのはUpdateの処理が終わった後に改めて移動値を計算しないと子の位置がブレブレになる為です。

      ↑のサンプルは直接位置を変えているのでRigidbodyを使った移動やCharacterControllerを使っている場合はどうなるかはわかりません・・・。