原カバンは鞄のお店ではありません。

Unityを使ったゲーム制作のあれこれを綴っていきます。

【Unity】オブジェクトの衝突位置を取得する

暑い・・・

去年と比べて今年は全然暑くないなぁ、とか思っていたら梅雨明けと同時に蒸し暑い季節がやってきました。

兎に角、動くたびに汗をかくので制汗シートが欠かせないのですが、この前、薬局で売っていたICE-Typeというやつを購入してみたら、室内だと冷えすぎて逆に痛いです。

シートには40枚分とか書いてるんですが、どうしよう。

 

本題

さて、ご存じのように次回作は2.5Dのシューティングゲームになる予定なのですが、2.5Dというだけあってある程度広い空間(ステージ)の中を強制スクロールで進んでいく形式になっています。


なので自機の移動範囲をそのステージ内に収める必要があるのですが、この『ステージ内に収める』という仕様を満たすために外壁となるオブジェクトを設定して、その外壁に自機がぶつかったら強制的にステージ内に戻す、みたいな処理を作ることにしました。

つまり今回はステージの外壁部分との衝突を判定しよう、というお話。

 

衝突判定

通常、Unityで衝突判定を行う場合は「当てる側」「当てられる側」の両方にColliderコンポーネントを設定して、OnTrigger系関数かOnCollision系関数で判定する手法を取ります。

 

衝突判定にOnTriggerとOnCollisionの二種類あるのは、OnCollisionを使う場合はCollider以外に物理演算用のコンポーネント『Rigidbody』をオブジェクトに設定する必要があるためで、「動くオブジェクト=Rigidbody有り」「動かないオブジェクト=Rigidbody無し」みたいな使い分けを行う場合のために、OnTrigger系関数とOnCollision系関数の二種類が用意されています。

 

ただ、OnTrigger系関数を使う場合は衝突の有無は判定できますが、衝突したオブジェクトはすり抜けしまいます。また、衝突が発生した正確な位置は分からないため、衝突位置にパーティクル等のエフェクトを表示したい場合には、Collider.ClosestPoint()を使って求めた「コライダー内の最も近い点」を衝突位置として扱う事になります。

 

今回の場合、外壁とぶつかったら自機はステージの中へ戻ってほしいのでOnCollisionを使う方針にしました。

 

OnCollisionを使うために

前述のようにOnCollisionを使うためにはColliderに加えてRigidbodyが必要となります。また、ColliderにはMeshColliderを使用しているためConvexオプションをONにします。ConvexオプションはMeshCollider同士の衝突判定有無の設定です。

これを自機と外壁のオブジェクトどちらにも設定します。

 

f:id:Karvan:20190730232912p:plain

自機側のMeshCollider(見え辛いけど…)

 

f:id:Karvan:20190730232955p:plain

自機側のInspector設定

 

OnCollisionで衝突を検知するには外壁側にもRigidbodyが必要なのですが、外壁部分は動かないオブジェクトなのでIsKinematicオプションをONにして物理演算を無効に設定します

ただIsKinematicをONにしても衝突によるRigidbodyの影響は受けるので、自機に比べてMass(重量)を大きく設定します。

 

f:id:Karvan:20190730233151p:plain

 

f:id:Karvan:20190730233209p:plain

外壁側のColliderとInspector設定

 

 スクリプト側の処理

ここまで設定すれば衝突時にOnCollision系関数が呼ばれるようになります。


自機側のOnCollisionEnter(衝突発生時)にて衝突位置を求めて、その衝突位置から自機へ向かうベクトルの方向にAddForceで力を加えてやれば自機がステージ内に戻ってくれます。

 
/// <summary>
/// 衝突発生時の処理
/// </summary>
/// <param name="collision">衝突したCollider</param>
private void OnCollisionEnter(Collision collision)
{
	float boundsPower = 10.0f;
	
	// 衝突位置を取得する
	Vector3 hitPos = collision.contacts[0].point;
	
	// 衝突位置から自機へ向かうベクトルを求める
	Vector3 boundVec = this.transform.position - hitPos;
	
	// 逆方向にはねる
	Vector3 forceDir = boundsPower * boundVec.normalized;
	this.GetComponent<Rigidbody>().AddForce(forceDir, ForceMode.Impulse);
}

上のソースでは直に取得していますが、GetComponentでRigidbodyを取得している箇所は、Start関数などで事前に取得しておく方が負荷が少ないです。

動作確認

実際に動かしてみるとこんな感じになります。
見た目が地味だったので、上の処理に加えて衝突位置にパーティクルを表示するようにしています。
 

f:id:Karvan:20190730233626g:plain

 まぁ、パーティクルをつけても地味ですが・・・

 

◇プライバシーポリシー

●個人情報の利用目的

当ブログでは、メールでのお問い合わせ、メールマガジンへの登録などの際に、名前(ハンドルネーム)、メールアドレス等の個人情報をご登録いただく場合がございます。

これらの個人情報は質問に対する回答や必要な情報を電子メールなどをでご連絡する場合に利用させていただくものであり、個人情報をご提供いただく際の目的以外では利用いたしません。

●個人情報の第三者への開示

当サイトでは、個人情報は適切に管理し、以下に該当する場合を除いて第三者に開示することはありません。

・本人のご了解がある場合
・法令等への協力のため、開示が必要となる場合

個人情報の開示、訂正、追加、削除、利用停止
ご本人からの個人データの開示、訂正、追加、削除、利用停止のご希望の場合には、ご本人であることを確認させていただいた上、速やかに対応させていただきます。

アクセス解析ツールについて

当サイトでは、Googleによるアクセス解析ツール「Googleアナリティクス」を利用しています。

このGoogleアナリティクスはトラフィックデータの収集のためにCookieを使用しています。このトラフィックデータは匿名で収集されており、個人を特定するものではありません。
この機能はCookieを無効にすることで収集を拒否することが出来ますので、お使いのブラウザの設定をご確認ください。

●免責事項

当サイトからリンクやバナーなどによって他のサイトに移動された場合、移動先サイトで提供される情報、サービス等について一切の責任を負いません。

当サイトのコンテンツ・情報につきまして、可能な限り正確な情報を掲載するよう努めておりますが、誤情報が入り込んだり、情報が古くなっていることもございます。

当サイトに掲載された内容によって生じた損害等の一切の責任を負いかねますのでご了承ください。

●プライバシーポリシーの変更について

当サイトは、個人情報に関して適用される日本の法令を遵守するとともに、本ポリシーの内容を適宜見直しその改善に努めます。

修正された最新のプライバシーポリシーは常に本ページにて開示されます。