インゲームにおけるゲームAI復習(随時更新)

## ゲームAIの理解
今記事は、【年末だよ】Unity お・と・なのLT大会 2023の発表内容のブレスト用記事として執筆しています。
https://meetup.unity3d.jp/jp/events/1409
発表後も、随時更新すると思います。

### 近年のAIについて
近年、AIに対する理解が抽象的になっており、ゲームにおけるAIとの明確な分離の上での理解が必要になってきた。
ここ30年ぐらいを遡っても、機械学習、深層学習、人工汎用知能(AGI)など、多岐に渡った技術、アルゴリズムが生まれてきました。
その中で、AIとして機能するもの、学習モデルを作った上で機能するもの、様々な様式が生まれてきました。

本記事は筆者が開発中のゲームの内容を抽象化すると共に、元となる論文を引用せず、自身の理解と文脈をまとめる、復習のための記事となります。
壁打ちの相手として、ChatGPT4を用いて記事を書いています。
(随時更新していきます)

### 今回フォーカスするAI
現在、自身で開発しているシステムにおけるAIで、

 - PG(Procedural Generation)
 - NavMesh
 - FSM(Finit State Macine)
 - Decision Trees
 - Behavior Trees
 - AI Planner
 - ML-Agents

といったいくつかのAIシステムが混在しています。
その中から、Decision Trees、Behavior Trees、AI Plannerの概念を、文脈立てて復習していきたいと思います。

### 何をするAIなのか？
まず、それぞれのシステムの概念を、

Decision Trees：戦術を元に判断するAI
Behavior Trees：戦略を元に判断するAI
AI Planner：シナリオを元に判断するAI

として、定義したいと思います。

仮にNCPに実装するAIを例に解説すると、
**Decision Trees**→目の前に現れたプレイヤーに対してどのような振る舞い、ステータスに切り替えるか？を判断
**Behavior Trees**→プレイヤーが目指している目標に対してどうするべきかを判断する
**AI Planner**→最終的にプレイヤーに対して何が起きればベストかを基準に判断する

といった、このような位置づけで進めていきたいと思います。


## 各AIシステムの概念＆役割
### NavMeshの役割
NavMesh（ナビゲーションメッシュ）は、Unityなどの3Dゲームエンジンにおいて、ゲーム内のキャラクター（特にNPC）が移動するための仮想的な地図です。以下にその主な特徴と使用例を簡潔に説明します。

### 主な特徴
1. **ナビゲーション用地形マップ：** NavMeshは、キャラクターが移動可能な領域と移動不可能な領域（障害物など）を区別するための3D地図です。
2. **自動経路計算：** キャラクター（NPC）はNavMeshを使って最短かつ最適な経路を自動的に計算し、障害物を回避しながら目的地まで移動します。
3. **柔軟なカスタマイズ：** 開発者はNavMeshをカスタマイズし、異なる地形や環境に合わせて移動パターンを調整できます。

### 使用例
- **NPCの徘徊：** ゲーム内でNPCがランダムに、または特定のパターンでエリア内を移動する場合にNavMeshを使用します。
- **敵キャラクターの追跡：** 敵AIがプレイヤーを追跡する際に、NavMeshを用いて効率的な経路を見つけます。
- **動的な環境対応：** ゲーム内で環境が変化した場合（例えば、扉が開く、橋が崩れるなど）、NavMeshを再計算して新しい移動経路を生成します。
### Decision Treesの役割

Decision Treesは、AIの意思決定プロセスにおいて使用されるツールです。
これらは、特に単純な条件に基づいて迅速な決定を必要とするシナリオにおいて有用です。

#### 主な特徴

1. **条件に基づく意思決定**:
   - Decision Treesは、一連の条件を評価し、それに基づいて最適な行動を決定します。
   - 通常、これらの条件は単純なはい/いいえの形式で、順番に評価されます。

2. **静的な意思決定プロセス**:
   - 一度決定されたパスは、環境が変化しても変わることはありません。これは比較的静的な意思決定プロセスを意味します。

3. **単純な構造**:
   - Decision Treesは、その構造が単純で理解しやすいため、特定のシナリオでの迅速な意思決定に適しています。

#### 使用例

- ゲーム内のNPCが特定の状況で迅速に決定を下す必要がある場合。
- 例えば、敵AIがプレイヤーを発見したかどうかに基づいて攻撃するか逃げるかを決定する場合。

Decision Treesは、特定の問題に対して具体的な解答を提供するのに適しています。
このツールを使用することで、開発者はAIエージェントに、簡単かつ明確な条件に基づいて行動をとらせることができます。これにより、AIの行動はより予測可能で一貫性があり、特定のゲームシナリオや状況に適したものになります。
Decision Treesは、特に単純なゲーム環境や限られた行動選択が必要な場合に有用です。


### Behavior Treesの役割

Behavior Treesは、ゲームAIの行動と意思決定プロセスをモデル化するために使用されるシステムです。これらは、特に動的なゲーム環境や複数の行動が必要なシナリオにおいて、AIエージェントの行動を制御するのに適しています。

#### 主な特徴

1. **階層的な構造**:
   - Behavior Treesは、階層的なノードの構造を持ちます。各ノードは特定の行動や条件を表します。

2. **動的な意思決定プロセス**:
   - ゲーム環境や他のエージェントの行動に応じて、行動木はリアルタイムで更新されます。
   - これにより、AIエージェントは状況に応じて行動を変更できます。

3. **柔軟な行動パターンの実装**:
   - Behavior Treesを使用することで、AIエージェントはさまざまな行動パターンや戦略を採用できます。

4. **条件に基づく行動の選択**:
   - ゲームの特定の条件に基づいて、最適な行動を選択します。

#### 使用例

- 敵AIがプレイヤーの行動や環境の変化に応じて異なる戦術を取る場合。
- ゲーム内でNPCがプレイヤーとの対話やイベントに応じて異なる行動をする場合。

Behavior Treesは、AIエージェントがリアルタイムで環境に適応し、多様な行動を取ることを可能にします。
これにより、ゲームのAIはより現実的で予測不可能な振る舞いを示し、プレイヤーに対してより豊かなゲーム体験を提供することができます。
開発者は、Behavior Treesを使用して、AIの行動を細かく制御し、ゲームの特定のシナリオや状況に適応させることができます。


### AI Plannerの役割

AI Plannerは、特に複雑なゲーム環境や戦略ゲームにおいてAIエージェントの行動計画を立てるために使用されるシステムです。このシステムは、エージェントが環境内で直面する様々な状況や目標に対応するための計画を策定します。

#### 主な特徴

1. **目標指向の計画**:
   - AIエージェントが達成すべき特定の目標やタスクを定義します。
   - これらの目標は、エージェントの最終的な行動や決定に影響を与えます。

2. **複雑な意思決定のサポート**:
   - さまざまな選択肢や戦略の中から最適な行動計画を立てるための高度な意思決定プロセスを提供します。

3. **動的な環境への適応**:
   - ゲームの状況や他のエージェントの行動が変化すると、計画はそれに合わせて調整されます。
   - これにより、エージェントは変化する状況に柔軟に対応できます。

4. **長期的な戦略の立案**:
   - AI Plannerは、短期的な反応だけでなく、長期的な目標達成のための計画を立てるのにも適しています。

#### 使用例

- 戦略ゲームでの敵AIの行動計画、たとえば軍隊の配置や資源管理の戦略。
- ロールプレイングゲーム（RPG）でのNPCの行動パターンやクエストの遂行計画。

AI Plannerは、AIエージェントがより複雑で戦略的な行動をとるための計画を立てる際に非常に有用です。開発者は、ゲームの特定のシナリオや戦略に適したAIの行動計画を立てるためにAI Plannerを使用します。これにより、ゲーム体験はよりリアルで没入感のあるものとなります。

## NavMeshを使用したNPCクラスを作成

### はじめに

AI（人工知能）とNPC（ノン・プレイヤーキャラクター）は、ゲーム開発において非常に重要な要素です。これらの概念と、ゲーム開発におけるAIの役割、そしてNPCがプレイヤー体験に与える影響について説明します。

### AI（人工知能）の基本概念
- **AIの定義:** AIは、人間のような知性を模倣し、学習し、問題を解決する能力を持つコンピュータシステムです。
- **ゲームにおけるAIの役割:** ゲーム内でAIは、キャラクターの行動を制御し、ゲーム環境に対するリアルタイムの反応を生成します。これには敵キャラクターの戦略、NPCの行動パターン、ゲームの難易度調整などが含まれます。

### NPC（ノン・プレイヤーキャラクター）の基本概念
- **NPCの定義:** NPCは、プレイヤーではないがゲーム内で重要な役割を果たすキャラクターです。これらは通常、ゲームのストーリーや世界を豊かにするためにAIによって制御されます。
- **NPCの役割:** NPCはゲーム内で多様な役割を持ちます。例えば、ストーリーを進行させるキャラクター、情報を提供するキャラクター、プレイヤーと交戦する敵キャラクターなどがいます。

### ゲーム開発におけるAIの役割
- **没入感の向上:** AIは、ゲーム内のキャラクターにより自然でリアリスティックな行動をもたらし、没入感を高めます。
- **ゲームプレイの多様性:** AIによってキャラクターの行動が変化し、プレイヤーは毎回異なる体験ができます。
- **難易度のバランス:** AIはゲームの難易度を調整し、プレイヤーに適切な挑戦を提供することができます。

### NPCがプレイヤー体験に与える影響
- **ストーリーテリング:** NPCはゲームのストーリーを伝える重要な手段です。彼らはプレイヤーにクエストを提供したり、バックストーリーを語ることで、ゲームの世界をより深く理解させます。
- **感情的な絆:** よく設計されたNPCは、プレイヤーとの感情的なつながりを生み出すことができます。これにより、ゲーム体験がより記憶に残るものになります。
- **対話とインタラクション:** NPCとの対話やインタラクションは、プレイヤーがゲームの世界に参加し、影響を与える方法を提供します。

これらの要素は、ゲームがプレイヤーに提供する体験の質を大きく決定します。AIとNPCは、ゲームデザインとプレイヤーの体験を形作る上で不可欠な要素です。

### NavMeshの基本概念
- **定義:** NavMeshは、ゲームの3D空間内でキャラクターが安全に移動できる領域を定義するためのメッシュ（ネットワーク）です。
- **目的:** NPCが障害物を避けながら効率的に目的地に移動できるようにすることが主な目的です。
- **機能:** NavMeshは移動可能な地形を分析し、障害物や不可能な領域を識別します。これにより、キャラクターはリアルタイムで最適な経路を見つけることができます。

### 経路探索のAI
- **経路探索:** 経路探索のAIは、NavMeshを使用して、現在地から目的地までの最適な経路を計算します。
- **動的計算:** このシステムは動的に経路を計算し、環境の変化（例えば、新しい障害物の出現）に応じて経路を調整します。
- **効率的な移動:** AIは、最短距離、最小時間、またはその他の条件に基づいて、最適な経路を選択します。

### AIの設計原則とNPCの設計思想
1. **モジュラリティ:** AIの各部分は独立していて、異なるシナリオで再利用可能です。このソースコードでは、`WanderNPC`クラスは徘徊行動に特化しています。
2. **状態の管理:** AIは異なる状態（例えば、徘徊、一時停止）を持ち、これらの状態は状況に応じて変化します。ソースコードでは、`isPaused`フラグを使用してNPCの動きを制御しています。
3. **拡張性:** コードは将来的な拡張（例えば、追跡や他の行動パターンの追加）を容易にするように設計されています。

### 徘徊行動の設計と実装
- **基本概念:** 徘徊は、NPCがランダムな位置に移動する行動です。これにより、NPCは環境内を自然に動き回るように見えます。
- **実装:** 
  - `SetRandomDestination`メソッドでは、NPCの現在位置からランダムな方向にある目的地を決定し、`NavMeshAgent`を使用してその地点に移動を指示します。
  - `Update`メソッド内で、NPCが目的地に近づいたら新しい目的地を設定します。これにより、NPCは継続的に徘徊します。

### 追跡行動の設計（追加可能な概念）
- **基本概念:** 追跡行動では、NPCは特定のターゲット（例えばプレイヤー）を追いかけます。
- **実装の提案:** 
  - `Update`メソッドに追跡ロジックを追加し、NPCがターゲットに近づいた場合にターゲットの位置に向かって移動するようにします。
  - `NavMeshAgent`の`SetDestination`メソッドを使用して、ターゲットの現在位置をNPCの新しい目的地として設定します。

### `NavMeshAgent` の役割
- **定義:** `NavMeshAgent`はUnityのナビゲーションシステムのコアコンポーネントで、ゲームオブジェクト（この場合はNPC）に対して自動的な経路探索と移動機能を提供します。
- **主な機能:**
  - **経路計算:** `NavMeshAgent`はNavMesh上での最適な経路を計算し、キャラクターを目的地まで導きます。
  - **障害物回避:** 障害物や他のエージェントを避けながら移動する能力を持ちます。
  - **移動速度と加速:** 移動速度、回転速度、加速度などのパラメータを設定できます。

### `wanderRadius` の役割
- **定義:** `wanderRadius`はカスタムプロパティで、NPCが徘徊する際のランダムな目的地を決定するための範囲を表します。
- **使用方法:**
  - NPCが徘徊するときにどれだけ広い範囲をカバーするかを制御します。
  - 大きい`wanderRadius`はNPCが広範囲を徘徊することを意味し、小さい値はより限定されたエリア内での移動を意味します。

### `NavMeshAgent`の設定方法
1. **コンポーネントの追加:** Unityエディタで、NPCに対して`NavMeshAgent`コンポーネントを追加します。
2. **パラメータの調整:** エディタ内で、移動速度、加速度、回転速度などのパラメータを調整できます。
3. **スクリプトでの使用:** スクリプト内で`GetComponent<NavMeshAgent>()`を呼び出すことで、このコンポーネントにアクセスし、プログラム的に設定を変更できます。

### `wanderRadius`の設定方法
- スクリプト内で、`wanderRadius`は直接値を設定するか、あるいはインスペクタから調整可能な`SerializeField`として定義されます。
- `SetRandomDestination`メソッド内で、`wanderRadius`はNPCの新しいランダムな目的地を決定する際に使用されます。

### `Awake` メソッド
- **機能:** Unityのライフサイクルにおいて、オブジェクトが最初にアクティブになった時に一度だけ呼び出されます。このメソッドは初期化のために使用されます。
- **コード内での役割:** 
  - `NavMeshAgent`コンポーネントと`Renderer`コンポーネントをそれぞれ`agent`変数と`npcRenderer`変数に割り当てます。
  - 最初のランダムな目的地を設定するために`SetRandomDestination`メソッドを呼び出します。
  - NPCが"NPC"タグを持っていない場合、それを割り当てます。

### `SetRandomDestination` メソッド
- **機能:** NPCに新しいランダムな目的地を割り当てるためのメソッドです。
- **実装の詳細:**
  - `Random.insideUnitSphere`を使用して、NPCの現在位置を基点とするランダムな方向を計算します。この方向は`wanderRadius`で定義された範囲内に制限されます。
  - `NavMesh.SamplePosition`を使用して、計算されたランダムな方向に移動可能な地点（NavMesh上の地点）を見つけます。
  - 移動可能な地点が見つかった場合、`NavMeshAgent`の`SetDestination`メソッドを呼び出して、NPCの新しい目的地を設定します。

### `Update` メソッド
- **機能:** オブジェクトがアクティブである間、毎フレーム呼び出されるメソッドです。
- **コード内での役割:** 
  - NPCが目的地に近づいた場合（`agent.remainingDistance < 0.5f`）、新しい目的地を設定するために`SetRandomDestination`を再度呼び出します。
  - `isPaused`変数の状態に基づいてNPCの動きを一時停止または再開します。

### NPCの目的地選択と移動プロセス
1. **初期化:** ゲーム開始時、`Awake`メソッドがNPCを初期化し、最初のランダムな目的地を設定します。
2. **ランダムな目的地の選択:** `SetRandomDestination`メソッドは、NPCの現在位置周辺でランダムな目的地を選択します。
3. **移動:** `NavMeshAgent`は自動的にNPCを新しい目的地へと導きます。この過程で障害物を避けるなどのナビゲーションが行われます。
4. **目的地到達と再選択:** NPCが目的地に近づくと（`Update`メソッド内でチェック）、新しいランダムな目的地が選択され、プロセスが繰り返されます。

このプロセスにより、NPCはゲーム環境内を自然に徘徊し、動的でリアリスティックな行動パターンを示します。

### 実際のソースコード

それでは、実際の内容が以下のコードになります。

```WanderNPC.cs

using UnityEngine;
using UnityEngine.AI;

// 徘徊するNPCの動作を制御するクラス
public class WanderNPC : MonoBehaviour
{
    private NavMeshAgent agent; // NavMeshエージェント
    public bool debugMode = true; // デバッグモードのオン/オフ
    private Vector3 destination; // 現在の目的地

    // 徘徊パラメータ
    [SerializeField]
    private float wanderRadius = 10f;
    [SerializeField]
    private float wanderTimer = 5f;
    private float timer;

    void Start()
    {
        agent = GetComponent<NavMeshAgent>(); // NavMeshエージェントの取得
        timer = wanderTimer; // タイマーの初期化
    }

    void Update()
    {
        timer += Time.deltaTime;

        // 徘徊タイマーが設定した間隔を超えたら新しい目的地を設定
        if (timer >= wanderTimer)
        {
            destination = RandomWanderTarget(transform.position, wanderRadius); // 新しい目的地を決定
            agent.SetDestination(destination); // NavMeshエージェントに目的地を設定
            timer = 0; // タイマーをリセット
        }

        // デバッグモードがオンの場合、ルートを色付きで表示
        if (debugMode)
        {
            DrawPath(agent.path);
        }
    }

    // 徘徊パラメータを設定するメソッド
    public void SetWanderParameters(float radius, float minTime, float maxTime)
    {
        wanderRadius = radius;
        wanderTimer = Random.Range(minTime, maxTime); // タイマーをランダムに設定
    }

    // ランダムな徘徊の目的地を決定するメソッド
    private Vector3 RandomWanderTarget(Vector3 origin, float distance)
    {
        Vector3 randomPoint = origin + Random.insideUnitSphere * distance;
        randomPoint.y = 0; // 地面に沿うように高さを調整
        NavMeshHit navHit;
        NavMesh.SamplePosition(randomPoint, out navHit, distance, -1);
        return navHit.position;
    }


    // パスを描画するメソッド
    private void DrawPath(NavMeshPath path)
    {
        if (path.corners.Length < 2)
            return;

        for (int i = 1; i < path.corners.Length; i++)
        {
            // 線の色を赤くして目立たせる
            Debug.DrawLine(path.corners[i - 1], path.corners[i], Color.red, 0.1f, false);

            // 追加：パスのポイント間に小さな球体を描画してパスを強調する
            Debug.DrawRay(path.corners[i - 1], Vector3.up * 0.5f, Color.red, 0.1f);
        }
    }

}

```
### デバック時の注意点
- NavMeshが正しく設定されていないと、`NavMeshAgent`は期待通りに機能しません。徘徊するNPCが適切に移動できるようにするためには、NavMeshの設定が必要です。

### NavMeshの設定方法
1. **ナビゲーション可能なエリアの定義:**
   - Unityエディタで、ゲーム内でNPCが移動可能なエリアを選択します。
   - `Navigation`ウィンドウを開き（`Window` > `AI` > `Navigation`）、`Navigation Static`オプションを選択して、選択したエリアを静的オブジェクトとしてマークします。

2. **NavMeshの生成:**
   - `Navigation`ウィンドウ内の`Bake`タブを開きます。
   - 必要に応じて、エリアタイプ、エージェントサイズ、その他のパラメータを調整します。
   - `Bake`ボタンを押して、NavMeshを生成します。

3. **確認:**
   - NavMeshが生成されると、エディタのシーンビューにナビゲーション可能なエリアが表示されます（通常は青いオーバーレイで示されます）。
   - 生成されたNavMeshが、NPCが移動する予定のエリアをカバーしていることを確認します。


## DecisionTreesNPCの実装

### ディシジョンツリーの基本概念
ディシジョンツリーは、選択肢の結果を木構造で表したものです。この木構造は、分岐（節点）と終端（葉）から成り立っており、各分岐は特定の条件または質問を表します。ディシジョンツリーは、複雑な意思決定プロセスを単純化し、直感的に理解できる形で表現するために使用されます。

### 動作原理
ディシジョンツリーでは、ルート（根）から始まり、条件に基づいて木の枝をたどります。各分岐点で、条件を満たすかどうかによって次の枝へ進みます。このプロセスを繰り返し、最終的に終端に到達すると、決定または結果が得られます。この方法により、データの分類や意思決定が行われます。

### 日常生活での例
1. **症状に基づく医療診断：** 患者が特定の症状を示した場合、医師はディシジョンツリーを使用して可能な疾患を絞り込みます。例えば、発熱があるか、咳があるかなどの質問に基づいて、風邪、インフルエンザ、その他の病気を区別します。
   
2. **買い物時の意思決定：** 消費者は商品を購入する際、価格、品質、ブランドなどの要因に基づいてディシジョンツリーを無意識に使っています。例えば、予算内であれば購入、品質が低ければ別の商品を検討、といった流れです。

### ディシジョンツリーの設計概念
1. **階層的な意思決定プロセス：** ディシジョンツリーは、意思決定の各段階を階層的に表現します。最上部のルートノードから始まり、条件に応じて枝分かれしていき、最終的に葉ノード（結果）に至ります。

2. **条件分岐：** 各ノードは特定の条件や質問を表し、それに対する回答（通常は「はい」または「いいえ」）に基づいて、次のノードへ進みます。

3. **透明性と解釈の容易さ：** ディシジョンツリーは、他の多くの機械学習モデルと比べて、その決定プロセスが直感的で理解しやすいという利点があります。

4. **過学習のリスク：** ディシジョンツリーは複雑化しやすく、特に訓練データに過度に適合してしまうこと（過学習）があります。これを避けるために、木の深さの制限や枝刈りなどの手法が使われます。

### 実装規模と影響範囲
- **規模：** ディシジョンツリーは、小規模なデータセットから大規模なデータセットまで幅広く適用可能です。ただし、データの複雑度やツリーの深さによっては、計算資源や処理時間が問題になることがあります。

- **応用分野：** ディシジョンツリーは、ビジネスの意思決定、医療診断、金融リスク評価、顧客セグメンテーション、マーケティング戦略など、多岐にわたる分野で利用されています。

- **影響範囲：** 意思決定の自動化や高速化に大きく貢献し、特にデータに基づく予測や分類の精度を向上させることができます。しかし、データの質や偏りによっては、誤った結論やバイアスをもたらすリスクもあります。

### 実際のコード

```DecisionTreesNPC.cs
using UnityEngine;

public class DecisionTreesNPC : MonoBehaviour
{
    private enum State { Wander, Idle }
    private State currentState;

    [SerializeField] private WanderNPC wanderNPC;
    [SerializeField] private float detectionRadius = 5f;
    [SerializeField] private float minIdleCooldown = 3f;
    [SerializeField] private float maxIdleCooldown = 10f;
    [SerializeField] private float idleCooldown;
    private float lastIdleTime;

    void Awake()
    {
        currentState = State.Wander;
        wanderNPC = GetComponent<WanderNPC>();
        idleCooldown = Random.Range(minIdleCooldown, maxIdleCooldown);

        if (wanderNPC == null)
        {
            Debug.LogError("WanderNPC component not found on the GameObject.");
        }
    }

    void Update()
    {
        switch (currentState)
        {
            case State.Wander:
                // Now checking for nearby NPCs only if the cooldown has elapsed
                if (Time.time - lastIdleTime > idleCooldown)
                {
                    CheckForNearbyNPCs();
                }
                break;
            case State.Idle:
                if (Time.time - lastIdleTime > idleCooldown)
                {
                    ResumeWandering();
                }
                break;
        }
    }

    private void CheckForNearbyNPCs()
    {
        Collider[] hitColliders = Physics.OverlapSphere(transform.position, detectionRadius);
        foreach (var hitCollider in hitColliders)
        {
            if (hitCollider.gameObject.CompareTag("NPC") && hitCollider.gameObject != gameObject)
            {
                DecisionTreesNPC otherNPC = hitCollider.GetComponent<DecisionTreesNPC>();
                if (otherNPC != null && otherNPC.currentState == State.Wander)
                {
                    PauseWandering();
                    break;
                }
            }
        }
    }

    private void PauseWandering()
    {
        currentState = State.Idle;
        lastIdleTime = Time.time;
        idleCooldown = Random.Range(minIdleCooldown, maxIdleCooldown);
        wanderNPC.PauseWandering();
    }

    private void ResumeWandering()
    {
        currentState = State.Wander;
        wanderNPC.ResumeWandering();
    }

    public void SetDetectionRadius(float radius)
    {
        detectionRadius = radius;
    }

    public void SetIdleCooldownRange(float minCooldown, float maxCooldown)
    {
        minIdleCooldown = minCooldown;
        maxIdleCooldown = maxCooldown;
    }
}

```
### 概要
このスクリプトは、ディシジョンツリーの基本的な原理を利用して、NPCの行動を簡単なルールに基づいて制御します。状態（`State`）に基づいてNPCがどのように行動するかが決まり、周囲の環境（他のNPCの存在など）に応じて状態を変更します。これにより、NPCは自律的に環境に反応し、リアルタイムで行動を変更することができます。

スクリプトの全体的な設計は、Unityのゲーム開発で一般的なパターンに従っており、NPCに対して基本的なAI行動を追加するための良い出発点を提供します。

### 実際のクラスとプロパティ
- **クラス名:** `DecisionTreesNPC` 
  - Unityの`MonoBehaviour`を継承しており、これによりUnityのゲームオブジェクトにアタッチして使用できます。

- **プロパティ:** 
  - `private enum State { Wander, Idle }`: NPCの状態を表す列挙型。`Wander`（徘徊）と`Idle`（アイドル）の2つの状態があります。
  - `private State currentState`: 現在のNPCの状態を保持します。
  - `private WanderNPC wanderNPC`: 徘徊動作を制御するためのコンポーネントの参照。
  - `private float detectionRadius`: 他のNPCを検出する範囲。
  - `private float minIdleCooldown`, `maxIdleCooldown`: アイドル状態の最小・最大持続時間。
  - `private float idleCooldown`: 現在のアイドル状態の持続時間。
  - `private float lastIdleTime`: 最後にアイドル状態になった時刻。

### 主要なメソッド
- **`Awake`メソッド:**
  - NPCが最初にアクティブになったときに一度だけ呼ばれる初期化メソッド。
  - NPCの状態を`Wander`に設定し、必要なコンポーネントを設定します。

- **`Update`メソッド:**
  - このメソッドは毎フレーム呼ばれ、NPCの状態に基づいて行動を更新します。
  - `Wander`状態では、一定時間が経過すると周囲のNPCをチェックします。
  - `Idle`状態では、一定時間が経過すると再び徘徊を再開します。

- **`CheckForNearbyNPCs`メソッド:**
  - 周囲のNPCを検出し、条件に応じて行動を変更します。
  - 他の徘徊中のNPCを検出した場合、`PauseWandering`メソッドを呼び出してアイドル状態に移行します。

- **`PauseWandering`メソッドと`ResumeWandering`メソッド:**
  - `PauseWandering`はNPCをアイドル状態に移行させ、`ResumeWandering`は徘徊状態に戻します。


## まとめ

### Decision Trees

- **単純な条件に基づく決定を行うAI**:
  - Decision Treesは、より単純なif-elseの条件に基づいて意思決定を行うために使用されます。これは一つのNPCやオブジェクトが直面する特定の問題に対して迅速な決定を下す際に役立ちますが、必ずしも戦術に限定されるわけではありません。


### Behavior Trees

- **動的な環境適応を持つAI**:
  - Behavior Treesは、エージェントが環境の変化に柔軟に対応し、リアルタイムで行動を変更する必要がある場合に最適です。これは、ストーリーの中の一部の戦略だけでなく、より広範な状況に適応するAIの行動を制御するのに適しています。

### AI Planner

- **長期的な戦略と目標を持つAI**:
  - AI Plannerは、長期的な目標や複雑な計画を必要とするシナリオに適しています。これは必ずしもストーリーに基づくとは限らず、戦略ゲームのような複雑なシナリオでの戦術的な意思決定や計画立案にも使用されます。


まとめると、各ツールはAIの異なる側面を対応しており、ゲーム内での役割や用途は多岐にわたります。
AI Plannerは長期的な計画立案、Behavior Treesは動的な環境適応、Decision Treesは単純な条件に基づく決定に特化しています。
これらのツールは、ゲームの特定の要件やAIの行動の複雑さに応じて選択され、しばしば補完的に使用されます。