using System.Collections; using Animancer; using UnityEngine; namespace BaseGames.Enemies.Abilities { /// /// 带动画的天花板跌落能力:播放 Fall 动画 + 切换物理体为 Dynamic 自由下落,落地后启用接触伤害并恢复巡逻。 /// 取代 sealed 的 CeilingDropAbility,动画所有权完整封装在本能力中。 /// 可复用于任何需要从天花板落下并造成接触伤害的敌人。 /// [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(); } 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; } } }