UnityでFPSをつくる その9-3 [ 敵の攻撃 ]

2020年8月13日

Playerが敵の攻撃を受けた時のUIをスクリプトで操作していきます。
「PlayerHP」と名付けたスクリプトを作成し、以下のコードを貼り付けてください。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class PlayerHP : MonoBehaviour
{
    //HPゲージ
    [SerializeField] int playerHP;

    //被ダメージ量
    [SerializeField] int receveDamageScore;

    //被ダメージ演出用ボード
    public Image damageBoard,deathStateBoard;

    //HPゲージ用のUI複数をまとめたオブジェクト
    public GameObject remainingHp;

    //カメラの動きを制御する
    Transform transformCamera;

    //カメラを揺らす時間の長さ、大きさ
    [SerializeField]float duration, magnitude;

    //相対位置取得用
    Vector3 pos;

    // Start is called before the first frame update
    void Start()
    {
        //被ダメージ演出用ボードを透明に初期化
        damageBoard.color = Color.clear;

        //カメラのTransformを取得
        transformCamera = Camera.main.transform;

        //Playerからの相対位置を取得
        pos = transformCamera.localPosition;        
    }

    // Update is called once per frame
    void Update()
    {

    }

    void OnTriggerEnter(Collider other)
    {
        //弾にぶつかったら
        if (other.tag == "EnemyBullet")
        {
            //プレイヤーのHPを減少させる
            playerHP -= receveDamageScore;

            //画面を点滅させるコルーチンの開始
            StartCoroutine("damageFlashing");

            //カメラを揺らすコルーチンの開始
            StartCoroutine(shakeCamera(duration, magnitude));

            //ダメージを受けたら対応するHPゲージを黒色にする
            if (playerHP <= 4)
            {
                remainingHp.transform.Find("RemainingHp_5").gameObject.GetComponent<Image>().color = Color.black;
            }
            if (playerHP <= 3)
            {
                remainingHp.transform.Find("RemainingHp_4").gameObject.GetComponent<Image>().color = Color.black;
            }
            if (playerHP <= 2)
            {
                remainingHp.transform.Find("RemainingHp_3").gameObject.GetComponent<Image>().color = Color.black;
            }
            if (playerHP <= 1)
            {
                remainingHp.transform.Find("RemainingHp_2").gameObject.GetComponent<Image>().color = Color.black;
            }
            if (playerHP <= 0)
            {
                remainingHp.transform.Find("RemainingHp_1").gameObject.GetComponent<Image>().color = Color.black;
                
                //スクリプトをOFFにする
                GetComponent<PlayerMove>().enabled = false;
                
                //その場で停止させる
                GetComponent<Rigidbody>().velocity = Vector3.zero;

                //死亡状態用に画面を赤くする
                deathStateBoard.color = new Color(1f, 0f, 0f, 0.9f);
            }
        }
    }

    //画面を点滅
    IEnumerator damageFlashing() 
    {
        //被ダメージ演出用ボードを薄い赤色にする
        damageBoard.color = new Color(1f, 0f, 0f, 0.7f);
        
        //次の処理に移行するまでの待機時間
        yield return new WaitForSeconds(0.15f);

        //被ダメージ演出用ボードを透明にする
        damageBoard.color = Color.clear;
    }

    //カメラを揺らす
    IEnumerator shakeCamera(float duration, float magnitude) 
    {
        //時間計測用
        var elapsed = 0f;

        //指定の時間を経過するまで
        while (elapsed < duration) 
        {
            //カメラのXY(縦横)位置を動かす
            transformCamera.localPosition = new Vector3(pos.x + Random.Range(-1f, 1f) * magnitude, pos.y + Random.Range(-1f, 1f) * magnitude, pos.z);

            //経過時間
            elapsed += Time.deltaTime;
            
            //次フレームで処理を再開。
            yield return null;
        }
        //揺れる前の位置にカメラを戻す
        transformCamera.localPosition = pos;
    }
}

コードの内容を見ていきます。
4行目、照準実装時と同様に今回もUIを扱うので名前空間「UnityEngine.UI」を宣言しています。

No.374

9,12行目では初期状態のHPと、被弾した際にどの程度減少させるかを決める変数を宣言しています。
UIの表示に合わせてHPは「5」、ダメ―ジは「1」ずつ負う設定にします。

No.375

15,18行目はさきほど作成したUIを取得する為の変数を宣言しています。

No.376

21,24,27行目はダメージを負った際、カメラを揺らす処理に使う変数を宣言しています。

No.377

33行目はImageオブジェクトの色を透明にし忘れた場合の保険として初期化をしています。

No.378

36行目でTransformの探索結果の参照を変数にキャッシュし、
39行目でカメラを揺らした後に戻す基準となる相対位置を取得しています。

No.379

48~54行目で、弾がぶつかった際にタグで判別し、HPを1減らしています。

No.380

57行目はコルーチンという任意のタイミングで処理を停止・再開できる関数を使っています。

No.381

StartCoroutineで()内の名前が付いたコルーチンを開始します。
damageFlashingの定義は96~106行目に書いています。

No.382

戻り値の型をIEnumeratorとし、yield以降に処理を中断するタイミングを記述します。
この中断が加わることで、1フレーム内で処理を完了させていた今までの関数との違いが生まれます。

No.383

弾とぶつかった際にdamageBoardを薄い赤色にして、0.15秒経過後に初期状態と同じ透明に戻しています。

60行目はカメラを揺らす処理にコルーチンを使っています。

No.384

定義は109~128行目です。

No.385

引数にfloat型変数を2つ持ち、その値を元にどのくらいの時間、どのくらいの大きさで揺らすのかを決めています。
今回は画面の点滅と時間を合わせて0.15秒揺らすようにします。
時間の計測にはvarで型指定をした変数を使っています。
今まで変数を宣言する際の型指定は「int」「float」のように明示していましたが、varの場合は暗黙的な型指定となり、代入される値によって型が決まります

No.386

while文()内の条件式がtrueの間{}の処理を繰り返します
これを使って経過時間の比較を行い、Random.Rangeにより-1から1の範囲で乱数を発生させて、magnitudeで指定した大きさを掛け合わせ、+-XY方向にカメラの位置を移動させることで揺れを実現しています。

No.387

経過時間はTime.deltaTime(直前のフレームからの経過時間)で取得するようにしているので、Update内で書く時同様、1フレームで1回取得するようにコルーチンを中断・再開させる必要があります。
その場合はyield return nullを使います。
while等の繰り返し処理の中にyield return nullを記述することにより、その行まできたら処理を中断して、次フレームから再開できます。
今回のようにwhile文{}内の最終行に書くことで、繰り返し処理が1フレームにつき1回実行されるようになりました。

No.388

カメラを揺らし終えた後、揺らす前の位置を記憶しておいた変数を代入し、Playerオブジェクトとの相対位置を元通りにしています。

63行目からは残りHPに応じた処理を行っています。
被弾1~4発目までは対応HPゲージを黒にしています。

No.389
No.390

被弾5発目はHPゲージを黒にする処理に加え、プレイヤーの入力を受け付けなくする為にスクリプトのOFF(trueでON)、移動停止、画面を赤くして死亡状態を演出しています。

No.391
No.392

最後に、このスクリプトをPlayerオブジェクトへアタッチし、各項目を以下の図のように設定します。

No.393

以上で[ 敵の攻撃 ]の説明を終わります、おつかれさまでした!

FPS

Posted by kenji