FPSを作ってみよう12-銃を撃った時に敵との当たり判定をする機能-

今回は銃を撃った時の敵との当たり判定をする機能を作成していきます。

前回

FPSを作ってみよう11-装備武器の能力を設定するスクリプトの作成-
FPSゲームで装備武器の能力を設定するスクリプトの作成していきます。装備する武器それぞれに能力を保持するスクリプトを取りつけて武器毎に管理します。

銃それぞれに銃のパラメータを保持しておくスクリプトを設定しました。

今回は銃を撃った時に銃口の先に敵がいたら敵を消す処理を作成していきます。

銃を撃った時に物理的な弾を飛ばして弾が敵に当たったかどうかを判定するのではなく、スクリプトで銃口の先に敵がいるかどうかで当たり判定を行います。

物理的な弾を飛ばしたい方は

Unityのアクションゲームで物理的な弾を飛ばして敵との当たり判定をする
Unityのガンシューティングゲームなどで銃から弾を飛ばす場合にスクリプトで敵と接触したか判断するのではなく、物理的な弾を飛ばしその弾が当たったかどうかで判断させます

を参考に改造してみてください。

またバイオハザード4や5や6等のようなTPS(ThirdPersonShooter)にあるようなレーザーポインタからレーザーを表示し標的に狙いを定める機能も作成しません。

レーザーで狙いを定めたい方は

Unityでバイオハザード風ガンシューティングの機能を作ってみる
Unityでバイオハザード風のガンシューティングの機能を作成していきます。今回は銃を構えそこからレーザーポインタを表示するようにし敵に当たっている時に攻撃ボタンを押すと敵にダメージを与えます。

を参考にして改造してみてください。

スポンサーリンク

銃を撃った時に敵に当たっているか判定する為にどうする?

当たり判定をするにはある位置からある位置まで真っ直ぐにレイを飛ばし、その間に敵がいたら当たったと判定させます。

その為に銃口に『最初の位置』を作成し、そこから『銃口の方向×銃の射程距離』のレイを飛ばします。

まずは銃口の位置を作成します。

Equip直下に武器を移動させる

↑のように空オブジェクトを作りEquipという名前にし、Equipタグを設定します。

その子要素に武器をドラッグ&ドロップします。

Equipの位置がXYZともに0であれば武器を子要素に移動しても元の位置から変わりません。

銃の子要素に右クリック→3D Object→Sphereを作成し、名前をMuzzleとします。

見た目は必要ないのでMesh Rendererのチェックを外します。

Localにして銃口の先にMuzzleの向きを向かせる

Muzzleの向きを銃口の先にしたいので、↑のようにLocalにして銃口の先に青色の矢印がくるようにします。

Muzzleを少し上にずらす

↑のようにRadiusの調整と位置、向きを修正してください。

銃口の位置より少し上側にずらしているのは銃に付いている狙いを定めるやつ(名前がわからない)で敵を狙った時にちょうどよく当てる為です。

Muzzleのインスペクタ

Muzzleのインスペクタは↑のようになりました。

これで銃口の位置と向きが出来上がりました。
レイを飛ばすにはこの位置と向きを使ってスクリプトからレイを飛ばします。

銃を撃った時の敵との当たり判定を行うスクリプトShotの作成

次に銃に弾を撃った時の処理スクリプトShotを作成し取りつけます。

Shotスクリプトの解説

Start関数内で銃に設定しているWeaponStatusスクリプトを取得し、銃の射程距離を取得しておきます。

またMuzzle(銃口)に設定しているSpehreColliderの半径を取得します。

これはレイを線ではなく丸い空間で飛ばす時に必要となるからです。

Update関数内で行っている事は視覚的に銃の射程距離と方向がどうなっているかを確認する為に処理を書いていますが、特に必要はありません。

Judge関数が実際にレイを飛ばし敵に当たっているかどうかを判定している部分です。

この関数を呼び出すのはキャラクター操作スクリプトMyCharaになります。

Rayクラスは

new Ray(レイを飛ばす位置, レイの方向)

となるのでレイを飛ばす位置をMuzzleの位置、レイの方向をMuzzleの前方方向を設定しています。

Physics.SphereCastAllはSphere(球)のレイを飛ばし当たったコライダをすべて取得します。

敵にはすべてEnemyレイヤーを設定しておき、当たり判定をするレイヤーはEnemyのみにしておきます。

銃のタイプを取得し、WeaponType.HandGunであれば一番近い敵のみに攻撃を与え、それ以外の武器では貫通した後の敵にも攻撃を与えるようにしています。

すべての敵を距離で判断してダメージを変更するというのもいいかもしれませんね。

とりあえず今回はハンドガンとそれ以外のタイプ、与えるダメージはみな同じとしておきます。

ダメージというより当たったら消していますけどね・・・。

今回はSphereCastでSphereの範囲でレイを飛ばしていますが、単純に線で判定したい場合はRaycastやLinecastを使ってもいいと思います。

ただその場合はSphereCastのように範囲で他のコライダとの判定をしない為に厳密に敵を狙う必要が出てきます。

Linecast等でレイを飛ばして判定する場合はレーザーポインターを表示するとわかりやすいかもしれません。

これでShotスクリプトが完成したので銃に設定しインスペクタでmuzzleにMuzzleゲームオブジェクトを設定してください。

主人公操作スクリプトからShotのJudge関数を呼び出す処理を追加

最後に主人公が銃を撃った時にこのShotスクリプトのJudge関数を呼び出す処理を追加します。

上のようにShotスクリプトを取得します。

gameObject.FindWithTagでEquipのタグが設定されているゲームオブジェクトを取得し、その子要素の1番目に武器があるのでその武器からShotスクリプトを取得します。

銃を撃つ処理の所にShotスクリプトのJudge関数を呼び出すようにします。

これで処理が完成しました。

敵に狙いを定めて当たり判定機能を確認してみる

あとはEnemyレイヤーを設定したCubeをいくつか設定して狙って撃ってみましょう。

銃を撃った時の当たり判定のサンプル

多少ずれているような気がしますね・・・・。

銃の位置とカメラの向きを合わせる

さきほど銃を撃った時に少しずれている感じがしました。

それはキャラクターの上半身が向いている方向はカメラの方向に合わせていますが、スクリプトでは銃口の方向にレイを飛ばし当たり判定をしています。

なのでゲームプレイヤーがゲーム画面を見て銃を敵に向けた時に、銃の狙いを定める部分がカメラの中心に来るようにしなければ見た目と処理が合わなくなってしまいます。

そこで簡単に銃の狙いを定める部分がカメラの中心にくるようにしましょう。

UIのImageを作成

ヒエラルキー上で右クリック→UI→Imageを作成すると↑のようにCanvasの子要素にImageが作成されます。

中心に小さい四角を表示する

Imageのインスペクタを表示し、PosX、PosY、Width、Heightを変更し中央に小さい四角が表示されるようにします。

中央に小さい四角が表示された

↑が実際の画面になります。

中央に小さい四角が表示され、ここがカメラの中心になります。

つまり銃の狙いを定める場所をこの小さい四角と合わせればいいわけです。

現時点では銃の狙いが中心から外れている

現時点では↑のように銃の狙いを定める部分と小さい四角がずれている事がわかります。

そこでカメラの位置を調整し銃の狙いと四角が合うようにします。

CameraPositionの階層

しかし、Main Cameraの位置を調整してもUnityを実行するとずれてしまいます。

ここで思い出してもらいたいのがMain Cameraの位置はスクリプトでCameraPositionゲームオブジェクトの位置にしていたという事です。

そこでキャラクターのAnimatorのIdleに銃を構えるアニメーションを設定し、Unityを実行します。

CameraPositionを移動して合わせる

実行中にCameraPositionを選択しSceneタブにしてCameraPositionを移動させるかインスペクタの数値を調整して銃の狙いを定める部分と小さい四角の位置を合わせます。

狙いを定める部分をカメラの中心にするか、銃口をカメラの中心にするかはお好みで設定してください。

Unity実行中にCopyComponent

位置の調整が終わったらUnity実行中のままCameraPositionのインスペクタの歯車を押しCopy Componentを選択します。

Unityの実行を止めてCameraPositionのインスペクタの歯車を押し、Paste Component Valuesを選択します。

これで実行中に調整した位置がCameraPositionに反映されます。

それでは調整が終わったのでUnityの実行をして銃の狙いが合うかどうか確認してみましょう。

狙いを合わせたサンプル

↑のように小さい四角と銃の狙いを定める部分が合うようになり、狙ったゲームオブジェクトを消すことが出来るようになりました。

作成したMuzzleのSphereColliderのサイズを少し大きく作ったので当たり判定が広くなっています。

当たり判定を小さくしたい場合はSphereColliderのRadiusを小さくしたり、RaycastやLinecastを使った線での判定がいいかもしれません。

MuzzleのScale自体を変更してしまうとRadiusにも影響が出るみたいなので、MuzzleのScaleはすべて1にしたままRadiusを調整した方がいいと思います。

これで銃を撃った時の当たり判定の機能が完成しました。

銃の狙いを定める部分(フロントサイト?)をカメラの中心に合わせる処理で使った小さい白い四角は作業が終わったら消してください。

わかりやすいように照準(レティクル?)として別の画像に変えて表示したままとするのもいいかもしれません。

ただ照準はずっと真ん中にあるので銃がキャラクターのアニメーションで動いても照準は動かないという風になってしまうので気を付けてください。

敵の前に障害物があった場合の対処をする

さてここまででサンプルのような敵の場合は問題なく処理が出来ていると思います。

しかし実際のゲームでは不具合が発生します。

それは敵の前に障害物があっても敵を攻撃出来てしまうというところです。

敵の前に障害物があるかどうかは銃口からぶつかった相手との距離を計算し、敵が障害物より前にある場合だけ攻撃出来るようにしなければいけません。

そこでShotスクリプトのJudge関数を改良し、間に障害物があった時の対処が出来るようにします。

あらかじめ敵のゲームオブジェクトにEnemyタグとEnemyレイヤー、障害物にはBlockタグとBlockレイヤーを設定しておきます。

障害物にタグとレイヤーを設定する

↑は障害物ゲームオブジェクト(Sphereという名前はゲームオブジェクトを作成した時のデフォルトの名前なので関係ありません)でBlockタグとレイヤーを作成し、設定しています。

SphereCastAllで接触判定をするレイヤーをBlockとEnemyにして、衝突した相手を取得します。

次にfor文でループさせて衝突した相手情報から設定されているタグがBlockのものを抽出し銃口から一番近い距離をblockDistanceに入れます。

ハンドガンタイプの場合はEnemyタグが設定されているもので抽出し、障害物の手前でかつ一番手前の敵である時にnearEnemyにそのゲームオブジェクトを入れておきます。

ハンドガンタイプ以外の場合は障害物の前の敵すべてを攻撃するようにしています。

これで対処が出来たので敵の前に障害物を置いて確認してみましょう。

障害物と敵のサンプル

↑の四角形が敵で球を障害物として設定しています。

まずは武器の種類をハンドガンに設定した場合の実行例を見てみましょう。

ハンドガンタイプの攻撃サンプル

↑が実行結果です。

障害物前の敵(球)はひとつづつ消えていますね。

また、障害物の後ろの敵は攻撃を受けません。

次に銃のタイプをハンドガンからショットガンに変更して試してみます。

銃のタイプをショットガンに変更

↑のように銃のタイプをShotGunに変更します。

今回は確認の為ShotGunに変更して試しますが試した後はHandGunに戻してください。

ショットガンタイプの攻撃サンプル

↑が攻撃した時の実行結果です。

障害物前の敵は一気に攻撃され消えています。

しかし障害物の後ろの敵は攻撃されないようになっています。

これで障害物を考慮した判定が出来るようになりました。

FPSを作ってみようカテゴリの最近の記事について

今回はPhysics.RaycastやPhysics.LinecastではなくPhysics.SphereCastAllを使って幅のあるレイを飛ばして当たり判定をしてみましたが、

思ってたより当たり判定が広くなったような気がします。

弾のサイズと同じ球のレイを飛ばしましたが判定の幅が広いような?

今回の場合はCubeを撃って消してるだけで、人型の敵を登場させて動かして試してみるまではどうなるかわかりませんね。

FPSのカテゴリを作った時は初心者向きだとか言ってましたけど、なんだか本格的な感じになってきましたね(^_^;)

ペースもゆっくりといいながら結局は他の記事と同じペースに・・・。

まずいまずい・・・・。

またゆっくりにしていかないと・・・・(;一_一)