Files
zeling_v2/Assets/_Game/Scripts/Enemies/Abilities/AnimatedCeilingDropAbility.cs

89 lines
3.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.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;
}
}
}