This commit is contained in:
ohgushiyuga
2026-08-27 12:51:15 +09:00
parent 1441c96f74
commit 52341100ed
32 changed files with 840 additions and 1076 deletions

View File

@@ -1,24 +1,29 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
using System.Collections.Generic;
[RequireComponent(typeof(NavMeshAgent))]
[RequireComponent(typeof(Animator))]
public class BossController : MonoBehaviour, IDamageble
[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;
[SerializeField] public Transform ceilingAnchor;
[SerializeField] public Transform groundAnchor;
[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;
@@ -26,33 +31,28 @@ public class BossController : MonoBehaviour, IDamageble
[field: SerializeField] public float WebWarningDelay { get; private set; } = 0.8f;
[field: SerializeField] public float WebDuration { get; private set; } = 3.0f;
[Tooltip("★新設:すでに糸がある場所から、最低何メートル離すか(被り防止の半径)")]
[field: SerializeField] public float WebAvoidanceRadius { get; private set; } = 2.5f;
// ★新設:現在ステージ上に存在している(または予兆がある)糸の座標リスト
public List<Vector3> ActiveWebPositions { get; private set; } = new List<Vector3>();
[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 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<System.Type, ISpiderState> stateCache;
private Dictionary<Type, ISpiderState> stateCache;
// フェーズトリガー
private bool triggered50 = false;
private bool triggered25 = false;
public bool PendingUltimate { get; set; } = false;
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;
@@ -67,11 +67,44 @@ public class BossController : MonoBehaviour, IDamageble
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<System.Type, ISpiderState>
stateCache = new Dictionary<Type, ISpiderState>
{
{ typeof(SpiderIdleState), new SpiderIdleState(this) },
{ typeof(SpiderChaseState), new SpiderChaseState(this) },
@@ -87,6 +120,8 @@ public class BossController : MonoBehaviour, IDamageble
private void Update()
{
if (isPaused) return;
currentState?.UpdateState();
if (ChargeCooldownTimer > 0f)
@@ -97,26 +132,18 @@ public class BossController : MonoBehaviour, IDamageble
public void ChangeState<T>() where T : ISpiderState
{
if (currentState != null)
{
currentState.ExitState();
ResetAllAnimationTriggers();
Debug.Log($"🕷️ BossSpider [Exit]: {currentState.GetType().Name}");
}
currentState?.ExitState();
ResetAllAnimationTriggers();
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);
currentHp = Mathf.Clamp(currentHp - damageInfo.amount, 0, maxHp);
float hpPercentage = (currentHp / maxHp) * 100f;
Debug.Log($"💥 ボスに {damageInfo.amount} ダメージ。残りHP: {hpPercentage:F1}%");
if (hpPercentage <= 25f && !triggered25)
{
triggered25 = true;
@@ -143,7 +170,7 @@ public class BossController : MonoBehaviour, IDamageble
public bool TriggerParryStun()
{
if (currentState is SpiderBiteState || currentState is SpiderChargeState)
if (currentState is SpiderBiteState or SpiderChargeState)
{
ChangeState<SpiderStunState>();
return true;
@@ -151,49 +178,7 @@ public class BossController : MonoBehaviour, IDamageble
return false;
}
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)
{
// 1. 生成する座標をアクティブリストに登録(この場所への他の割り込みを禁止する)
ActiveWebPositions.Add(pos);
// 2. 地面に予兆マークを生成
GameObject marker = null;
if (warningMarkerPrefab != null)
{
marker = Instantiate(warningMarkerPrefab, pos + new Vector3(0, 0.5f, 0), Quaternion.identity);
}
yield return new WaitForSeconds(delay);
if (marker != null) Destroy(marker);
// 3. 本物の糸オブジェクトを生成(X軸90度回転)
if (webPrefab != null)
{
Quaternion webRotation = Quaternion.Euler(270f, 0f, 0f);
GameObject web = Instantiate(webPrefab, pos + new Vector3(0, 0.5f, 0), webRotation);
// ★持続時間(duration)が終わるまで待機してからDestroyする形に変更
yield return new WaitForSeconds(duration);
if (web != null) Destroy(web);
}
else
{
yield return new WaitForSeconds(duration);
}
// 4. 糸が消滅したので、リストから座標を削除してスペースを解放する
ActiveWebPositions.Remove(pos);
}
public void ResetChargeCooldown() => ChargeCooldownTimer = ChargeCooldownDuration;
public void ResetAllAnimationTriggers()
{
@@ -206,46 +191,11 @@ public class BossController : MonoBehaviour, IDamageble
Anim.ResetTrigger("StunStart");
}
#region デバッグ用機能
/// <summary>
/// Unityインスペクターの右上メニューから実行できるデバッグ用コマンドです
/// </summary>
[ContextMenu("🛠️ デバッグ:強制必殺技(天井ジャンプ)")]
[ContextMenu("🛠️ デバッグ:強制必殺技")]
public void DebugTriggerUltimate()
{
if (!Application.isPlaying)
{
Debug.LogWarning("⚠️ ゲームが再生中(Playモード)の時のみ実行可能です。");
return;
}
Debug.Log("🛠️ 【デバッグ操作】必殺技の予約券を発行し、強制的に天井ジャンプステートへ遷移します。");
// 必殺技の発動権をONにし、現在の行動をキャンセルして天井ジャンプへ移行
if (!Application.isPlaying) return;
PendingUltimate = true;
ChangeState<SpiderJumpToCeilingState>();
}
#endregion
}
/// 共通データ(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();
}