187 lines
6.9 KiB
C#
187 lines
6.9 KiB
C#
using UnityEngine;
|
||
using UnityEngine.AI;
|
||
using System.Collections.Generic;
|
||
|
||
[RequireComponent(typeof(NavMeshAgent))]
|
||
[RequireComponent(typeof(Animator))]
|
||
public class BossController : MonoBehaviour
|
||
{
|
||
#region インスペクター設定項目
|
||
[Header("ステータス設定")]
|
||
[SerializeField] private float maxHp = 500f;
|
||
[SerializeField] private float currentHp = 500f;
|
||
[SerializeField] public float chaseRange = 15f;
|
||
[SerializeField] public float biteRange = 2.5f;
|
||
|
||
[Header("必殺技(天井)設定")]
|
||
[Tooltip("天井の待機座標となる空のGameObjectのTransformを指定してください")]
|
||
[SerializeField] public Transform ceilingAnchor;
|
||
[Tooltip("地上に戻る際の、中央の着地座標のTransformを指定してください")]
|
||
[SerializeField] public Transform groundAnchor;
|
||
[Tooltip("天井から糸を発射する範囲の広さ")]
|
||
[SerializeField] public float attackRadius = 10f;
|
||
[Tooltip("糸が降る前の予兆マークのPrefab")]
|
||
[SerializeField] private GameObject warningMarkerPrefab;
|
||
[Tooltip("地面に残る糸(コライダー&鈍足スクリプト付き)のPrefab")]
|
||
[SerializeField] private GameObject webPrefab;
|
||
|
||
[Header("突進(体当たり)設定")]
|
||
[SerializeField] public float chargeSpeed = 12f;
|
||
[SerializeField] public float chargeDuration = 1.5f;
|
||
#endregion
|
||
|
||
#region コンポーネント・キャッシュ用(ステートからアクセス可能)
|
||
[HideInInspector] public NavMeshAgent Agent { get; private set; }
|
||
[HideInInspector] public Animator Anim { get; private set; }
|
||
[HideInInspector] public Transform Player { get; private set; }
|
||
#endregion
|
||
|
||
#region FSM(状態管理)システム
|
||
private ISpiderState currentState;
|
||
|
||
// ステートのインスタンスをキャッシュ(ガベージコレクションの発生を抑える)
|
||
private Dictionary<System.Type, ISpiderState> stateCache;
|
||
|
||
// フェーズ管理用フラグ(50%と25%のトリガー管理)
|
||
private bool triggered50 = false;
|
||
private bool triggered25 = false;
|
||
public bool PendingUltimate { get; set; } = false; // 必殺技の発動権があるか
|
||
#endregion
|
||
|
||
private void Awake()
|
||
{
|
||
Agent = GetComponent<NavMeshAgent>();
|
||
Anim = GetComponent<Animator>();
|
||
|
||
GameObject pObj = GameObject.FindGameObjectWithTag("Player");
|
||
if (pObj != null) Player = pObj.transform;
|
||
|
||
InitializeFSM();
|
||
}
|
||
|
||
private void InitializeFSM()
|
||
{
|
||
stateCache = new Dictionary<System.Type, ISpiderState>
|
||
{
|
||
{ typeof(SpiderIdleState), new SpiderIdleState(this) },
|
||
{ typeof(SpiderChaseState), new SpiderChaseState(this) },
|
||
{ typeof(SpiderBiteState), new SpiderBiteState(this) },
|
||
{ typeof(SpiderChargeState), new SpiderChargeState(this) },
|
||
{ typeof(SpiderJumpToCeilingState), new SpiderJumpToCeilingState(this) },
|
||
{ typeof(SpiderCeilingAttackState), new SpiderCeilingAttackState(this) },
|
||
{ typeof(SpiderStunState), new SpiderStunState(this) }
|
||
};
|
||
|
||
// 初期ステートをIdleに設定
|
||
ChangeState<SpiderIdleState>();
|
||
}
|
||
|
||
private void Update()
|
||
{
|
||
// 現在のステートのUpdate処理を実行(ポリモーフィズム)
|
||
currentState?.UpdateState();
|
||
}
|
||
|
||
/// <summary>
|
||
/// オブジェクト指向に基づいた、型安全なステート遷移メソッド
|
||
/// </summary>
|
||
public void ChangeState<T>() where T : ISpiderState
|
||
{
|
||
if (currentState != null)
|
||
{
|
||
currentState.ExitState();
|
||
Debug.Log($"🕷️ BossSpider [Exit]: {currentState.GetType().Name}");
|
||
}
|
||
|
||
currentState = stateCache[typeof(T)];
|
||
Debug.Log($"🕷️ BossSpider [Enter]: {currentState.GetType().Name}");
|
||
currentState.EnterState();
|
||
}
|
||
|
||
/// <summary>
|
||
/// ダメージを受け取る外部窓口。HPトリガーによるフェーズ遷移をここで監視する
|
||
/// </summary>
|
||
public void TakeDamage(float damage)
|
||
{
|
||
currentHp -= damage;
|
||
currentHp = Mathf.Clamp(currentHp, 0, maxHp);
|
||
float hpPercentage = (currentHp / maxHp) * 100f;
|
||
|
||
Debug.Log($"💥 ボスに {damage} ダメージ。残りHP: {hpPercentage:F1}%");
|
||
|
||
// 25%以下カットのトリガー
|
||
if (hpPercentage <= 25f && !triggered25)
|
||
{
|
||
triggered25 = true;
|
||
PendingUltimate = true;
|
||
TriggerPhaseTransition();
|
||
}
|
||
// 50%以下カットのトリガー
|
||
else if (hpPercentage <= 50f && !triggered50 && hpPercentage > 25f)
|
||
{
|
||
triggered50 = true;
|
||
PendingUltimate = true;
|
||
TriggerPhaseTransition();
|
||
}
|
||
}
|
||
|
||
private void TriggerPhaseTransition()
|
||
{
|
||
// 咆哮、ジャンプ、スタン中以外であれば、現在の行動をキャンセルして即座に天井へエスケープ
|
||
if (currentState is not SpiderJumpToCeilingState &&
|
||
currentState is not SpiderCeilingAttackState &&
|
||
currentState is not SpiderStunState)
|
||
{
|
||
ChangeState<SpiderJumpToCeilingState>();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 【パリィ連携窓口】プレイヤーのVRInputPunchなどから、パリィ成功時にこのメソッドが叩かれる
|
||
/// </summary>
|
||
public void TriggerParryStun()
|
||
{
|
||
// パリィを受け付ける攻撃ステート(噛みつき、体当たり)のときのみスタンに移行する
|
||
if (currentState is SpiderBiteState || currentState is SpiderChargeState)
|
||
{
|
||
ChangeState<SpiderStunState>();
|
||
}
|
||
}
|
||
|
||
#region ファクトリメソッド(エフェクトやオブジェクト生成の保守性を担保)
|
||
public void SpawnWarningAndWeb(Vector3 targetGroundPos, float delay, float webDuration)
|
||
{
|
||
StartCoroutine(SpawnWebRoutine(targetGroundPos, delay, webDuration));
|
||
}
|
||
|
||
private System.Collections.IEnumerator SpawnWebRoutine(Vector3 pos, float delay, float duration)
|
||
{
|
||
// 1. 地面に予兆マークを生成
|
||
GameObject marker = null;
|
||
if (warningMarkerPrefab != null)
|
||
{
|
||
marker = Instantiate(warningMarkerPrefab, pos + new Vector3(0, 0.02f, 0), Quaternion.identity);
|
||
}
|
||
|
||
yield return new WaitForSeconds(delay);
|
||
|
||
// 2. 予兆マークを消し、本物の糸オブジェクトを生成
|
||
if (marker != null) Destroy(marker);
|
||
|
||
if (webPrefab != null)
|
||
{
|
||
GameObject web = Instantiate(webPrefab, pos, Quaternion.identity);
|
||
Destroy(web, duration); // 数秒後に自動消滅
|
||
}
|
||
}
|
||
#endregion
|
||
}
|
||
|
||
#region 1. ステート共通インターフェース
|
||
public interface ISpiderState
|
||
{
|
||
void EnterState();
|
||
void UpdateState();
|
||
void ExitState();
|
||
}
|
||
#endregion |