using UnityEngine; public class SpiderIdleState : SpiderStateBase { public SpiderIdleState(BossController owner) : base(owner) { } public override void EnterState() => boss.Anim.SetTrigger("Idle"); public override void UpdateState() { if (boss.Player == null) return; if (Vector3.Distance(boss.transform.position, boss.Player.position) <= boss.ChaseRange) { boss.ChangeState(); } } } public class SpiderChaseState : SpiderStateBase { public SpiderChaseState(BossController owner) : base(owner) { } public override void EnterState() => boss.Agent.isStopped = false; public override void UpdateState() { if (boss.Player == null) return; float distance = Vector3.Distance(boss.transform.position, boss.Player.position); // 遠距離突進判定(カプセル化されたプロパティを使用) if (distance >= boss.ChargeMinimumDistance && boss.ChargeCooldownTimer <= 0f) { boss.ChangeState(); return; } // 至近距離噛みつき判定 if (distance <= boss.BiteRange) { boss.ChangeState(); return; } boss.Agent.SetDestination(boss.Player.position); boss.Anim.SetFloat("Speed", boss.Agent.velocity.magnitude); } } public class SpiderBiteState : SpiderStateBase { private float stateTimer; public SpiderBiteState(BossController owner) : base(owner) { } public override void EnterState() { stateTimer = 1.5f; boss.Agent.ResetPath(); boss.Agent.isStopped = true; boss.Agent.velocity = Vector3.zero; boss.Anim.SetFloat("Speed", 0f); boss.Anim.SetTrigger("Attack_Bite"); Debug.Log("🎯 蜘蛛ボス:噛みつき攻撃! その場に完全固定。"); } public override void UpdateState() { boss.Agent.velocity = Vector3.zero; stateTimer -= Time.deltaTime; if (stateTimer <= 0f) { if (boss.PendingUltimate) boss.ChangeState(); else boss.ChangeState(); } } } public class SpiderChargeState : SpiderStateBase { private float roarTimer; private float chargeTimer; private Vector3 chargeDirection; private bool isChargingPhase; public SpiderChargeState(BossController owner) : base(owner) { } public override void EnterState() { roarTimer = boss.ChargeRoarDuration; chargeTimer = boss.ChargeDuration; isChargingPhase = false; boss.Agent.isStopped = true; boss.Agent.velocity = Vector3.zero; boss.ResetChargeCooldown(); boss.Anim.SetTrigger("Attack_Roar"); Debug.Log($"🔊 蜘蛛ボス:遠距離を検知。その場でピタッと完全停止。咆哮(溜め {roarTimer}秒)を開始!"); } public override void ExitState() { boss.Anim.SetBool("IsCharging", false); boss.Agent.speed = boss.NormalSpeed; boss.Agent.acceleration = boss.NormalAcceleration; boss.Agent.isStopped = false; } public override void UpdateState() { // フェーズ1:咆哮・溜めロックオン if (!isChargingPhase) { roarTimer -= Time.deltaTime; boss.Agent.velocity = Vector3.zero; // 完全固定 if (boss.Player != null) { Vector3 lookDir = (boss.Player.position - boss.transform.position).normalized; lookDir.y = 0; if (lookDir != Vector3.zero) { boss.transform.rotation = Quaternion.Slerp(boss.transform.rotation, Quaternion.LookRotation(lookDir), Time.deltaTime * 10f); } } if (roarTimer <= 0f) { isChargingPhase = true; boss.Anim.SetTrigger("ChargeStart"); boss.Anim.SetBool("IsCharging", true); if (boss.Player != null) { chargeDirection = (boss.Player.position - boss.transform.position).normalized; chargeDirection.y = 0; } Vector3 targetDestination = boss.transform.position + chargeDirection * 20f; boss.Agent.isStopped = false; boss.Agent.speed = boss.ChargeSpeed; boss.Agent.acceleration = 120f; boss.Agent.SetDestination(targetDestination); Debug.Log("🚀 蜘蛛ボス:ロックオン完了。全速力で突進ダッシュを発射!"); } return; } // フェーズ2:突進移動中 chargeTimer -= Time.deltaTime; if (chargeTimer <= 0f || (boss.Agent.velocity.magnitude < 0.2f && chargeTimer < (boss.ChargeDuration - 0.3f))) { boss.ChangeState(); } } } public class SpiderJumpToCeilingState : SpiderStateBase { private float roarTimer; private float jumpProgress; private Vector3 startPos; private bool isJumpingPhase; public SpiderJumpToCeilingState(BossController owner) : base(owner) { } public override void EnterState() { roarTimer = boss.JumpRoarDuration; jumpProgress = 0f; startPos = boss.transform.position; isJumpingPhase = false; boss.Agent.enabled = false; boss.Anim.SetTrigger("Attack_Roar"); Debug.Log($"🚨 蜘蛛ボス:必殺技前の一大威嚇(咆哮 {roarTimer}秒)!"); } public override void UpdateState() { // フェーズ1:飛び立つ前の威嚇 if (!isJumpingPhase) { roarTimer -= Time.deltaTime; if (roarTimer <= 0f) { isJumpingPhase = true; boss.Anim.SetTrigger("JumpUp"); } return; } // フェーズ2:ジャンプ空中移動 if (boss.ceilingAnchor == null) return; jumpProgress += Time.unscaledDeltaTime * 1.5f; Vector3 currentPos = Vector3.Lerp(startPos, boss.ceilingAnchor.position, jumpProgress); currentPos.y += Mathf.Sin(jumpProgress * Mathf.PI) * 3f; boss.transform.position = currentPos; if (jumpProgress >= 1f) { boss.transform.position = boss.ceilingAnchor.position; boss.ChangeState(); } } } // ========================================== // ■ STATE: CEILING ATTACK (インスペクター無段階調整版) // ========================================== public class SpiderCeilingAttackState : SpiderStateBase { private float timer = 0f; private float shotTimer = 0f; public SpiderCeilingAttackState(BossController owner) : base(owner) { } public override void EnterState() { // ★リファクタリング:インスペクターで設定した必殺技の長さを適用 timer = boss.CeilingAttackDuration; shotTimer = 0f; boss.Anim.SetBool("IsOnCeiling", true); boss.PendingUltimate = false; Debug.Log($"🕸️ 蜘蛛ボス:天井からの乱射開始! 持続時間: {timer}秒 / 発射間隔: {boss.CeilingShotInterval}秒"); } public override void ExitState() { boss.Anim.SetBool("IsOnCeiling", false); if (boss.groundAnchor != null) boss.transform.position = boss.groundAnchor.position; boss.Agent.enabled = true; boss.Anim.SetTrigger("Land"); } public override void UpdateState() { timer -= Time.deltaTime; shotTimer -= Time.deltaTime; // タイマーがゼロになったら発射 if (shotTimer <= 0f) { ExecuteRandomWebAttack(); // ★リファクタリング:インスペクターで設定した間隔(秒)でリセットして弾数を制御 shotTimer = boss.CeilingShotInterval; } if (timer <= 0f) { boss.ChangeState(); } } private void ExecuteRandomWebAttack() { Vector3 targetGroundPos = Vector3.zero; int maxAttempts = 15; // 無限ループ(フリーズ)を防止するための最大再抽選回数 for (int i = 0; i < maxAttempts; i++) { // 1. 一旦ランダムな座標を算出 Vector2 randomCircle = Random.insideUnitCircle * boss.AttackRadius; targetGroundPos = new Vector3( boss.groundAnchor.position.x + randomCircle.x, boss.groundAnchor.position.y, boss.groundAnchor.position.z + randomCircle.y ); // 2. 現在アクティブなすべての糸との距離を計測する bool isTooClose = false; foreach (Vector3 existingPos in boss.ActiveWebPositions) { // 設定された回避半径(WebAvoidanceRadius)より近い既存の糸があるか? if (Vector3.Distance(targetGroundPos, existingPos) < boss.WebAvoidanceRadius) { isTooClose = true; break; // 被り検知、この座標は使えないのでチェックを抜けて再抽選へ } } // 3. 誰とも被っていなければ、このクリーンな座標で確定 if (!isTooClose) { break; } } // 確定した(または上限に達して安全が担保された)座標へ向けて発射命令 boss.SpawnWarningAndWeb(targetGroundPos, boss.WebWarningDelay, boss.WebDuration); } } public class SpiderStunState : SpiderStateBase { private float stunTimer; public SpiderStunState(BossController owner) : base(owner) { } public override void EnterState() { stunTimer = 4.0f; boss.Anim.SetTrigger("StunStart"); boss.Anim.SetBool("IsStunned", true); if (boss.Agent.enabled) boss.Agent.isStopped = true; Debug.Log("🛡️ パリィ成功! チャンスタイム!"); } public override void ExitState() { boss.Anim.SetBool("IsStunned", false); if (boss.Agent.enabled) boss.Agent.isStopped = false; } public override void UpdateState() { stunTimer -= Time.deltaTime; if (stunTimer <= 0f) { if (boss.PendingUltimate) boss.ChangeState(); else boss.ChangeState(); } } }