敵AI Ver.1

This commit is contained in:
oogushiyuuga
2026-05-26 17:04:24 +09:00
parent b321a48aec
commit 4141e2d189
18 changed files with 6495 additions and 28 deletions

186
Assets/Scripts/EnemyAI.cs Normal file
View File

@@ -0,0 +1,186 @@
using UnityEngine;
using UnityEngine.AI;
using System.Collections;
[RequireComponent(typeof(NavMeshAgent))]
public class EnemyAI : MonoBehaviour
{
// 敵の状態を定義する列挙型FSM
public enum AIState { Wander, Chase }
[Header("現在の状態")]
[SerializeField] private AIState currentState = AIState.Wander;
[Header("索敵設定")]
[Tooltip("プレイヤーを検知する距離")]
[SerializeField] private float detectionRange = 10f;
[Tooltip("プレイヤーを見失う距離(検知距離より広く設定するのがコツ)")]
[SerializeField] private float loseRange = 15f;
[Tooltip("プレイヤーのタグ名")]
[SerializeField] private string playerTag = "Player";
[Header("徘徊Wander設定")]
[SerializeField] private float wanderRadius = 8f;
[SerializeField] private float minWaitTime = 0.1f;
[SerializeField] private float maxWaitTime = 1f;
[Tooltip("目的地に向けて移動を開始してから、何秒で諦めるか")]
[SerializeField] private float wanderTimeout = 7f;
private NavMeshAgent agent;
private Transform playerTransform;
private bool isWandering = false;
void Start()
{
agent = GetComponent<NavMeshAgent>();
// 負荷軽減のため、AIの思考ループをコルーチンで開始毎フレーム Update で処理しない)
StartCoroutine(AILoop());
}
// AIの意思決定を行うメインループ0.2秒ごとに実行して負荷を劇的に下げる)
private IEnumerator AILoop()
{
while (true)
{
// 1. プレイヤーを探す(まだ見つけていない場合のみ)
if (playerTransform == null)
{
FindPlayer();
}
// 2. 現在のステート(状態)に応じて処理を分岐
switch (currentState)
{
case AIState.Wander:
WanderBehavior();
break;
case AIState.Chase:
ChaseBehavior();
break;
}
// 毎フレームではなく、0.2秒1秒に5回だけ実行するQuest 3向けの最適化
yield return new WaitForSeconds(0.2f);
}
}
// シーン内からプレイヤー(タグ付き)を探すメソッド
private void FindPlayer()
{
GameObject playerObj = GameObject.FindGameObjectWithTag(playerTag);
if (playerObj != null)
{
playerTransform = playerObj.transform;
}
}
// ─── 【徘徊状態】の行動 ───
private void WanderBehavior()
{
if (playerTransform != null)
{
// プレイヤーとの距離を計算
float distanceToPlayer = Vector3.Distance(transform.position, playerTransform.position);
// 索敵範囲内に入ったら「追跡状態」へ遷移
if (distanceToPlayer <= detectionRange)
{
currentState = AIState.Chase;
isWandering = false;
agent.ResetPath(); // 徘徊の目的地をリセット
Debug.Log($"👁️ {gameObject.name}: プレイヤーを発見! 追跡します。");
return;
}
}
// 徘徊の移動ロジック
if (!isWandering)
{
StartCoroutine(WanderMoveRoutine());
}
}
private IEnumerator WanderMoveRoutine()
{
isWandering = true;
// ランダムな目的地を取得して移動開始
Vector3 newPos = GetRandomNavMeshPoint(transform.position, wanderRadius);
agent.SetDestination(newPos);
float elapsedTime = 0f; // 経過時間を計るカウンター
// 目的地に到着するまで待つ(ただしタイムアウト時間を超えたらループを抜ける)
while (agent.remainingDistance > agent.stoppingDistance)
{
// 0.5秒ごとにチェック
yield return new WaitForSeconds(0.5f);
elapsedTime += 0.5f;
// 【スタック対策】設定した制限時間を超えた場合
if (elapsedTime >= wanderTimeout)
{
Debug.Log($"⚠️ {gameObject.name}: 目的地({newPos})に到達できないため諦めました。");
agent.ResetPath(); // 動けない経路(バグ目的地)を一旦クリアする
break; // whileループを強制終了して次の目的地設定へ進める
}
}
// 到着後(または諦めた後)、ランダムな時間だけその場で待機して次の徘徊へ
yield return new WaitForSeconds(Random.Range(minWaitTime, maxWaitTime));
isWandering = false;
}
// ─── 【追跡状態】の行動 ───
private void ChaseBehavior()
{
if (playerTransform == null)
{
currentState = AIState.Wander;
return;
}
float distanceToPlayer = Vector3.Distance(transform.position, playerTransform.position);
// 見失う距離loseRangeを超えたら「徘徊状態」に戻る
if (distanceToPlayer > loseRange)
{
currentState = AIState.Wander;
agent.ResetPath();
Debug.Log($"❓ {gameObject.name}: プレイヤーを見失った。徘徊に戻ります。");
return;
}
// プレイヤーの位置を目的地に設定0.2秒ごとに更新されるため、滑らかに追従します)
agent.SetDestination(playerTransform.position);
}
// NavMesh上のランダムな座標を取得する補助メソッド
private Vector3 GetRandomNavMeshPoint(Vector3 center, float radius)
{
Vector3 randomDirection = Random.insideUnitSphere * radius;
randomDirection += center;
NavMeshHit hit;
if (NavMesh.SamplePosition(randomDirection, out hit, radius, NavMesh.AllAreas))
{
return hit.position;
}
return center;
}
// 【視覚的なデバッグ用】エディタ上で索敵範囲を線で表示する
private void OnDrawGizmosSelected()
{
// 索敵範囲(赤)
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(transform.position, detectionRange);
// 見失う範囲(黄)
Gizmos.color = Color.yellow;
Gizmos.DrawWireSphere(transform.position, loseRange);
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 88407b9f9a6654436b641cce64611731