Unity3Dで2Dシューティングゲームの作り方、パート2(自機と敵機の共通化、弾発射、通常の当たり判定、レイヤーで当たり判定)

2Dシューティングゲームの作り方の続き、パート2です。
どんどん作っていきましょう。

敵を作成する

Unity Japan: 第04回 敵を作成しよう

Spaceship.csの作成
自機と敵が共通してもつ機能をSpaceshipにまとめます。

Spaceshipスクリプト

using UnityEngine;

[RequireComponent(typeof(Rigidbody2D))]
public class Spaceship : MonoBehaviour {

	public float speed;
	public float shotDelay;
	public GameObject bullet;

	public void Shot (Transform origin) {
		Instantiate (bullet, origin.position, origin.rotation);
	}
	
	public void Move (Vector2 direction) {
		rigidbody2D.velocity = direction * speed;
	}
}

Spaceshipスクリプトを使って、Playerスクリプトを簡素化します。

using UnityEngine;
using System.Collections;

public class Player : MonoBehaviour {

	Spaceship spaceship;

	IEnumerator Start() {
		spaceship = GetComponent<Spaceship> ();

		while(true){
			spaceship.Shot (transform);
			yield return new WaitForSeconds (spaceship.shotDelay);
		}
	}

	// Update is called once per frame
	void Update () {
		float x = Input.GetAxisRaw ("Horizontal");
		float y = Input.GetAxisRaw ("Vertical");

		Vector2 direction = new Vector2 (x, y).normalized;
		spaceship.Move (direction);
	}
}

PlayerオブジェクトにSpaceshipスクリプトをアタッチ(ドラッグ&ドロップ)します。
PlayerオブジェクトのInspectorのSpaceship(Script)を下記のように変更します。

Speed: 5
Shot Delay: 0.05
Bullet: PrefabのPlayerBulletをアタッチします。

敵を表示する
PrefabのEnemyをシーンビューにドロップします。
SpaceshipをEnemyオブジェクトにアタッチします。

敵を移動させる
新たにEnemyスクリプトを作成します。

Enemyスクリプト

using UnityEngine;
using System.Collections;

public class Enemy : MonoBehaviour {
	Spaceship spaceship;

	// Use this for initialization
	void Start () {
		spaceship = GetComponent<Spaceship> ();

		spaceship.Move (transform.up * -1);
	}
}

EnemyスクリプトをEnemyオブジェクトにアタッチします。
Enemyオブジェクトを選択してInspectorのSpaceship(Scriptを変更します)のSpeedを0.5にします。
Gravity Scaleも0にします。

Playボタンを押すとEnemyが下に向かって行くようになります。

弾を作成する
SpritesのBullet_1をシーンビューにドラック&ドロップします。
HierarchyのBullet_1という名前をEnemyBulletに変更します。
EnemyBulletにBulletスクリプトをアタッチしてRigidbody2Dを追加します。
BulletのSpeedを2、Gravity Scaleを0にします。
EnemyBulletをPrefabsにドラッグ&ドロップしてPrefab化します。
HierarchyのEnemyBulletを削除します。
Enemyオブジェクトを選択して、PrefabsのEnemyBulletをInspectorのSpaceship(Script)のBulletにアタッチします。

弾を発射する
敵は3つの角度から弾を発射できるようにします。
空のGameObjectを作成してShotPositionという名前に変更します。
ShotPositionを3つ作って、全てHierarchyのEnemyにドラッグ&ドロップします。
ShotPositionのRotationのZをそれぞれ160、180、200に変更します。
ShotPositionのPositionのYを-0.3にしてあとは全て0にします。

ShotPositionから弾を撃つ
Enemyオブジェクトで3つのShotPositionから弾をようにEnemyスクリプトを修正します。

Enemyスクリプト

using UnityEngine;
using System.Collections;

public class Enemy : MonoBehaviour {
	Spaceship spaceship;

	// Use this for initialization
	IEnumerator Start () {
		spaceship = GetComponent<Spaceship> ();

		spaceship.Move (transform.up * -1);

		while (true){
			for (int i=0; i<transform.childCount; i++){
				Transform shotPosition = transform.GetChild(i);

				spaceship.Shot(shotPosition);
			}

			yield return new WaitForSeconds (spaceship.shotDelay);
		}
	}
}

これで敵が弾をうにょうにょ発射するようになります。

弾を撃たない敵を作る
Spaceshipスクリプトのpublic GameObject bulletの後に下記のようにコードを追加します。

public GameObject bullet;
public bool canShot;

Enemyスクリプトのspaceship.Move (transform.up * -1)の後に下記のようにコードを追加します。

spaceship.Move (transform.up * -1);

if (spaceship.canShot == false) {
	yield break;
}

これでEnemyオブジェクトのInspectorに表示される、
Can shotをチェックすると敵が弾を撃って、チェックをはずすと弾を撃たなくなります。

Can shotにチェックを入れて敵が弾を撃っているところ

当たり判定とアニメーションイベントとレイヤー

Unity Japan: 第05回 当たり判定とアニメーションイベントとレイヤー

プレイヤーに当たり判定を付ける。
PlayerオブジェクトにBox Collider 2Dをアタッチします。
SizeをXとY両方とも0.05に変更します。

Playerをトリガーにする。
PlayerオブジェクトのBox Collider 2DのIs Triggerにチェックを入れる。
ぶつかったときに反発しますが、トリガーに設定すると反発しなくなります。

エネミーに当たり判定を付ける。
EnemyオブジェクトにPolygon Collider 2Dをアタッチします。
コライダーの形状を編集します。
まず、EnemyオブジェクトのSprite Rendererの右三角のマークをクリックして閉じます。これでシーンビューでEnemyオブジェクトの位置が固定されます。これをしないと、コライダーの形状の編集がうまくいかないそうです。

緑の線のところにマウスを持っていってShiftを押すとその線を選択できて動かすことができます。ちょこちょこ直して下記のようにします。Ctrlを押すと削除できます。

最後に、EnemyオブジェクトのPolygon Collider 2DのIs Triggerにチェックを入れます。

弾に当たり判定を付ける。
PrefabからPlayerBulletとEnemyBulletを選択してシーンビューにドロップします。
シーンビューのEnemyBulletを選択してCircle Collider 2Dをアタッチして、Radiusを0.07に変更します。
HierarchyのPlayerBulletを展開してBulletを選択してPolygon Collider 2Dをアタッチします。

・・・。コライダーの形状の編集がうまくいきません。
Bulletが小さすぎて、0.05サイズの制限に引っかかっている気がします。
Polygon Collider 2DではなくBox Collider 2DをアタッチしてXのサイズを0.05に変更することにしました。ややでかい当たり判定になってしまいますが気にしない!

Box Collider 2Dの歯車アイコンをクリックしてCopy Componentを選択します。シーンビューでもう片方のBulletを選択してInspectorで右クリックをしてPaste Component Valuesを選択して、Box Collider 2Dをアタッチします。本当はPolygon Collider 2Dでつくったコライダーをコピペするのに便利な機能らしいです。

EnemyBulletと2つのBulletのIs Triggerをチェックします。
EnemyBulletとPlayerBulletのPrefabをApplyします。
シーン上からEnemyBulletとPlayerBulletを削除します。

スクリプトから当たり判定を検出する
まず機体があたったら爆発するようにSpaceshipスクリプトを編集します。

Spaceshipスクリプト

using UnityEngine;

[RequireComponent(typeof(Rigidbody2D))]
public class Spaceship : MonoBehaviour {

	public float speed;
	public float shotDelay;
	public GameObject bullet;
	public bool canShot;
	public GameObject explosion;

	public void Explosion () {
		Instantiate (explosion, transform.position, transform.rotation);
	}

	public void Shot (Transform origin) {
		Instantiate (bullet, origin.position, origin.rotation);
	}
	
	public void Move (Vector2 direction) {
		rigidbody2D.velocity = direction * speed;
	}
}

Playerオブジェクトを選択して、PrefabのExplosionをInspectorのSpaceship(Script)にあるExplosionにアタッチします。
Enemyオブジェクトも同じようにExplosionをアタッチします。

PlayerスクリプトのPlayerクラスに下記のコードを追加します。

void OntriggerEnter2D (Collider2D c){
	Destroy (c.gameObject);
	spaceship.Explosion ();
	Destroy (gameObject);
}

これでとりあえずPlayボタンを押してみます。

弾や敵に当たったときに自機が爆発するのはいいのですが、
このままだと爆発がとまらずに繰り返してしまいます。

爆発を制御する
アニメーションのループ設定
Assets → Animations → Explosion
Explodeを選択してLoop Timeのチェックをはずします。これで爆発がループしなくなります。

爆発が終わってループもしなくなりましたが、爆発オブジェクトがゲーム上に残ってしまっています。Explosionスクリプトを作成してスクリプトから削除できるようにします。

Explosionスクリプト

using UnityEngine;

public class Explosion : MonoBehaviour {

	void OnAnimationFinish(){
		Destroy (gameObject);
	}
}

ExplosionスクリプトをPrefabのExplosionにアタッチします。

アニメーションイベント
アニメーションが終わったときにOnAnimationFinishを呼び出してExplosionオブジェクトを削除するようにします。

Window → Animation
表示されたAnimationをドラッグしてシーンビューのタブにドロップします。

PrefabのExplosionをシーンビューにドロップします。
Explosionオブジェクトを選択したまま、シーンビューのAnimationタブをクリックします。

Add Eventができなくてウジウジいじくってたら、いつの間にかできるようになりました。下の先のとんがった長方形のマークをクリックしてEventを追加します。FunctionにOnAnimationFinishを選択します。

白いバーができるので、それを一番後ろまで動かします。
言葉で説明するのが難しいので下の画像を参考にしてください。

左にある矢印がAdd Eventです。
追加したEventが右の矢印の位置になるまで動かします。
イベントの追加が終わったらExplosionオブジェクトを削除します。

弾とエネミーが削除されるエリアを作る。
弾とエネミーが指定エリア内から外へ行った場合、削除します。
空のGameObjectを作成してDestroyAreaという名前にして、Box Collider 2Dを追加します。
Box Collider 2DのSizeをX:9 Y:7に変更してIs Triggerをチェックします。

DestroyAreaスクリプトを作成します。

using UnityEngine;

public class DestroyArea : MonoBehaviour {

	void OnTriggerExit2D(Collider2D c){
		Destroy (c.gameObject);
	}
}

スクリプトをDestroyAreaオブジェクトにアタッチします。

この状態でPlayボタンを押すと、
すぐに自機が爆発してDestroyAreaも削除されてしまいます。
これはPlayerスクリプトのOntriggerEnter2Dに引っかかるからです。
レイヤーを使うとこの問題を回避できます。

レイヤーで当たり判定を制御する
敵が敵の弾で爆発しないことや、自機が自分の弾で爆発しないようにするためにも、レイヤーで当たり判定を行います。

レイヤーを設定する
Edit → Project Settings → Tags and Layers
User Layer8: Player
User Layer9: Enemy
User Layer10: Bullet(Player)
User Layer11: Bullet(Enemy)
User Layer12: DestroyArea

当たり判定の制御
Edit → Project Settings → Physics 2D

マトリックスの組み合わせを下記のようにします。

PrefabのEnemy、EnemyBullet、Player、PlayerBulletのLayerをそれぞれ該当する名前のレイヤーに変更します。DestroyAreaオブジェクトのレイヤーもDestroyAreaに変更します。

スクリプトでレイヤー制御する
マトリクスでPlayerとDestroyAreaのチェックをはずしておけば、Playボタンを押したときに自機が爆発するということはなくなるのですが、ここではチェックははずさずにPlayerスクリプトのOnTriggerEnter2Dを変更して対処してみます。

PlayerスクリプトのOnTriggerEnter2Dの場所だけ下記に変更

void OnTriggerEnter2D (Collider2D c){
	string layerName = LayerMask.LayerToName (c.gameObject.layer);

	if(layerName == "Bullet(Enemy)"){
		Destroy (c.gameObject);
	}

	if(layerName == "Bullet(Enemy)" || layerName == "Enemy"){
		spaceship.Explosion ();
		Destroy (gameObject);
	}
}

エネミーの当たり判定
エネミーに自機の弾が当たったときに爆発させて削除します。

EnemyスクリプトのEnemyクラスに下記のコードを追加

void OnTriggerEnter2D (Collider2D c){
	string layerName = LayerMask.LayerToName (c.gameObject.layer);

	if(layerName != "Bullet(Player)") return;

	Destroy (c.gameObject);
	spaceship.Explosion ();
	Destroy (gameObject);
}

これで自機の弾が敵に当たると敵が爆発して削除されます。

PlayerBulletゲームオブジェクトを削除する
弾を発射後5秒後に削除するようにBulletスクリプトを修正します。

using UnityEngine;
using System.Collections;

public class Bullet : MonoBehaviour {

	public int speed = 10;
	public float lifeTime = 5;

	// Use this for initialization
	void Start () {
		rigidbody2D.velocity = transform.up.normalized * speed;

		Destroy (gameObject, lifeTime);
	}
}

PrefabのEnemyBulletのスピードを5、PlayerBulletのスピードは2に変更します。
これで弾が任意の時間に削除されます。

今回はここまでです。
初めてのやり方がたくさんでてきましたが、
やり方さえ覚えてしまえば、簡単に対処できてしまえそうです。

Unity3Dはすごいですね。

コメントを残す

メールアドレスが公開されることはありません。

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください