Files
FutureMen2/Assets/_FutureMen2/Scripts/Enemy/Boss/SwordProjectile.cs
2026-01-05 11:00:50 +08:00

190 lines
6.2 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 System;
using DragonLi.Frame;
using UnityEngine;
public class SwordProjectile : MonoBehaviour
{
public enum State
{
Idle,
FlyingOut,
Grounded,
Returning
}
[Header("飞行参数")]
public float flySpeed = 15f;
[Tooltip("落地后停留时间")]
public float stayDuration = 2f;
[Tooltip("玩家被击中半径,不使用物理碰撞")]
private float hitRadius = 3f;
[Tooltip("落地方向距离的高度偏移(可让剑从空中稍微高出)")]
public float heightOffset = 0f;
[Header("伤害")]
public float damageAmount = 15f;
private State currentState = State.Idle;
private Vector3 startPosition; // BOSS 背部原始位置
private Vector3 targetPosition; // 本次飞行的地面目标位置
private float groundY; // 地面高度 (scene 中地面 Y 坐标)
private float stayTimer = 0f;
private bool hasHitPlayerThisPhase = false;
private Transform playerTransform;
// 返回完成时的回调事件(由 BossController 订阅,用于结束投掷动画)
public event Action OnReturnComplete;
/// <summary>
/// 由 BossController 实例化后调用,用来设置飞行目标并开始飞行
/// </summary>
/// <param name="player">玩家 Transform</param>
/// <param name="bossBackPoint">BOSS 背部挂点 Transform</param>
/// <param name="flySpd">飞行速度</param>
/// <param name="groundYPos">地面 Y 坐标</param>
/// <param name="stayTime">落地停留时间</param>
/// <param name="radius">击中玩家距离阈值</param>
/// <param name="dmg">伤害值</param>
public void Initialize(Transform player,
Transform bossBackPoint,
float flySpd,
float groundYPos,
float stayTime,
float radius,
float dmg)
{
playerTransform = player;
flySpeed = flySpd;
groundY = groundYPos;
stayDuration = stayTime;
hitRadius = radius;
damageAmount = dmg;
currentState = State.FlyingOut;
hasHitPlayerThisPhase = false;
stayTimer = 0f;
// 记录起始位置Boss 背部挂点)
startPosition = bossBackPoint.position;
// 目标落点:在玩家当前位置正上方,附加 heightOffset
Vector3 playerPos = playerTransform != null ? playerTransform.position : Vector3.zero;
targetPosition = new Vector3(playerPos.x, groundY, playerPos.z);
// 让剑从稍微高于背部位置处起飞(视觉效果可调整)
transform.position = startPosition + Vector3.up * heightOffset;
}
private void Update()
{
switch (currentState)
{
case State.FlyingOut:
FlyOut();
break;
case State.Grounded:
HandleGrounded();
break;
case State.Returning:
FlyBack();
break;
}
}
/// <summary>
/// 飞出阶段:朝 targetPosition 飞行
/// </summary>
private void FlyOut()
{
Vector3 dir = (targetPosition - transform.position).normalized;
transform.position += dir * flySpeed * Time.deltaTime;
// 飞行途中持续检测与玩家距离,若小于 hitRadius 且本阶段未曾击中,则造成一次伤害
if (!hasHitPlayerThisPhase && playerTransform != null)
{
float sqrDist = (transform.position - playerTransform.position).sqrMagnitude;
if (sqrDist <= hitRadius * hitRadius)
{
HitPlayer();
hasHitPlayerThisPhase = true;
}
}
// 到达或跌落到 groundY 时,进入 Grounded 阶段
if (transform.position.y <= groundY)
{
// 瞬间把 Y 设为 groundY
Vector3 pos = transform.position;
pos.y = groundY;
transform.position = pos;
currentState = State.Grounded;
hasHitPlayerThisPhase = false; // 重置,本阶段可再击中一次
stayTimer = 0f;
}
transform.LookAt(playerTransform.position);
}
/// <summary>
/// 落地停留阶段
/// </summary>
private void HandleGrounded()
{
stayTimer += Time.deltaTime;
// 停留阶段再次检测“玩家距离”,若小于阈值且尚未击中本阶段,则造成伤害
if (!hasHitPlayerThisPhase && playerTransform != null)
{
float sqrDist = (transform.position - playerTransform.position).sqrMagnitude;
if (sqrDist <= hitRadius * hitRadius)
{
HitPlayer();
hasHitPlayerThisPhase = true;
}
}
// 停留时长到,进入返回阶段
if (stayTimer >= stayDuration)
{
currentState = State.Returning;
hasHitPlayerThisPhase = false;
}
}
/// <summary>
/// 返回阶段:朝 startPosition 上方飞行 (保留 heightOffset)
/// 到达后触发 OnReturnComplete销毁自身
/// </summary>
private void FlyBack()
{
// 目标为背部挂点上方相同高度
Vector3 returnTarget = startPosition + Vector3.up * heightOffset;
Vector3 dir = (returnTarget - transform.position).normalized;
transform.position += dir * flySpeed * Time.deltaTime;
if (!hasHitPlayerThisPhase && playerTransform != null)
{
float sqrDist = (transform.position - playerTransform.position).sqrMagnitude;
if (sqrDist <= hitRadius * hitRadius)
{
HitPlayer();
hasHitPlayerThisPhase = true;
}
}
// 检测是否到达“返回目标”足够近
if ((transform.position - returnTarget).sqrMagnitude <= 0.01f)
{
OnReturnComplete?.Invoke();
Destroy(gameObject);
}
}
private void HitPlayer()
{
var ph = playerTransform.GetComponent<IDamagable>();
if (ph != null)
ph.ApplyDamage(damageAmount,null,transform);
}
}