Files
VRProject/Assets/Scripts/BossController.cs
2026-07-01 14:08:37 +09:00

187 lines
6.9 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
{
#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