190 lines
6.2 KiB
C#
190 lines
6.2 KiB
C#
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);
|
||
}
|
||
}
|