Files
VRProject/Assets/_Scripts/BossController.cs
ohgushiyuga 43ba4da2df parili
2026-07-08 16:28:20 +09:00

203 lines
6.4 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using UnityEngine;
using UnityEngine.AI;
using System.Collections.Generic;
[RequireComponent(typeof(NavMeshAgent))]
[RequireComponent(typeof(Animator))]
public class BossController : MonoBehaviour, IDamageble
{
[SerializeField] private float maxHp = 500f;
[SerializeField] private float currentHp = 500f;
[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;
[SerializeField] public Transform ceilingAnchor;
[SerializeField] public Transform groundAnchor;
[field: SerializeField] public float AttackRadius { get; private set; } = 10f;
[field: SerializeField] public float JumpRoarDuration { get; private set; } = 5f;
[SerializeField] private GameObject warningMarkerPrefab;
[SerializeField] private GameObject webPrefab;
public NavMeshAgent Agent { get; private set; }
public Animator Anim { get; private set; }
public Transform Player { get; private set; }
public float ChargeCooldownTimer { get; private set; } = 0f;
public float NormalSpeed { get; private set; }
public float NormalAcceleration { get; private set; }
private ISpiderState currentState;
private Dictionary<System.Type, ISpiderState> stateCache;
// フェーズトリガー
private bool triggered50 = false;
private bool triggered25 = false;
public bool PendingUltimate { get; set; } = false;
private void Awake()
{
Agent = GetComponent<NavMeshAgent>();
Anim = GetComponent<Animator>();
GameObject pObj = GameObject.FindGameObjectWithTag("Player");
if (pObj != null) Player = pObj.transform;
InitializeFSM();
}
private void Start()
{
if (Agent != null)
{
NormalSpeed = Agent.speed;
NormalAcceleration = Agent.acceleration;
}
}
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) }
};
ChangeState<SpiderIdleState>();
}
private void Update()
{
currentState?.UpdateState();
if (ChargeCooldownTimer > 0f)
{
ChargeCooldownTimer -= Time.deltaTime;
}
}
public void ChangeState<T>() where T : ISpiderState
{
if (currentState != null)
{
currentState.ExitState();
ResetAllAnimationTriggers();
Debug.Log($"🕷️ BossSpider [Exit]: {currentState.GetType().Name}");
}
currentState = stateCache[typeof(T)];
Debug.Log($"🕷️ BossSpider [Enter]: {currentState.GetType().Name}");
currentState.EnterState();
}
public void TakeDamage(DamageInfo damageInfo)
{
currentHp -= damageInfo.amount;
currentHp = Mathf.Clamp(currentHp, 0, maxHp);
float hpPercentage = (currentHp / maxHp) * 100f;
Debug.Log($"💥 ボスに { damageInfo.amount} ダメージ。残りHP: {hpPercentage:F1}%");
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 void TriggerParryStun()
{
if (currentState is SpiderBiteState || currentState is SpiderChargeState)
{
ChangeState<SpiderStunState>();
}
}
public void ResetChargeCooldown()
{
ChargeCooldownTimer = ChargeCooldownDuration;
}
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)
{
GameObject marker = null;
if (warningMarkerPrefab != null)
{
marker = Instantiate(warningMarkerPrefab, pos + new Vector3(0, 0.02f, 0), Quaternion.identity);
}
yield return new WaitForSeconds(delay);
if (marker != null) Destroy(marker);
if (webPrefab != null)
{
GameObject web = Instantiate(webPrefab, pos, Quaternion.identity);
Destroy(web, duration);
}
}
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");
}
}
/// 共通データboss参照を自動保持し、各子ステートの無駄なコードを極限まで削ります。
public abstract class SpiderStateBase : ISpiderState
{
protected readonly BossController boss;
protected SpiderStateBase(BossController owner)
{
boss = owner;
}
public virtual void EnterState() { }
public abstract void UpdateState();
public virtual void ExitState() { }
}
public interface ISpiderState
{
void EnterState();
void UpdateState();
void ExitState();
}