第11回 エネミーのHP、弾の攻撃力、アニメーションの追加
前章ではタイトルを付け、ゲームの「開始」と「終了」を明確にしました。 次は、ゲームを面白くする要素として「HP(ヒットポイント)」と「弾の攻撃力」そして「エネミーがダメージを受けた時のアニメーション」「プレイヤーの無敵アニメーション」を実装します。
まずはEnemy.csにヒットポイントを実装します。
Enemy.cs
using UnityEngine;
using System.Collections;
public class Enemy : MonoBehaviour
{
// ヒットポイント
public int hp = 1;
// Spaceshipコンポーネント
Spaceship spaceship;
IEnumerator Start ()
{
// Spaceshipコンポーネントを取得
spaceship = GetComponent<Spaceship> ();
// ローカル座標のY軸のマイナス方向に移動する
Move (transform.up * -1);
// canShotがfalseの場合、ここでコルーチンを終了させる
if (spaceship.canShot == false) {
yield break;
}
while (true) {
// 子要素を全て取得する
for (int i = 0; i < transform.childCount; i++) {
Transform shotPosition = transform.GetChild (i);
// ShotPositionの位置/角度で弾を撃つ
spaceship.Shot (shotPosition);
}
// shotDelay秒待つ
yield return new WaitForSeconds (spaceship.shotDelay);
}
}
// 機体の移動
public void Move (Vector2 direction)
{
GetComponent<Rigidbody2D>().velocity = direction * spaceship.speed;
}
void OnTriggerEnter2D (Collider2D c)
{
// レイヤー名を取得
string layerName = LayerMask.LayerToName (c.gameObject.layer);
// レイヤー名がBullet (Player)以外の時は何も行わない
if (layerName != "Bullet (Player)") return;
// 弾の削除
Destroy(c.gameObject);
// 爆発
spaceship.Explosion ();
// エネミーの削除
Destroy (gameObject);
}
}
次にBullet.csに攻撃力を実装します。
Bullet.cs
using UnityEngine;
public class Bullet : MonoBehaviour
{
// 弾の移動スピード
public int speed = 10;
// ゲームオブジェクト生成から削除するまでの時間
public float lifeTime = 1;
// 攻撃力
public int power = 1;
void Start ()
{
// ローカル座標のY軸方向に移動する
GetComponent<Rigidbody2D>().velocity = transform.up.normalized * speed;
// lifeTime秒後に削除
Destroy (gameObject, lifeTime);
}
}
Enemy.csのOnTriggerEnter2Dメソッド内にヒットポイントを減らしていく処理を実装していきます。 今回はたまの攻撃力の数値でヒットポイントを減らしていきます。
Enemy.cs
using UnityEngine;
using System.Collections;
public class Enemy : MonoBehaviour
{
// ヒットポイント
public int hp = 1;
// Spaceshipコンポーネント
Spaceship spaceship;
IEnumerator Start ()
{
// Spaceshipコンポーネントを取得
spaceship = GetComponent<Spaceship> ();
// ローカル座標のY軸のマイナス方向に移動する
Move (transform.up * -1);
// canShotがfalseの場合、ここでコルーチンを終了させる
if (spaceship.canShot == false) {
yield break;
}
while (true) {
// 子要素を全て取得する
for (int i = 0; i < transform.childCount; i++) {
Transform shotPosition = transform.GetChild (i);
// ShotPositionの位置/角度で弾を撃つ
spaceship.Shot (shotPosition);
}
// shotDelay秒待つ
yield return new WaitForSeconds (spaceship.shotDelay);
}
}
// 機体の移動
public void Move (Vector2 direction)
{
GetComponent<Rigidbody2D>().velocity = direction * spaceship.speed;
}
void OnTriggerEnter2D (Collider2D c)
{
// レイヤー名を取得
string layerName = LayerMask.LayerToName (c.gameObject.layer);
// レイヤー名がBullet (Player)以外の時は何も行わない
if (layerName != "Bullet (Player)") return;
// PlayerBulletのTransformを取得
Transform playerBulletTransform = c.transform.parent;
// Bulletコンポーネントを取得
Bullet bullet = playerBulletTransform.GetComponent<Bullet>();
// ヒットポイントを減らす
hp = hp - bullet.power;
// 弾の削除
Destroy(c.gameObject);
// ヒットポイントが0以下であれば
if(hp <= 0 )
{
// 爆発
spaceship.Explosion ();
// エネミーの削除
Destroy (gameObject);
}
}
}
WaveプレハブのEnemyを3つ選択し、HPの値を10にします。
ゲームを再生してみましょう。プレイヤーの弾に何発か当たらないとエネミーは爆発しなくなります。
エネミーがダメージを受けたときに、わかりやすく機体の色を赤色にします。
図11.1:
新しいレイヤーを作成し、ダメージを受けた時の表現を実装する(完成図)
エネミーには既にNormalという「スプライトを切り替えていく」アニメーションがあります。今回はそのNormalアニメーションに、色を変化させるアニメーションを「上書き」する形でダメージを表現します。
レイヤーを追加します。追加した後は、「NameをDamage Layer、Weightを1にしてください。
図11.2: レイヤーの追加
図11.3: レイヤー名とウェイトの変更
図11.3にBlendingという項目があり、これがOverrideでWeightが1であれば「他のレイヤーで同じ色のアニメーションをしている場合でも、Damage
Layerの色のアニメーションを使用する」事になります。
Enemyフォルダ上で右クリックし、Create → Animation
を選択してアニメーションのアセットを作成します。名前はDamageとしてください。
図11.4:
フォルダを右クリックしてメニューを選択するとフォルダ下に作成される
作成したらアニメーターに登録するためにDamageアニメーションをアニメーターのDamage Layer上にドラッグ&ドロップします。
次はアニメーションを作成していきます。一度Enemyのプレハブをシーン上へドラッグ&ドロップしてください。
Enemyゲームオブジェクトを選択しながら、Window → Animationを選択し、Animationウィンドウを開きます。 開いたらNormalアニメーションが選択されているので、Damageアニメーションへ変更します。
アニメーションを行うプロパティを登録します。Add Curve
ボタンでSprite
Renderer → Colorを追加してください。
するとデフォルトでKeyが2箇所、設定されているので右側のKeyを削除します。Keyを右クリックしてDelete Keysを選択してください。
更に2つの操作を行います。 まずは、0フレーム目にあるKeyを5フレーム目に移動させます。一番上のKeyをドラッグして5フレームまで移動させてください。フレーム数の確認は左上で確認することが出来ます。 次に色の情報を変更します。Color.rを1、Color.gを0、Color.bを0.6、Color.aを1にしてください。
インスペクターのプレビューでエネミーが赤色になっているはずです。
ダメージを受けた時のみエネミーを赤色にするため、トリガーという機能を使います。
まずアニメーター上で空のステートを作成します。アニメーターウィンドウ上で右クリックをして、Create State → Empty
を選択してください。
名前をDummyとします。ステートの名前を変更するにはインスペクター上で行います。ステートをクリックしてインスペクターを表示させましょう。
次にDummyをデフォルトステートとします。Dummyステート上で右クリックしてSet As Default
を選択してください。デフォルトステートはゲーム再生直後に再生されるステートのことを指します。こうすることによって、ゲーム再生時、Damage
LayerではDummyステートが再生され、何もアニメーションを行わない状態になります。
次は何も行わない状態からトリガーによって**Damageアニメーションを再生 → 何も行わない状態 → Damageアニメーション...**と繰り返し状態の遷移を行うようにします。 Dummy上で右クリックし、Make Transitionを選択します。
カーソルに矢印が追従するのでDamageステート上でクリックします。そうするとDummyステートからDamageステートへ何らかの条件で遷移する事ができるようになります。
同じようにDamageステートからDummyステートへのTransitionも作成しましょう。
次にトリガーを作成します。アニメーターウィンドウの左上にあるParametersタブを押してParametersに切り替え、+ボタンを押してTriggerを選択してください。
名前をDamageとします。
DummyステートからDamageステートへのTransitionをクリックし、表示されたインスペクターのConditionsを+を押して追加し、をDamageへと変更します。さらに、Has Exit Timeのチェックを外しておきましょう。DamageステートからDummyステートへのTransitionはそのままにしておきましょう。
図11.5: ConditionsをDamageへ
図11.6: そのまま変更なし
ダメージを受けた時に「トリガーをセット」してダメージアニメーションを再生させます。 まずはSpaceship.csにAnimatorコンポーネントを取得するメソッドを追加します。
Spaceship.cs
using UnityEngine;
[RequireComponent(typeof(Rigidbody2D), typeof(Animator))]
public class Spaceship : MonoBehaviour
{
// 移動スピード
public float speed;
// 弾を撃つ間隔
public float shotDelay;
// 弾のPrefab
public GameObject bullet;
// 弾を撃つかどうか
public bool canShot;
// 爆発のPrefab
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;
}
}
OnTriggerEnter2Dメソッド内にある、エネミーのHPをチェックしている部分を「HPが0以上であればDamageトリガーをセットする」ようにします。
Enemy.cs
using UnityEngine;
using System.Collections;
public class Enemy : MonoBehaviour
{
// ヒットポイント
public int hp = 1;
// Spaceshipコンポーネント
Spaceship spaceship;
IEnumerator Start ()
{
// Spaceshipコンポーネントを取得
spaceship = GetComponent<Spaceship> ();
// ローカル座標のY軸のマイナス方向に移動する
Move (transform.up * -1);
// canShotがfalseの場合、ここでコルーチンを終了させる
if (spaceship.canShot == false) {
yield break;
}
while (true) {
// 子要素を全て取得する
for (int i = 0; i < transform.childCount; i++) {
Transform shotPosition = transform.GetChild (i);
// ShotPositionの位置/角度で弾を撃つ
spaceship.Shot (shotPosition);
}
// shotDelay秒待つ
yield return new WaitForSeconds (spaceship.shotDelay);
}
}
// 機体の移動
public void Move (Vector2 direction)
{
GetComponent<Rigidbody2D>().velocity = direction * spaceship.speed;
}
void OnTriggerEnter2D (Collider2D c)
{
// レイヤー名を取得
string layerName = LayerMask.LayerToName (c.gameObject.layer);
// レイヤー名がBullet (Player)以外の時は何も行わない
if (layerName != "Bullet (Player)") return;
// PlayerBulletのTransformを取得
Transform playerBulletTransform = c.transform.parent;
// Bulletコンポーネントを取得
Bullet bullet = playerBulletTransform.GetComponent<Bullet>();
// ヒットポイントを減らす
hp = hp - bullet.power;
// 弾の削除
Destroy(c.gameObject);
// ヒットポイントが0以下であれば
if(hp <= 0 )
{
// 爆発
spaceship.Explosion ();
// エネミーの削除
Destroy (gameObject);
}else{
spaceship.GetAnimator().SetTrigger("Damage");
}
}
}
最後にEnemyゲームオブジェクトを削除して、ゲームを再生してみましょう。
弾が当たった時にエネミーが赤色になりました。ですが徐々に赤色になり、徐々に元の色に戻るアニメーション(フェード)になっています。
もう一度、アニメーターのTransitionのインスペクターを確認しましょう。
図11.7と図11.8のようにDummyステートからDamageステートへのTransitionのFadeのつまみを限りなく近づけてみましょう。
図11.7:
図11.8:
DamageステートからDummyステートへのTransitionも同じ設定を行います。ただし、Exit Timeを1へ設定してください。
図11.9:
図11.10:
ゲームを再生してみてください。ダメージを受けた時、瞬間的に赤色になるはずです。
ゲーム開始時にプレイヤーを点滅させ、無敵の状態を実装します。 実装方法はエネミーのダメージアニメーションの実装と同じです。
PlayerアニメーターコントローラーをダブルクリックしてAnimatorウィンドウを開きます。
Invincible Layerを追加します。Weightを1にするのを忘れないで下さい。
Playerプレハブをシーン上へドラッグ&ドロップしてください。
次にアニメーションのアセットを作成します。Playerフォルダを選択し右クリックをして、Create → Animation
を選択しください。
作成されたアニメーションのアセット名はInvincibleとします。
InvincibleアニメーションのLoop Timeにチェックを入れてください。
Invincible LayerにInvincibleアニメーションをドラッグ&ドロップし、Invincibleステートを作成します。
Create State → Empty
でDummyステートを作成し、InvincibleステートからDummyステートへのTransitionを作成します。
TransitionのExit
Timeは3にしてください。これは3回Invincibleアニメーションをループさせる処理になります。
次にアニメーションを作成していきます。Playerゲームオブジェクトを選択しながらAnimationウィンドウを開き、Invincibleアニメーションを選択してください。
Add Curve
ボタンでSprite Renderer → EnabledとBox
Collider 2D → Enabledの2つを追加してください
図11.11:
図11.12:
0フレーム目はオフに、60フレーム目はオンにします。ただし、Box Collider 2Dのキーは削除してください。Invincibleアニメーション再生中にコライダーを無効化し、当たり判定を発生させないことで無敵を実現させます。
図11.13:
図11.14:
これを交互に60フレームまで「オフ、オン、オフ、オン...」というようにキーを打ち込んでください。この作業を楽にする方法として、キーのコピー&ペーストがあります。キーを選択し⌘(Ctrl)+C
でコピーしたら0:10の文字があるところをマウスでクリックし⌘(Ctrl)+V
でペーストしてください。
最後にPlayerゲームオブジェクトを削除して、ゲームを再生してみてください。ゲームを開始すると点滅の無敵状態が3秒間続きます。
今回はここで終了です。つまずいてしまった方はプロジェクトファイルをダウンロードして新たな気持ちで次の回へ進みましょう。
第01回 スプライトとスプライトアニメーションの作成
第02回 プレイヤーの移動
第03回 プレイヤーから弾を撃つ
第04回 敵を作成しよう
第05回 当たり判定とアニメーションイベントとレイヤー
第06回 背景を作る
第07回 Wave型の仕組み作り
第08回 音をつける
第09回 プレイヤーの移動制限と様々な修正
第10回 タイトルを付ける
第11回 エネミーのHP、弾の攻撃力、アニメーションの追加
第12回 Waveを5個にする、スコアの実装
第01回 複数の解像度に対応する(黒帯を追加する)
第02回 複数の解像度に対応する(引き伸ばす)
第03回 タッチパネル対応
第04回 バーチャルジョイスティック対応