89 lines
3.2 KiB
C#
89 lines
3.2 KiB
C#
using System.Collections;
|
||
using Animancer;
|
||
using UnityEngine;
|
||
|
||
namespace BaseGames.Enemies.Abilities
|
||
{
|
||
/// <summary>
|
||
/// 带动画的天花板跌落能力:播放 Fall 动画 + 切换物理体为 Dynamic 自由下落,落地后启用接触伤害并恢复巡逻。
|
||
/// 取代 sealed 的 CeilingDropAbility,动画所有权完整封装在本能力中。
|
||
/// 可复用于任何需要从天花板落下并造成接触伤害的敌人。
|
||
/// </summary>
|
||
[RequireComponent(typeof(Rigidbody2D))]
|
||
public class AnimatedCeilingDropAbility : EnemyAbilityBase
|
||
{
|
||
[Header("动画")]
|
||
[Tooltip("下落循环动画(旋转下落姿态,循环播放直到落地)")]
|
||
[SerializeField] private ClipTransition _fallLoopClip;
|
||
|
||
[Header("物理下落")]
|
||
[Tooltip("切换 Dynamic 后使用的重力倍率")]
|
||
[SerializeField] private float _fallGravityScale = 3.5f;
|
||
[Tooltip("下落超时保护(秒),超时后强制继续执行")]
|
||
[SerializeField] private float _maxFallTime = 3f;
|
||
[SerializeField] private LayerMask _groundMask;
|
||
|
||
[Header("落地后")]
|
||
[Tooltip("落地后恢复帧延迟(秒),之后切换为 Patrol 阶段")]
|
||
[SerializeField] private float _recoveryTime = 0.1f;
|
||
[SerializeField] private BodyContactDamage _contactDamage;
|
||
|
||
private Rigidbody2D _rb;
|
||
|
||
protected override void Awake()
|
||
{
|
||
base.Awake();
|
||
_rb = GetComponentInParent<Rigidbody2D>();
|
||
}
|
||
|
||
protected override IEnumerator ExecuteCoroutine()
|
||
{
|
||
Phase = AbilityRunState.Active;
|
||
|
||
// 播放下落动画(能力脚本负责动画所有权)
|
||
if (_fallLoopClip.Clip != null)
|
||
_animancer.Play(_fallLoopClip);
|
||
|
||
// 切换物理:Kinematic → Dynamic + 重力
|
||
var origBodyType = _rb.bodyType;
|
||
var origGravScale = _rb.gravityScale;
|
||
_rb.bodyType = RigidbodyType2D.Dynamic;
|
||
_rb.gravityScale = _fallGravityScale;
|
||
_rb.velocity = Vector2.zero;
|
||
|
||
// 等待落地(超时保护)
|
||
float elapsed = 0f;
|
||
while (elapsed < _maxFallTime)
|
||
{
|
||
elapsed += Time.fixedDeltaTime;
|
||
yield return new WaitForFixedUpdate();
|
||
if (elapsed > 0.05f && IsGrounded()) break;
|
||
}
|
||
_rb.velocity = Vector2.zero;
|
||
|
||
// 落地后启用接触伤害
|
||
if (_contactDamage != null)
|
||
_contactDamage.enabled = true;
|
||
|
||
yield return EnemyAbilityWaits.Get(_recoveryTime);
|
||
|
||
// SetAiPhase(Patrol) 自动播放 AnimConfig.Walk(地面移动动画)
|
||
_enemy.SetAiPhase(AiPhase.Patrol);
|
||
}
|
||
|
||
private bool IsGrounded()
|
||
{
|
||
var hit = Physics2D.Raycast(_rb.position, Vector2.down, 0.6f, _groundMask);
|
||
return hit.collider != null;
|
||
}
|
||
|
||
protected override void OnInterrupted(InterruptReason reason)
|
||
{
|
||
if (_rb != null)
|
||
_rb.velocity = Vector2.zero;
|
||
if (_contactDamage != null)
|
||
_contactDamage.enabled = false;
|
||
}
|
||
}
|
||
}
|