【C#】キャラクタが生きているか確認することから文法を学ぶ

最初、キャラクタがライフを5持っているとします
やられると1減るような様子をコードを使って自分の学習レベルを確認していきましょう

初学レベル(基本文法レベル)

このコードは、キャラクターの寿命を表現し、その寿命が0より大きいかどうかを確認するC#プログラムです。クラスを使用せず、寿命を変数として扱い、ループを使って寿命を減少させます。キャラクターが生きている場合には “true"、そうでない場合には “false" を表示します

using System;

class Program
{
    static void Main()
    {
        int life = 5; // キャラクターの寿命を5に設定

        for (int i = 0; i < 10; i++)
        {
            // キャラクターが生きているかどうかを表示
            Console.WriteLine("そのキャラクターは生きているのか? " + (life > 0)); 
            
            if (life > 0)
            {
                life--; // 寿命を1つ減少
            }
        }
    }
}

このコードは、クラスを使用せずにキャラクターの寿命を表現し、その寿命が0より大きいかどうかを確認します。クラスを使用しない場合、寿命 (life) を変数として扱い、条件文を使用して寿命を管理します。初学者にとっては、クラスを使用せずに基本的な条件文と変数を操作する方法を理解しやすいでしょう。

そのキャラクターは生きているのか? True
そのキャラクターは生きているのか? True
そのキャラクターは生きているのか? True
そのキャラクターは生きているのか? True
そのキャラクターは生きているのか? True
そのキャラクターは生きているのか? False
そのキャラクターは生きているのか? False
そのキャラクターは生きているのか? False
そのキャラクターは生きているのか? False
そのキャラクターは生きているのか? False

初級レベル(基本文法レベル)

初級者向けの難易度を少し上げたコードにしましょう。このコードは、条件文、ループ、関数(メソッド)の使用など、プログラミングの基本的な概念をさらに理解するのに役立つものです

using System;

class Program
{
    static void Main()
    {
        int characterLife = InitializeCharacterLife(); // キャラクターの寿命を初期化

        for (int i = 0; i < 10; i++)
        {
            Console.WriteLine("現在のキャラクターの寿命: " + characterLife);
            
            if (IsCharacterAlive(characterLife))
            {
                characterLife = DecreaseLife(characterLife); // 寿命を減少
            }
            else
            {
                Console.WriteLine("キャラクターは死んでいます!");
            }
        }
    }

    static int InitializeCharacterLife()
    {
        return 5; // キャラクターの初期寿命を設定
    }

    static bool IsCharacterAlive(int life)
    {
        return life > 0; // キャラクターが生きているかどうかを判定
    }

    static int DecreaseLife(int life)
    {
        return life - 1; // 寿命を1つ減少させる
    }
}

現在のキャラクターの寿命: 5
現在のキャラクターの寿命: 4
現在のキャラクターの寿命: 3
現在のキャラクターの寿命: 2
現在のキャラクターの寿命: 1
現在のキャラクターの寿命: 0
キャラクターは死んでいます!
現在のキャラクターの寿命: 0
キャラクターは死んでいます!
現在のキャラクターの寿命: 0
キャラクターは死んでいます!
現在のキャラクターの寿命: 0
キャラクターは死んでいます!
現在のキャラクターの寿命: 0
キャラクターは死んでいます!

このコードは、次の点を含んでいます:

  1. 関数(メソッド)を使用してコードを構造化し、可読性を高めます。キャラクターの寿命を初期化する InitializeCharacterLife メソッド、キャラクターが生きているかどうかを判定する IsCharacterAlive メソッド、寿命を減少させる DecreaseLife メソッドを使用します。
  2. 条件文を使用して、キャラクターの寿命が0より大きい場合に処理を実行し、寿命が0に達した場合にキャラクターが死んだことを表示します。

このコードは、関数を使用してコードを構造化し、より多くの制御構造を含んでおり、初級者がプログラミングの基本的なコンセプトを理解するのに役立つでしょう。

中級レベル(オブジェクト指向)

using System;

class Character
{
    private int life;

    public Character(int initialLife)
    {
        life = initialLife; // キャラクターの寿命を初期化
    }

    public bool IsAlive
    {
        get { return life > 0; } // キャラクターが生きているかどうかをプロパティで提供
    }

    public void DecreaseLife()
    {
        if (IsAlive)
        {
            life--; // 寿命を減少
        }
    }
}

class Program
{
    static void Main()
    {
        try
        {
            Character player = new Character(5); // キャラクターを作成

            for (int i = 0; i < 10; i++)
            {
                Console.WriteLine("現在のキャラクターの寿命: " + player.IsAlive);
                player.DecreaseLife();
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("エラーが発生しました: " + ex.Message);
        }
    }
}

現在のキャラクターの寿命: True
現在のキャラクターの寿命: True
現在のキャラクターの寿命: True
現在のキャラクターの寿命: True
現在のキャラクターの寿命: True
現在のキャラクターの寿命: false
現在のキャラクターの寿命: false
現在のキャラクターの寿命: false
現在のキャラクターの寿命: false
現在のキャラクターの寿命: false

このコードでは、中級レベルの概念を導入しています:

  1. Character クラスがオブジェクト指向プログラミング(OOP)の原則に従ってリファクタリングされ、コンストラクタ、プロパティ、エラーハンドリングが導入されています。
  2. Character クラスのコンストラクタを使用してキャラクターの初期寿命を設定できるようにし、初期値は5に設定されます。
  3. キャラクターが生きているかどうかを示すプロパティ IsAlive を導入し、プロパティを介してアクセスします。
  4. エラーハンドリングを追加し、例外が発生した場合にエラーメッセージを表示します。

このコードは、中級プログラマーにとって、オブジェクト指向プログラミングの基本概念やエラーハンドリングの実践を学ぶのに役立つでしょう。

キャラクターの寿命が 9 に変更されました
キャラクターの寿命が 8 に変更されました
キャラクターの寿命が 7 に変更されました
キャラクターの寿命が 6 に変更されました
キャラクターの寿命が 5 に変更されました
キャラクターの寿命が 4 に変更されました
キャラクターの寿命が 3 に変更されました
キャラクターの寿命が 2 に変更されました
キャラクターの寿命が 1 に変更されました
キャラクターの寿命が 0 に変更されました

上級レベル(オブジェクト指向)


using System;
using System.Linq;

// 寿命が変更されたときに通知するイベントハンドラを定義
public delegate void LifeChangedEventHandler(int newLife);

// キャラクターのインターフェースを定義
public interface ICharacter
{
    event LifeChangedEventHandler LifeChanged; // 寿命が変更されたときにイベントを発生
    int Life { get; } // 現在の寿命を取得
    bool IsAlive { get; } // キャラクターが生きているかどうかを取得
    void DecreaseLife(int amount); // 寿命を減少
}

class Character : ICharacter
{
    private int life;

    public event LifeChangedEventHandler LifeChanged; // 寿命変更のイベントを定義

    public Character(int initialLife)
    {
        life = initialLife; // キャラクターの寿命を初期化
    }

    public int Life
    {
        get { return life; } // 現在の寿命を取得
    }

    public bool IsAlive
    {
        get { return life > 0; } // キャラクターが生きているかどうかを取得
    }

    public void DecreaseLife(int amount)
    {
        if (IsAlive)
        {
            life -= amount; // 寿命を減少
            OnLifeChanged(); // 寿命変更イベントを発生
        }
    }

    protected virtual void OnLifeChanged()
    {
        LifeChanged?.Invoke(life); // 寿命変更イベントを発生
    }
}

class Program
{
    static void Main()
    {
        Character player = new Character(10); // キャラクターを作成

        // 寿命変更のイベントハンドラを登録
        player.LifeChanged += (newLife) => Console.WriteLine("キャラクターの寿命が " + newLife + " に変更されました");

        try
        {
            // LINQを使用してキャラクターの寿命を10回減少
            for (int i = 0; i < 10; i++)
            {
                player.DecreaseLife(1);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("エラーが発生しました: " + ex.Message);
        }
    }
}

この上級レベルのコードでは、次の概念とプラクティスを導入しています:

  1. インターフェース (ICharacter) の使用: インターフェースを使用して、キャラクタークラスを共通の方法で操作できるようにします。
  2. イベントの使用: キャラクターの寿命が変更されたときにイベントを発生し、外部でリッスンできるようにします。
  3. LINQ (Language-Integrated Query) の使用: LINQを使用してループを処理し、キャラクターの寿命を10回減少させます。
  4. エラーハンドリング: 例外処理を導入し、エラーが発生した場合にエラーメッセージを表示します。

このコードは、上級プログラマー向けに、イベント、インターフェース、LINQ、例外処理などの高度なプログラミング概念とプラクティスを学ぶのに役立つでしょう。

エキスパートレベル(オブジェクト指向)

using System;
using System.Threading;
using System.Threading.Tasks;

class Character
{
    private int life;

    public event EventHandler<int> LifeChanged; // 寿命が変更されたときにイベントを発生

    public Character(int initialLife)
    {
        life = initialLife; // キャラクターの寿命を初期化
    }

    public int Life
    {
        get { return life; } // 現在の寿命を取得
    }

    public bool IsAlive
    {
        get { return life > 0; } // キャラクターが生きているかどうかを取得
    }

    public async Task DecreaseLifeAsync(int amount)
    {
        await Task.Delay(1000); // 非同期処理をシミュレーション

        if (IsAlive)
        {
            life -= amount; // 寿命を減少
            OnLifeChanged(); // 寿命変更イベントを発生
        }
    }

    protected virtual void OnLifeChanged()
    {
        LifeChanged?.Invoke(this, life); // 寿命変更イベントを発生
    }
}

class Program
{
    static async Task Main()
    {
        Character player = new Character(10); // キャラクターを作成

        // 寿命変更のイベントハンドラを登録
        player.LifeChanged += (sender, newLife) => Console.WriteLine("キャラクターの寿命が " + newLife + " に変更されました");

        try
        {
            // 非同期処理を使用してキャラクターの寿命を10回減少
            for (int i = 0; i < 10; i++)
            {
                await player.DecreaseLifeAsync(1);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("エラーが発生しました: " + ex.Message);
        }
    }
}

このエキスパートレベルのコードでは、高度な概念と設計原則を導入しています:

  1. 非同期プログラミング: async および await キーワードを使用して非同期メソッドを実行し、非同期待ち時間をシミュレートします。
  2. イベントハンドリング: イベントハンドラを登録して、寿命変更イベントが発生したときに処理を実行します。
  3. ジェネリック: ジェネリック型 EventHandler<TEventArgs> を使用して、イベントハンドラに寿命を含む情報を渡します。
  4. 例外処理: エラーハンドリングを導入し、非同期メソッドで発生したエラーをキャッチしてエラーメッセージを表示します。

このコードは、エキスパートレベルのプログラマー向けに、非同期プログラミング、イベントハンドリング、ジェネリック、例外処理などの高度なプログラミング概念とプラクティスを学ぶのに役立つでしょう。

Unityで使えるように変更

using System;
using System.Collections;
using UnityEngine;

public class Character : MonoBehaviour
{
    public string characterName;
    public int initialLife = 10;

    private int life;
    public int Life => life;
    public bool IsAlive => life > 0;

    public event EventHandler<int> LifeChanged;

    private void Start()
    {
        life = initialLife;
        StartCoroutine(DecreaseLifeCoroutine());
    }

    private IEnumerator DecreaseLifeCoroutine()
    {
        while (IsAlive)
        {
            yield return new WaitForSeconds(1.0f); // 1秒ごとに寿命を減少させる

            life -= 2; // 2ずつ寿命を減少
            OnLifeChanged(Life);
        }
    }

    private void OnLifeChanged(int newLife)
    {
        LifeChanged?.Invoke(this, newLife);
    }
}

この Unity スクリプトでは以下のことを行っています:

  1. Character クラスを Unity の MonoBehaviour として宣言し、Unity のコンポーネントとして使用できるようにします。
  2. characterName と initialLife を Unity インスペクタで設定できるようにしました。
  3. Start メソッドで寿命を初期化し、DecreaseLifeCoroutine コルーチンを開始します。
  4. DecreaseLifeCoroutine コルーチン内で寿命を定期的に減少させ、時間ごとに寿命が減少するようにしています。
  5. 寿命が変更されるたびにイベントを発生させ、変更を監視する外部のコードがリアルタイムで寿命の変化を検出できるようにします。

このスクリプトを Unity のゲームオブジェクトにアタッチすることで、Unity ゲーム内でキャラクターの寿命を管理し、変更を監視できます。 Unity のエディタ内で characterName と initialLife を設定し、スクリプトはそれに従って動作します。

public event EventHandlerをUnityEventに置き換える

Unityでは、C#の EventHandler を代わりに UnityEvent を使用してイベント処理を実装できます。以下は、UnityEventを使用したUnityスクリプトの例です。

using System;
using UnityEngine;
using UnityEngine.Events;

public class Character : MonoBehaviour
{
    public string characterName;
    public int initialLife = 10;

    private int life;
    public int Life => life;
    public bool IsAlive => life > 0;

    [Serializable]
    public class LifeChangedEvent : UnityEvent<int> { }

    public LifeChangedEvent onLifeChanged = new LifeChangedEvent();

    private void Start()
    {
        life = initialLife;
        StartCoroutine(DecreaseLifeCoroutine());
    }

    private IEnumerator DecreaseLifeCoroutine()
    {
        while (IsAlive)
        {
            yield return new WaitForSeconds(1.0f); // 1秒ごとに寿命を減少させる

            life -= 2; // 2ずつ寿命を減少
            onLifeChanged.Invoke(Life);
        }
    }
}

このUnityスクリプトでは、以下の変更を行っています:

  1. UnityEvent を使用して onLifeChanged イベントを宣言し、LifeChangedEvent というカスタムのイベントクラスを作成し、イベントが int パラメータを伴うことを示します。
  2. onLifeChanged.Invoke(Life) を使用してイベントを発生させ、外部のコードでリアルタイムで寿命の変化を監視できるようにします。

Unityエディタ内で、onLifeChanged にリスナーを追加し、寿命が変化するたびにカスタムのアクションを実行できます。

各キャラクタのスクリプトを別々のゲームオブジェクトをアタッチして衝突すると相手のライフを減らすようにする

各キャラクターを別々のゲームオブジェクトとして作成し、衝突時に相手のライフを減少させるために、キャラクターごとに個別のスクリプトをアタッチする方法を説明します。以下はスクリプトの例です。

Character.cs (キャラクタースクリプト)

using System;
using UnityEngine;
using UnityEngine.Events;

public class Character : MonoBehaviour
{
    public string characterName;
    public int initialLife = 10;

    private int life;
    public int Life => life;
    public bool IsAlive => life > 0;

    [Serializable]
    public class LifeChangedEvent : UnityEvent<int> { }

    public LifeChangedEvent onLifeChanged = new LifeChangedEvent();

    private void Start()
    {
        life = initialLife;
    }

    public void DecreaseLife(int amount)
    {
        if (IsAlive)
        {
            life -= amount;
            onLifeChanged.Invoke(Life);
        }
    }
}

CollisionHandler.cs (衝突処理用スクリプト)

using UnityEngine;

public class CollisionHandler : MonoBehaviour
{
    private void OnCollisionEnter(Collision collision)
    {
        Character otherCharacter = collision.gameObject.GetComponent<Character>();

        if (otherCharacter != null)
        {
            // 衝突したキャラクターから1ポイントのライフを減らす
            otherCharacter.DecreaseLife(1);
        }
    }
}
  1. Character クラスは以前に説明した通りです。それを別々のゲームオブジェクトにアタッチします。これにより、各キャラクターが異なる初期ライフを持つことができます。
  2. CollisionHandler スクリプトは、衝突した相手の Character コンポーネントを検出し、その DecreaseLife メソッドを呼び出してライフを減少させます。

各キャラクターのゲームオブジェクトには、Character コンポーネントと CollisionHandler コンポーネントの両方をアタッチします。これにより、衝突した場合に他のキャラクターのライフが減少するようになります。

この設定により、Unity ゲーム内で異なるキャラクター同士の衝突を処理し、相手のライフを減少させることができます。

UnityEvent

UnityEventは、Unityエディタ内でイベントリスナーを簡単に設定できる強力なツールで、以下のメリットがあります:

  1. エディタ内での設定: UnityEventは、Unityのエディタ内でイベントを設定できます。これにより、ゲームオブジェクト間のイベントハンドリングをコードを書かずに設定できます。非プログラマーのデザイナーやアーティストがイベント処理を設定できるため、コードの修正が不要です。
  2. シリアライズ可能: UnityEventはシリアライズ可能なデータ型で、プレハブやアセットとして保存できます。これにより、ゲームオブジェクトのコンポーネント間でイベントを簡単に再利用できます。
  3. 複数のリスナー: UnityEventは複数のリスナー(イベントハンドラ)をサポートします。つまり、1つのイベントに複数のアクションを関連付けることができます。これは、さまざまな場面でのイベント処理の柔軟性を提供します。
  4. クロスプラットフォーム: UnityEventは異なるプラットフォームやビルドにおいても動作します。これは、ゲームのポータビリティに役立ちます。
  5. 可視化: UnityEventを使うと、エディタ内でイベントリスナーの関連付けを直感的に行うことができます。これにより、開発者とデザイナーが協力してゲームのイベント処理を調整できます。

UnityEventは、ゲーム開発のプロセスを効率化し、コードの可読性を向上させるための優れたツールです。ただし、UnityEventはデザイナー向けに設計されたものであり、複雑な条件やロジックを処理するには限界がある場合もあります。特に高度なプログラムロジックや動的なイベント処理が必要な場合、コードでのカスタムイベント処理が依然として有用です