ゲームエンジンⅡ②(2025/10/09)

InputActionAsset
⇒PlayerInputComponent
 ⇒GameObject

🧠 デリゲートとは?

**デリゲート(delegate)**は、簡単に言うと「関数を変数として扱える仕組み」です。

  • 通常、変数には数値や文字列を入れますよね?
  • デリゲート変数には「関数(メソッド)」を入れることができます。

つまり、**「この変数を呼び出すと、登録された関数が実行される」**というような使い方ができます。

🔧 例(C#の場合)

// デリゲートの定義
delegate void MessageDelegate(string message);

// 関数の定義
void Hello(string msg) {
    Console.WriteLine("Hello: " + msg);
}

// デリゲート変数に関数を代入
MessageDelegate del = Hello;

// 呼び出し
del("World"); // → Hello: World

この例では、MessageDelegate という型の変数 delHello 関数を代入しています。del("World") と呼び出すと、Hello("World") が実行されるわけです。

🧩 どんなときに使うの?

  • イベント処理(ボタンを押したときの動作など)
  • コールバック関数(処理が終わった後に呼ばれる関数)
  • 柔軟な設計(関数を引数として渡したり、動的に切り替えたり)

🌀 他の言語では?

  • Pythonでは「関数は第一級オブジェクト」なので、普通に変数に代入できます。
  • Javaでは「関数型インターフェース」や「ラムダ式」で似たようなことができます。

ゆきさんの関心領域(C言語や抽象化)に照らすと、デリゲートは「関数ポインタの高級版」とも言えます。C言語の void (*func)(int) のような関数ポインタを、より安全かつ柔軟に扱えるようにしたものがデリゲートです。(by Copilot)


y軸の移動も組み込む。

using Unity.Collections;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.InputSystem;

public class Player : MonoBehaviour
{
    [Header("---Base Parameter")]
    public float moveSpeed = 5.0f;

    [Header("---Bullet Settings---")]
    public GameObject bulletPrefab;
    public float shotPower = 10f;

    [Header("=== Input Setting")]
    public InputActionReference moveInput;
    public InputActionReference fireInput;
    public PlayerInput playerInput;

    private Vector2 _moveInputValue;
    private float _fireInputValue;
    [HideInInspector] public Rigidbody2D bulletRb; // Rigidbodyの参照

    void Start()
    {
        moveInput.action.performed += MoveInputActionPerformed;
        moveInput.action.canceled += MoveInputActionCanceled;
    }

    private void MoveInputActionPerformed(InputAction.CallbackContext context)
    {
        _moveInputValue = context.ReadValue<Vector2>();
    }

    private void MoveInputActionCanceled(InputAction.CallbackContext context)
    {
        _moveInputValue = Vector2.zero;
    }
    void Update()
    {
        transform.Translate(_moveInputValue.x * moveSpeed * Time.deltaTime, _moveInputValue.y * moveSpeed *Time.deltaTime, 0);
        /*
        if (Input.GetKey(KeyCode.LeftArrow) | Input.GetKey(KeyCode.A))
        {
            transform.Translate(-moveSpeed * Time.deltaTime, 0, 0);
        }
        if (Input.GetKey(KeyCode.RightArrow)|Input.GetKey(KeyCode.D))
        {
            transform.Translate(moveSpeed*Time.deltaTime, 0, 0);
        }

        if (Input.GetKeyDown(KeyCode.Space))
            {
                GameObject bullet = Instantiate(bulletPrefab, this.transform.position, Quaternion.identity);
                bulletRb = bullet.GetComponent<Rigidbody2D>(); // Rigidbody2Dを取得してbulletRbに代入

                //AddForce(飛ばす方向と力、飛ばすときのモード指定)
                bulletRb.AddForce(new Vector2(0, 1) * shotPower, ForceMode2D.Impulse); // Rigidbody2Dに力を加える
            }
        */
    }
}

弾の射出を組み込む。

using System;
using System.Security.Cryptography;
using Unity.Collections;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.UI;

public class Player : MonoBehaviour
{
    [Header("---Base Parameter")]
    public float moveSpeed = 5.0f;

    [Header("---Bullet Settings---")]
    public GameObject bulletPrefab;
    public float shotPower = 10f;
    public float shotInterval = 0.5f; //発射間隔
    private float _shotIntervalTimer = 0f; //発射間隔タイマー

    [Header("=== Input Setting")]
    public InputActionReference moveInput;
    public InputActionReference fireInput;
    public PlayerInput playerInput;

    private Vector2 _moveInputValue;
    private float _fireInputValue;
    [HideInInspector] public Rigidbody2D bulletRb; // Rigidbodyの参照

    void Start()
    {
        moveInput.action.performed += MoveInputActionPerformed;
        moveInput.action.canceled += MoveInputActionCanceled;
        fireInput.action.performed += FireInputActionPerformed;
        fireInput.action.canceled += FireInputActionCanceled;
    }

    private void MoveInputActionPerformed(InputAction.CallbackContext context)
    {
        _moveInputValue = context.ReadValue<Vector2>();
    }

    private void MoveInputActionCanceled(InputAction.CallbackContext context)
    {
        _moveInputValue = Vector2.zero;
    }

    private void FireInputActionPerformed(InputAction.CallbackContext context)
    {
        _fireInputValue = context.ReadValue<float>();

    }

    private void FireInputActionCanceled(InputAction.CallbackContext context)
    {
        _fireInputValue = 0f;
    }
    private void ShootBullet()
    {
        GameObject bullet = Instantiate(bulletPrefab, transform.position, Quaternion.identity);
        bulletRb = bullet.GetComponent<Rigidbody2D>();
        bulletRb.AddForce(Vector2.up * shotPower, ForceMode2D.Impulse);
        Destroy(bullet, 3f); // 3秒後に自動で破棄

    }

    void Update()
    {
        transform.Translate(_moveInputValue.x * moveSpeed * Time.deltaTime, _moveInputValue.y * moveSpeed * Time.deltaTime, 0);


        /*
        if (Input.GetKey(KeyCode.LeftArrow) | Input.GetKey(KeyCode.A))
        {
            transform.Translate(-moveSpeed * Time.deltaTime, 0, 0);
        }
        if (Input.GetKey(KeyCode.RightArrow)|Input.GetKey(KeyCode.D))
        {
            transform.Translate(moveSpeed*Time.deltaTime, 0, 0);
        }

        if (Input.GetKeyDown(KeyCode.Space))
            {
                GameObject bullet = Instantiate(bulletPrefab, this.transform.position, Quaternion.identity);
                bulletRb = bullet.GetComponent<Rigidbody2D>(); // Rigidbody2Dを取得してbulletRbに代入

                //AddForce(飛ばす方向と力、飛ばすときのモード指定)
                bulletRb.AddForce(new Vector2(0, 1) * shotPower, ForceMode2D.Impulse); // Rigidbody2Dに力を加える
            }
        */

        _shotIntervalTimer += Time.deltaTime;
        if (_fireInputValue >= 0.5f)
        {
            //発射間隔を過ぎていたら弾を発射
            if (_shotIntervalTimer >= shotInterval)
            {
                ShootBullet();
                //発射間隔タイマーリセット
                _shotIntervalTimer = 0f;
            }
        }
    }
}

[IsTrigger]
Colliderを使っていると、通常、接触した場合に物理エンジンの挙動に沿って「弾き飛ばされる」動きになる。
Colliderに「IsTrigger」をつけると、接触しても物理挙動をせずに「判定」だけとれるようになる。
※ただし
OnCollisionEnter2DとかOnCollisionStay2Dとか、Collision系のメソッドが効かなくなる。
代わりにOnTriggerEnter2Dなどを使うことになる。

[Tag]
if (collision.transform.tag == “Bulleet”)
⇒”Bullet”とタグが完全一致した場合のみ引っかかる。
if(collision.transform.tag.Contains(“Bullet”))
⇒”Bullet”が含まれるタグを判別する。”EnemyBullet”にも引っかかる。

BulletPrefabにTagを設定する。

このとき、Bullet/Normalなどとすると、Bullet内でTagを細かく分類することができる。

using UnityEngine;

public class Enemy : MonoBehaviour
{
    // Start is called once before the first execution of Update after the MonoBehaviour is created
    void Start()
    {

    }

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

    }

    //Collider同士が接触した”瞬間(フレーム)”に実行される関数
    /*void OnCollisionEnter2D(Collision2D collision)
    {
        //OnCollisionEnter2D内の引数であるcollisionには、衝突したオブジェクトの情報(Collision2D)が入る
        Debug.Log($"HIT!...{collision.transform.name}");
        Destroy(gameObject);//接触で削除される
        Destroy(collision.gameObject);//接触した相手のオブジェクトも削除する
    }
    */

    private void OnTriggerEnter2D(Collider2D collision)
    {
        if(collision.transform.tag.Contains("Bullet"))
        {
            Destroy(gameObject);
        }
    }
}

弾が画面外に出たら破棄されるスクリプトを追加。
Destroyも、Enemy.csから移動する。BulletPrefabにアタッチ。
(Bullet.cs)

using UnityEngine;

public class Bullet : MonoBehaviour
{
    void Update()
    {
        if (Mathf.Abs(transform.position.y) > 10f) // 画面外に出たら
        {
            Destroy(gameObject);
        }
    }
    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.CompareTag("Enemy"))
        {
            Destroy(collision.gameObject); // 敵を破壊
            Destroy(gameObject); // 弾も消える
        }
    }

}

[継承]
親から子に、変数とメソッドの機能を引き継がせる機能。
この機能を使って、敵の種類を増やしていく。

EnemyPrefab一つだったものをEnemy0PrefabとEnemy1Prefabに拡張。
BossEnemy.csを作って、Enemy1にアタッチ。

Enemy.cs、BossEnemy.csは、「子」に当たる。親に当たるクラス「BaseEnemy.cs」を作成する。
「子」の側のクラス名の後に「BaseEnemy」を書くだけ。
これで「親」側で作ったメソッドや変数等を、「子」の側でそのまま使うことができる。

public class BaseEnemy : MonoBehaviour⇒ここが「親」を書く部分
public class Enemy : BaseEnemy
public class BossEnemy : BaseEnemy

なお、EnemyとBossEnemyからはMonoBehaviourが消えるが、BaseEnemyの親はMonoBehaviourになるので、「親」経由で継承されることになる。

コメント

タイトルとURLをコピーしました