using System.Collections; using Animancer; using BaseGames.AI; using UnityEngine; namespace BaseGames.Enemies.Abilities { /// /// 带动画的天花板跌落能力:播放 Fall 动画 + 切换物理体为 Dynamic 自由下落,落地后恢复巡逻。 /// 取代 sealed 的 CeilingDropAbility,动画所有权完整封装在本能力中。 /// 可复用于任何需要从天花板落下的敌人。 /// 身体接触伤害与本能力解耦:由常驻的 全程负责,本能力不再开关它。 /// [RequireComponent(typeof(Rigidbody2D))] public class AnimatedCeilingDropAbility : EnemyAbilityBase { private AnimatedCeilingDropAbilitySO _cfg; private Rigidbody2D _rb; protected override void Awake() { base.Awake(); _cfg = ResolveConfig(); _rb = GetComponentInParent(); } protected override IEnumerator ExecuteCoroutine() { if (_cfg == null) yield break; Phase = AbilityRunState.Active; // 播放下落动画(能力脚本负责动画所有权) if (_cfg.fallLoopClip.Clip != null) _animancer.Play(_cfg.fallLoopClip); // 切换物理:Kinematic → Dynamic + 重力 var origBodyType = _rb.bodyType; var origGravScale = _rb.gravityScale; _rb.bodyType = RigidbodyType2D.Dynamic; _rb.gravityScale = _cfg.fallGravityScale; _rb.velocity = Vector2.zero; // 等待落地(超时保护) float elapsed = 0f; while (elapsed < _cfg.maxFallTime) { elapsed += Time.fixedDeltaTime; yield return new WaitForFixedUpdate(); if (elapsed > 0.05f && IsGrounded()) break; } _rb.velocity = Vector2.zero; // 身体接触伤害由常驻 BodyContactDamage 全程负责,落地无需在此开启。 yield return EnemyAbilityWaits.Get(_cfg.recoveryTime); // PlayLocomotionClip(Patrol) 播放 AnimConfig.Walk(地面移动动画) _enemy.PlayLocomotionClip(LocomotionMode.Patrol); } private bool IsGrounded() { var hit = Physics2D.Raycast(_rb.position, Vector2.down, 0.6f, _cfg.groundMask); return hit.collider != null; } protected override void OnInterrupted(InterruptReason reason) { if (_rb != null) _rb.velocity = Vector2.zero; } } }