Unity3Dで2Dシューティングゲームの作り方、パート4 (HP、攻撃力、被弾時のアニメーション、無敵時間、多種類の敵集団、スコアの表示)

2Dシューティングゲームの作り方の続き、パート4です。

Unity3Dでゲーム作りを開始して1ヶ月でオリジナルのゲームを作る!
無理でした! いろいろあるチュートリアルだけでも、少なく見積もってもあと1ヶ月かかりそうです。

あせらず、コツコツ進めましょう。
年内にはオリジナルのゲームができるかな?


エネミーのHP、弾の攻撃力、アニメーションの追加

Unity Japan: 第11回 エネミーのHP、弾の攻撃力、アニメーションの追加

HPと攻撃力(power)の実装
EnemyスクリプトのEnemyクラスにhpを追加します。
public int hp = 1;

BulletスクリプトのBulletクラスにpowerを追加します。
public int power = 1;


ヒットポイントが0になった時に爆発させる
Enemyのhpが0になったときに爆発させます。

EnemyスクリプトのOnTriggerEnter2Dを変更する。
void OnTriggerEnter2D (Collider2D c){
	string layerName = LayerMask.LayerToName (c.gameObject.layer);

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

	Transform playerBulletTransform = c.transform.parent;

	Bullet bullet = playerBulletTransform.GetComponent<Bullet>();

	hp = hp - bullet.power;

	Destroy (c.gameObject);

	if(hp <= 0){
		spaceship.Explosion ();
		Destroy (gameObject);
	}
}

PrefabのWaveの3つのEnemyのhpを10にする。
これで敵に10発弾を当てないと敵が爆発しなくなります。


敵がダメージを受けた時に色を変える

Window → Animator
Layersの右側の+をクリックして、新しいLayerを作ります。
Layerの名前をDamage Layer、Weightは1にします。

AssetsのAnimationsのEnemyのところに新しいAnimationを作成します。
Animationの名前をDamageにして、DamageをAnimatorビューにドロップします。

PrefabのEnemyをシーンビューにドロップします。
Enemyオブジェクトを選択したまま、Animationビューに切り替えます。
Normalとう項目があるので、それをクリックしてDamageという項目に変更します。
Add Curveを押して、Sprite RendererからColorを追加します。
一番右側の5つの◇のマークを右クリックしてDelete Keysをして削除します。
左側にある5つの◇のマークをクリックして5フレーム目になるまで右側にドラッグします。
Color.r:1 Color.g:0 Color.b:0.6 Color.a:1に変更します。

今こんな感じになっています。



トリガーの作成
ダメージを受けたときだけEnemyに赤色をつけるためにトリガーを使います。
Animatorビュー上で右クリックをしてステータスを作ります。
Create Status → Empty、名前はDummyにします。
Dummyを右クリックしてSet As Defaultに設定します。
これでゲーム再生時にDummyステートが再生されAnimationは何も行いません。
Dummyを右クリックしてMake Transitionをクリックします。
すると矢印が出てくるので、それをDamageにくっつけます。
同じくDamageを右クリックしてMake Transitionをクリックして出てくる矢印をDummyにくっつけます。

Parametersの+をクリックしてTriggerを作成して名前をDamageにします。
DummyステートからDamageステートへのTransition(下向きの矢印)をクリックしてInspectorのConditionsをDamageに変更します。

トリガーはこんな感じになっています。



スクリプトからトリガーをセットする
SpaceshipスクリプトにAnimatorコンポーネントを取得するメソッドを追加します。
using UnityEngine;

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

	public float speed;
	public float shotDelay;
	public GameObject bullet;
	public bool canShot;
	public GameObject explosion;
	private Animator animator;

	void Start(){
		animator = GetComponent<Animator> ();
	}

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

	public void Shot (Transform origin) {
		Instantiate (bullet, origin.position, origin.rotation);
	}

	public Animator GetAnimator(){
		return animator;
	}
}

EnemyスクリプトでOnTriggerEnter2Dが呼び出されたときhpがゼロでなければDamageトリガーをセットするように変更します。
if (hp <= 0) {
	spaceship.Explosion ();
	Destroy (gameObject);
} else {
	spaceship.GetAnimator().SetTrigger ("Damage");
}
Enemyオブジェクトを削除します。Playボタンを押してゲームをしてみると、敵が弾に当たると赤くなるようになりました。

微妙に長い赤い点滅が気に食わないので短く点滅するように変更します。

AnimatorビューのDummyからDamageへのTransition


DamageからDummyへのTransition


こんな感じで変更するとしゃかしゃか点滅するようにあります。


プレイヤーに無敵時間を作る
ゲーム開始時の一定時間、プレイヤーが敵や敵の弾に当たっても爆発しない無敵時間を作ります。

レイヤーの追加
Assets → Animations → Player
SpaceshipをPlayerという名前に変更します。
Playerを選択したまま、Animatorビューを開きます。
新しいLayerを追加して名前をInvincible Layer、Weightを1に変更します。

アニメーションの作成
PrefabからPlayerを選択してシーンビューにドロップします。
Assets → Animations → Playerに新しいAnimationを作成して名前をInvincibleにします。
AnimationのInvincibleをAnimatorビューにドロップします。
InvincibleのLoop Timeをチェックします。
AnimatorビューでCreate StateでDummyという名前のStateを作成します。
InvincibleからDummyへTransitionを作成します。

Transitionが下記のようになるように調整します。(これでInvincibleが3回ループします)



次にPlayerオブジェクトを選択してAnimationビューを開きます。
Normalという項目をInvincibleに変更します。
Add Curve → Sprite Renderer → Enabledを追加します。
Add Curve → Box Collider 2D → Enabledを追加します。

Add Keyをしてキーを追加してBox Collider 2Dのキーは削除します。
ちなみにダブルクリックするとコピーしたキーをペーストすることができます。

最終的には下記のようになります。



Sprite Rendererのキーは、チェックなし、チェックあり、チェックなし、・・以下繰り返しというように設定します。
この繰り返しでゲーム開始時の3秒間、自機が点滅するようになります。Box Collider 2Dのチェックをはずしていることから、3秒間無敵状態になります。

Waveを5個にする、スコアの実装

Unity Japan: 第12回 Waveを5個にする、スコアの実装

4種類のWaveを作成します。
基本的にPrefabのEnemyをシーンビューにドロップしてhpとCan Shotを変更等をして最後にWaveをPrefabに保存します。

Wave2 沢山の敵
弾を撃たないHPが4の敵

Wave3 ちょっと強い敵
弾を撃つHPが10の敵

Wave4 前後前から来る敵
弾を撃つHPが10の敵

Wave5 大きな敵
弾を撃つHPが100の大きな敵

全部作成したらEmitterオブジェクトを選択してWavesのSizeを5にして、
Element1にWave2、Element2にWave3、Element3にWave4、Element4にWave5を設定します。

Playボタンを押すと、Wave → Wave2 → Wave3 → Wave4 → Wave5 → Wave → 以下ループするはずなのですが、しません!!!

Wave4が終わると、敵が出なくなります・・。
なぜでしょう??

コードを見ながら問題点を探し中・・・。
いろいろ試してみたところWave4に問題がある。
これを抜かすか別のWaveに入れ替えるかするとループする。

Wave4だけが枠外に敵機が設置されてるんだよね・・。
うーん。

敵が消えていないっぽい。


わかった。

Emitterの位置Yが3.5になっていて、
Wave4の左右に配置した敵の位置Yが+3.5加算されて枠外を飛んでいってしまって、そのまま生き延びちゃうから次のWaveが始まらなくなってた。Wave4の左右に配置した敵の位置Yを追加で-3.5すると問題解決した。


スコアの表示
空のGameObjectを作成して名前をScore GUIにして位置をリセットします。
2つのGUI Textを作って名前をHighScoreとScoreにしてScore GUIの子要素にします。

HighScoreオブジェクトの設定
Position X:1 Y:0 Z:0
Text: 0
Anchor: lower right
Font: AssetsのSAM_5C_27TRG_をドラッグ
Font Size: 24

Scoreオブジェクトの設定
Position X:1 Y:1 Z:0
Text: 0
Anchor: upper right
Font: AssetsのSAM_5C_27TRG_をドラッグ
Font Size: 24

スクリプトの作成
スコアの表示をするScoreスクリプトを作成します。
using UnityEngine;

public class Score : MonoBehaviour {

	public GUIText scoreGUIText;
	public GUIText highScoreGUIText;
	private int score;
	private int highScore;
	private string highScoreKey = "highScore";

	void Start () {
		Initialize ();	
	}
	
	void Update () {
		if(highScore < score){
			highScore = score;
		}
		scoreGUIText.text = score.ToString ();
		highScoreGUIText.text = "HighScore : " + highScore.ToString ();
	}

	private void Initialize(){
		score = 0;
		highScore = PlayerPrefs.GetInt (highScoreKey, 0);
	}

	public void AddPoint(int point){
		score = score + point;
	}

	public void Save(){
		PlayerPrefs.SetInt (highScoreKey, highScore);
		PlayerPrefs.Save ();

		Initialize ();
	}
}

作成したScoreスクリプトをScore GUIオブジェクトにアタッチします。
ScoreオブジェクトをScore GUIオブジェクトのScore(Script)のScore GUITextにドロップします。
HighScoreオブジェクトをScore GUIオブジェクトのScore(Script)のHigh Score GUITextにドロップします。


エネミーにポイントを持たせる
Enemyスクリプトを少し変更します
public int hp = 1;
public int point = 100; #これを追加する

敵が破壊されたときポイントを追加するようにする
if (hp <= 0) {
	FindObjectOfType<Score>().AddPoint (point);

	spaceship.Explosion ();
	Destroy (gameObject);
} else {
	spaceship.GetAnimator().SetTrigger ("Damage");
}

Prefabの各WaveのEnemyを難易度によってポイントを変えておく。たとえばでかい敵には300ポイントをあげるとか。


ハイスコアを保存する
ManagerスクリプトでGaveOverしたときにハイスコアを保存させるようにします。
public void GameOver(){
	FindObjectOfType<Score> ().Save ();

	title.SetActive (true);
}


これでひとまず完成です!



2Dシューティングゲームの完成品


弾を撃たない弱い敵だけ破壊して、あとは逃げればスコアを伸ばせるはず!

とりあえずゲーム的には完成ですが、
次回に続きます!

次回はモバイル、iOS、最適化、録画等をやって締めくくろうと思います。

コメントを残す

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