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

2020年8月3日

敵の攻撃を実装していきます。
まず、敵の座標を以下のように変更します。

No.332
No.333

敵はゲーム開始時にプレイヤ―の方を向いていて、後方移動をして壁伝いに反時計回りを繰り返して攻撃をしてくる設定にします。

No.334

移動の部分は前回で実装済みなので、今回は「弾の発射 → 軌道 → 衝突」の流れでいきます。

まずは弾の発射地点となる銃口を用意します。
Hierarchyウィンドウから「Create → Create Empty」でGameObjectを作成します。

No.335

GameObjectを「EnemyMuzzle」にリネームして、Enemyの子オブジェクトにします。

No.336

EnemyMuzzleのTransformをリセットし、Position Z を「1」にします。

No.337
No.338

これで、Enemyオブジェクトの原点からEnemyオブジェクト1個分前方が弾の発射地点になりました。

No.339

次に弾を作ります。
弾はゲーム中に何度も複製して発射します。このような、同じモノを何度も複製する時にはPrefab(プレハブ)という機能を使っていきます。
ProjectウィンドウのAssetsフォルダ内で「Prefabs」と名前を付けてフォルダを作ります。

No.340

次にHierarchyウィンドウでSphereオブジェクトを作り、「EnemyBullet」にリネームしたものをPrefabsフォルダにドラッグ&ドロップします。

No.341
No.342

アイコンの色が青く変わり、EnemyBulletのPrefabがPrafabsフォルダにできました。
これを基にEnemyBulletを複製します。
HierarchyウィンドウにあるEnemyBulletは不要なのでDeleteしておきましょう。

No.343

複製なら「Ctrl + D」でもできますが、Prefabで複製したものは複製元の変更が複製先にも適用されます。
例えば、同じ敵を100体配置した後に、「オブジェクトのサイズを変えたい」「コライダーの位置を変えたい」「マテリアルを変えたい」と思った場合、「Ctrl + D」で複製したものは100体分を個別に変更することになります。一方、Prefabで複製したものは複製元のオブジェクト1体を変更するだけで、残り99体に同じ変更が適用されます。

No.344

EnemyBulletに色を付けていきます。
ProjectウィンドウのMaterialsフォルダを選択 → Create → Material で作成したファイルを「EnemyBulletPattern」にリネームします。

No.345

Albedoを選択し、 Colorの数値を下図のようにします。

No.346

EnemyBulletを選択してコンポーネントを以下のように変更してください。

No.347

「EnemyBullet」タグを新たに作り、指定します。

No.348

色と大きさを指定した弾が出来上がりました。次に、スクリプトを使って発射口から発射します。
スクリプト「EnemyShooting」を作り、以下のコードをコピペしてください。

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

public class EnemyShooting : MonoBehaviour
{
    [SerializeField] GameObject enemyBulletPrefab;
    [SerializeField] float shotInterval;
    float timeCount;

    // Start is called before the first frame update
    void Start()
    {

    }

    // Update is called once per frame
    void Update()
    {
        //時間計測
        timeCount += Time.deltaTime;
        //指定の秒数以上になった場合
        if (timeCount >= shotInterval) 
        {
            //カウントをリセット
            timeCount = 0;
            //弾を生成
            Instantiate(enemyBulletPrefab, transform.position, Quaternion.identity);
        }
    }
}

このスクリプトをEnemyMuzzleオブジェクトにアタッチして、各項目を以下のように指定します。

No.349

実行してみると、弾の発射が確認できます。

No.350

スクリプトの内容を見ていきます。
7~9行目で、Inspectorから変更する変数には[SerializeField]を付けて宣言しています。
それぞれPrefab用、発射間隔用、時間計測用に使います。

No.351

21行目で、フレーム毎の経過時間をTime.deltaTimeで取得し、timeCountに足し合わせています。

No.352

23~29行目で、timeCountがshotIntervalで指定した値以上になった場合、値を「0」に戻し、EnemyMuzzleの原点へ弾を生成しています。

No.353

現状では、発射された弾がその場で停止しているので、プレイヤーの方へ向かっていくようにします。
スクリプト「PlayerTracking」を作り、以下のコードをコピペしてください。

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

public class PlayerTracking : MonoBehaviour
{
    GameObject Player;
    [SerializeField] float Speed;

    // Use this for initialization
    void Start()
    {
        Player = GameObject.Find("Player");
    }

    // Update is called once per frame
    void Update()
    {
        transform.position = Vector3.MoveTowards(transform.position, Player.transform.position, Speed * Time.deltaTime);
    }

    //衝突判定
    void OnTriggerEnter(Collider other)
    {
        //衝突したオブジェクトのタグがPlayerだった場合
        if (other.tag == "Player")
        {
            //このスクリプトがアタッチされているオブジェクト(自分自身)を消す。
            Destroy(gameObject);
        }
    }
}

PrefabsフォルダのEnemyBulletにこのスクリプトをアタッチして、Speedの値を「500」にします。

No.354

実行すると、プレイヤーめがけて弾が移動するようになりました。

No.355

スクリプトの内容を見ていきます。
7~20行目は今までに触れてきたものと同様の処理です。

No.356

Inspectorで値を調整できるよう[SerializeField]で変数を宣言し、Playerを追跡する為にPlayerオブジェクトを取得します。その際、GameObject.Find関数はHierarchy上のオブジェクトを全走査する重い処理なので、Start関数の中で変数に保存しておくようにします。
そして、現在位置からPlayerの位置までSpeed変数で指定した値にフレーム数の差異による違いが出ないようTime.deltaTimeを掛けてMoveTowards関数で移動するという流れになっています。

23~31行目では衝突判定を行っています。

No.357

OnTriggerトリガーオブジェクトの侵入時に呼ばれます
発生させるには衝突する両方のオブジェクトにColliderがアタッチされている必要があります。
そして、衝突するオブジェクトのどちらか一方のIsTriggerにチェックが付いている必要があります。
さらに、衝突するオブジェクトのどちらか一方にRigidbodyがアタッチされている必要があります。

No.358

今回は、CubeやSphereなどの基本オブジェクト(「プリミティブ」と呼ばれる)には最初からColliderがアタッチされており、IsTriggerのチェックはEnemyBulletに、RigidbodyはPlayerにアタッチされている為、侵入開始時にOnTriggerEnterが正常に呼び出されています。
衝突した相手オブジェクトの情報(名前やタグ、transform等)はCollider型変数に格納されます。
その中のタグ情報を使ってPlayerかどうかを判定し、trueの場合は自分自身(=弾)をDestroy関数で削除しています。

No.359

長くなってきたので続きは次回にしたいと思います。おつかれさまでした!

FPS

Posted by kenji