201 lines
6.3 KiB
C#
201 lines
6.3 KiB
C#
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<Type, ISpiderState> stateCache;
|
|
|
|
private bool triggered50;
|
|
private bool triggered25;
|
|
private bool isPaused;
|
|
|
|
private void Awake()
|
|
{
|
|
Agent = GetComponent<NavMeshAgent>();
|
|
Anim = GetComponent<Animator>();
|
|
WebSpawner = GetComponent<BossWebSpawner>();
|
|
|
|
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<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) }
|
|
};
|
|
|
|
ChangeState<SpiderIdleState>();
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
if (isPaused) return;
|
|
|
|
currentState?.UpdateState();
|
|
|
|
if (ChargeCooldownTimer > 0f)
|
|
{
|
|
ChargeCooldownTimer -= Time.deltaTime;
|
|
}
|
|
}
|
|
|
|
public void ChangeState<T>() 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<SpiderJumpToCeilingState>();
|
|
}
|
|
}
|
|
|
|
public bool TriggerParryStun()
|
|
{
|
|
if (currentState is SpiderBiteState or SpiderChargeState)
|
|
{
|
|
ChangeState<SpiderStunState>();
|
|
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<SpiderJumpToCeilingState>();
|
|
}
|
|
} |