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

334 lines
11 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;
// ==========================================
// ■ STATE: IDLE (待機状態)
// ==========================================
public class SpiderIdleState : ISpiderState
{
private BossController boss;
public SpiderIdleState(BossController owner) => boss = owner;
public void EnterState() => boss.Anim.SetTrigger("Idle");
public void ExitState() { }
public void UpdateState()
{
if (boss.Player == null) return;
// プレイヤーを検知したら即座に追跡へ
if (Vector3.Distance(boss.transform.position, boss.Player.position) <= boss.chaseRange)
{
boss.ChangeState<SpiderChaseState>();
}
}
}
// ==========================================
// ■ STATE: CHASE (追跡・行動分岐状態)
// ==========================================
public class SpiderChaseState : ISpiderState
{
private BossController boss;
private float decisionCooldown = 0f;
public SpiderChaseState(BossController owner) => boss = owner;
public void EnterState() => boss.Agent.isStopped = false;
public void ExitState() => boss.Agent.isStopped = true;
public void UpdateState()
{
if (boss.Player == null) return;
// NavMeshでの追従
boss.Agent.SetDestination(boss.Player.position);
boss.Anim.SetFloat("Speed", boss.Agent.velocity.magnitude);
// ディシジョン(思考)タイマーの更新
decisionCooldown -= Time.deltaTime;
if (decisionCooldown > 0f) return;
float distance = Vector3.Distance(boss.transform.position, boss.Player.position);
// 1. 至近距離 ➔ 通常噛みつき
if (distance <= boss.biteRange)
{
boss.ChangeState<SpiderBiteState>();
decisionCooldown = 2f;
}
// 2. 中距離 ➔ 走って体当たり攻撃
else if (distance > boss.biteRange && distance <= boss.chaseRange * 0.6f)
{
boss.ChangeState<SpiderChargeState>();
decisionCooldown = 5f;
}
}
}
// ==========================================
// ■ STATE: BITE (通常噛みつき - パリィ可)
// ==========================================
public class SpiderBiteState : ISpiderState
{
private BossController boss;
private float stateTimer;
public SpiderBiteState(BossController owner) => boss = owner;
public void EnterState()
{
stateTimer = 1.5f; // 攻撃アニメーションの想定想定想定
boss.Anim.SetTrigger("Attack_Bite");
Debug.Log("🎯 蜘蛛ボス:噛みつき攻撃!(パリィ受付中)");
}
public void ExitState() { }
public void UpdateState()
{
stateTimer -= Time.deltaTime;
if (stateTimer <= 0f)
{
// 攻撃が終わったら、フェーズ移行チェックを挟みつつ追跡に戻る
if (boss.PendingUltimate) boss.ChangeState<SpiderJumpToCeilingState>();
else boss.ChangeState<SpiderChaseState>();
}
}
}
// ==========================================
// ■ STATE: CHARGE (走って体当たり突進 - パリィ可)
// ==========================================
public class SpiderChargeState : ISpiderState
{
private BossController boss;
private float roarTimer;
private float chargeTimer;
private Vector3 chargeDirection;
private bool isChargingPhase;
public SpiderChargeState(BossController owner) => boss = owner;
public void EnterState()
{
roarTimer = 1.5f;
chargeTimer = boss.chargeDuration;
isChargingPhase = false;
boss.Agent.enabled = false; // 移動をマニュアル制御するためNavMeshを切る
// 1. まず「威嚇の咆哮」アニメーションをトリガー
boss.Anim.SetTrigger("Attack_Roar");
Debug.Log("🔊 蜘蛛ボス:走る前に威嚇の咆哮中...(この間にプレイヤーはパリィの構えが可能)");
}
public void ExitState()
{
boss.Anim.SetBool("IsCharging", false);
boss.Agent.enabled = true;
}
public void UpdateState()
{
// フェーズ1威嚇中その場で動かない
if (!isChargingPhase)
{
roarTimer -= Time.deltaTime;
// 威嚇中もプレイヤーの方向をじっと見据える(ロックオン演出)
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 * 5f);
}
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;
boss.transform.rotation = Quaternion.LookRotation(chargeDirection);
}
}
return;
}
// フェーズ2突進中直線移動を実行。ここからプレイヤーはパリィ可能
boss.transform.position += chargeDirection * boss.chargeSpeed * Time.deltaTime;
chargeTimer -= Time.deltaTime;
if (chargeTimer <= 0f)
{
boss.ChangeState<SpiderChaseState>();
}
}
}
// ==========================================
// ■ STATE: JUMP TO CEILING (天井への大ジャンプ)
// ==========================================
public class SpiderJumpToCeilingState : ISpiderState
{
private BossController boss;
private float roarTimer;
private float jumpProgress;
private Vector3 startPos;
private bool isJumpingPhase;
public SpiderJumpToCeilingState(BossController owner) => boss = owner;
public void EnterState()
{
roarTimer = 2.0f; // ★天井に飛ぶ前の大威嚇の時間 (2.0秒)
jumpProgress = 0f;
startPos = boss.transform.position;
isJumpingPhase = false;
boss.Agent.enabled = false;
// 1. 必殺技移行を知らせる威嚇の咆哮
boss.Anim.SetTrigger("Attack_Roar");
Debug.Log("🚨 蜘蛛ボス:必殺技前の一大威嚇(咆哮)! このあと天井へ飛びます。");
}
public void ExitState() { }
public 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<SpiderCeilingAttackState>();
}
}
}
// ==========================================
// ■ STATE: CEILING ATTACK (必殺技:天井糸吐き)
// ==========================================
public class SpiderCeilingAttackState : ISpiderState
{
private BossController boss;
private float stateDuration = 7.0f; // 必殺技全体の長さ(秒)
private float shotInterval = 0.4f; // 糸を降らせる間隔
private float timer = 0f;
private float shotTimer = 0f;
public SpiderCeilingAttackState(BossController owner) => boss = owner;
public void EnterState()
{
timer = stateDuration;
shotTimer = 0f;
boss.Anim.SetBool("IsOnCeiling", true);
boss.PendingUltimate = false; // 発動権を消化
Debug.Log("🕸️ 蜘蛛ボス:天井から無数の糸を乱射中!");
}
public void ExitState()
{
boss.Anim.SetBool("IsOnCeiling", false);
// 地上の着地座標へ位置をリセットしてNavMeshを復帰
if (boss.groundAnchor != null) boss.transform.position = boss.groundAnchor.position;
boss.Agent.enabled = true;
boss.Anim.SetTrigger("Land");
}
public void UpdateState()
{
timer -= Time.deltaTime;
shotTimer -= Time.deltaTime;
if (shotTimer <= 0f)
{
ExecuteRandomWebAttack();
shotTimer = shotInterval;
}
// 時間が来たら、地上へ復帰
if (timer <= 0f)
{
boss.ChangeState<SpiderChaseState>();
}
}
private void ExecuteRandomWebAttack()
{
// ボスの中央(地上座標)を基準に、ランダムな円内に糸を降らせる
Vector2 randomCircle = Random.insideUnitCircle * boss.attackRadius;
Vector3 targetGroundPos = new Vector3(
boss.groundAnchor.position.x + randomCircle.x,
boss.groundAnchor.position.y,
boss.groundAnchor.position.z + randomCircle.y
);
// 予兆を出し、1.0秒後に2.5秒間残る糸を生成する
boss.SpawnWarningAndWeb(targetGroundPos, 1.0f, 2.5f);
}
}
// ==========================================
// ■ STATE: STUN (チャンスタイム - パリィ成功時)
// ==========================================
public class SpiderStunState : ISpiderState
{
private BossController boss;
private float stunTimer;
public SpiderStunState(BossController owner) => boss = owner;
public void EnterState()
{
stunTimer = 4.0f; // 4秒間の完全な無防備状態チャンスタイム
boss.Anim.SetTrigger("StunStart");
boss.Anim.SetBool("IsStunned", true);
if (boss.Agent.enabled) boss.Agent.isStopped = true;
Debug.Log("🛡️ パリィ成功! 蜘蛛ボスがひっくり返ってスタン中! チャンスタイム!");
}
public void ExitState()
{
boss.Anim.SetBool("IsStunned", false);
if (boss.Agent.enabled) boss.Agent.isStopped = false;
}
public void UpdateState()
{
stunTimer -= Time.deltaTime;
if (stunTimer <= 0f)
{
// スンが明けたら、もしHPトリガーが裏で引かれていれば優先して天井へ
if (boss.PendingUltimate) boss.ChangeState<SpiderJumpToCeilingState>();
else boss.ChangeState<SpiderChaseState>();
}
}
}