using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.AI; [RequireComponent(typeof(NavMeshAgent))] [RequireComponent(typeof(Animator))] [RequireComponent(typeof(BossWebSpawner))] public class BossController : MonoBehaviour, IDamageable, IPauseable { [Header("ステータス")] [SerializeField] private float maxHp = 500f; [SerializeField] private float currentHp = 500f; [field: Header("移動・攻撃パラメータ")] [field: SerializeField] public float ChaseRange { get; private set; } = 15f; [field: SerializeField] public float BiteRange { get; private set; } = 5f; [field: SerializeField] public float ChargeSpeed { get; private set; } = 14f; [field: SerializeField] public float ChargeDuration { get; private set; } = 2f; [field: SerializeField] public float ChargeMinimumDistance { get; private set; } = 6f; [field: SerializeField] public float ChargeCooldownDuration { get; private set; } = 4f; [field: SerializeField] public float ChargeRoarDuration { get; private set; } = 5f; [Header("アンカー・天井技設定")] public Transform ceilingAnchor; public Transform groundAnchor; [field: SerializeField] public float AttackRadius { get; private set; } = 15f; [field: SerializeField] public float JumpRoarDuration { get; private set; } = 2f; [field: SerializeField] public float CeilingAttackDuration { get; private set; } = 8.0f; [field: SerializeField] public float CeilingShotInterval { get; private set; } = 0.2f; [field: SerializeField] public float WebWarningDelay { get; private set; } = 0.8f; [field: SerializeField] public float WebDuration { get; private set; } = 3.0f; public NavMeshAgent Agent { get; private set; } public Animator Anim { get; private set; } public Transform Player { get; private set; } public BossWebSpawner WebSpawner { get; private set; } public float ChargeCooldownTimer { get; private set; } public float NormalSpeed { get; private set; } public float NormalAcceleration { get; private set; } public bool PendingUltimate { get; set; } private ISpiderState currentState; private Dictionary stateCache; private bool triggered50; private bool triggered25; private bool isPaused; private void Awake() { Agent = GetComponent(); Anim = GetComponent(); WebSpawner = GetComponent(); GameObject pObj = GameObject.FindGameObjectWithTag("Player"); if (pObj != null) Player = pObj.transform; InitializeFSM(); } private void Start() { if (Agent != null) { NormalSpeed = Agent.speed; NormalAcceleration = Agent.acceleration; } if (WorldPauseManager.Instance != null) { WorldPauseManager.Instance.Register(this); } } private void OnDestroy() { if (WorldPauseManager.Instance != null) { WorldPauseManager.Instance.Unregister(this); } } public void Pause() { isPaused = true; if (Anim != null) Anim.speed = 0f; if (Agent != null && Agent.enabled && Agent.isOnNavMesh) { Agent.isStopped = true; } } public void Resume() { isPaused = false; if (Anim != null) Anim.speed = 1f; if (Agent != null && Agent.enabled && Agent.isOnNavMesh) { Agent.isStopped = false; } } private void InitializeFSM() { stateCache = new Dictionary { { 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) } }; ChangeState(); } private void Update() { if (isPaused) return; currentState?.UpdateState(); if (ChargeCooldownTimer > 0f) { ChargeCooldownTimer -= Time.deltaTime; } } public void ChangeState() where T : ISpiderState { currentState?.ExitState(); ResetAllAnimationTriggers(); currentState = stateCache[typeof(T)]; currentState.EnterState(); } public void TakeDamage(DamageInfo damageInfo) { currentHp = Mathf.Clamp(currentHp - damageInfo.amount, 0, maxHp); float hpPercentage = (currentHp / maxHp) * 100f; if (hpPercentage <= 25f && !triggered25) { triggered25 = true; PendingUltimate = true; TriggerPhaseTransition(); } 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(); } } public bool TriggerParryStun() { if (currentState is SpiderBiteState or SpiderChargeState) { ChangeState(); return true; } return false; } public void ResetChargeCooldown() => ChargeCooldownTimer = ChargeCooldownDuration; public void ResetAllAnimationTriggers() { if (Anim == null) return; Anim.ResetTrigger("Attack_Bite"); Anim.ResetTrigger("Attack_Roar"); Anim.ResetTrigger("ChargeStart"); Anim.ResetTrigger("JumpUp"); Anim.ResetTrigger("Land"); Anim.ResetTrigger("StunStart"); } [ContextMenu("🛠️ デバッグ:強制必殺技")] public void DebugTriggerUltimate() { if (!Application.isPlaying) return; PendingUltimate = true; ChangeState(); } }