using UnityEngine; using DG.Tweening; /// /// 控制天子看板战箱子(回血时)掉落和收集动画的组件。 /// 动画序列: 抛射弧形 -> 多次弹跳 -> 等待 -> 飞向收集点 -> 销毁 /// [RequireComponent(typeof(RectTransform))] public class TianziBillboradBox : MonoBehaviour { private RectTransform rectTransform; [Header("1. 抛射阶段 (从Boss位置)")] [SerializeField] [Tooltip("初始位置的X/Y偏移量(像素单位)。箱子将从[Boss位置 + 偏移]开始抛射。")] Vector2 initialPositionOffset = Vector2.zero; // NEW: 初始位置偏移 [SerializeField] [Tooltip("箱子抛射的持续时间(秒)。")] float throwDuration = 0.6f; [SerializeField] [Tooltip("抛射的缓动动画类型。\n" + "Ease.OutQuad: 模拟抛物线,开始快,落地慢。")] Ease throwEase = Ease.OutQuad; [SerializeField] [Tooltip("抛射的基础水平距离(像素单位)。")] float throwHorizontalDistance = 200f; [SerializeField] [Tooltip("抛射距离的随机变化范围(百分比)。")] [Range(0f, 1f)] float throwDistanceRandomness = 0.3f; [SerializeField] [Tooltip("抛射的弧形最高点,相对于起点的高度(像素单位)。")] float throwArcHeight = 150f; [SerializeField] [Tooltip("抛射落地的最小垂直距离(像素单位),即比起点低多少。")] float minThrowVerticalDrop = 120f; [SerializeField] [Tooltip("抛射落地的最大垂直距离(像素单位),即比起点低多少。")] float maxThrowVerticalDrop = 180f; [Header("2. 弹跳阶段")] [SerializeField] [Tooltip("弹跳次数(1-3次)。")] [Range(1, 3)] int bounceCount = 2; [SerializeField] [Tooltip("第一次弹跳的高度(像素单位)。")] float firstBounceHeight = 80f; [SerializeField] [Tooltip("第二次弹跳的高度(像素单位)。")] float secondBounceHeight = 50f; [SerializeField] [Tooltip("第三次弹跳的高度(像素单位)。")] float thirdBounceHeight = 30f; [SerializeField] [Tooltip("每次弹跳的基础水平距离(像素单位)。")] float bounceHorizontalDistance = 100f; [SerializeField] [Tooltip("弹跳距离的随机变化范围(百分比)。")] [Range(0f, 1f)] float bounceDistanceRandomness = 0.4f; [SerializeField] [Tooltip("每次弹跳的持续时间(秒)。")] float bounceDuration = 0.3f; [SerializeField] [Tooltip("弹跳的缓动动画类型。")] Ease bounceEase = Ease.OutQuad; [Header("3. 等待阶段")] [SerializeField] [Tooltip("弹跳停止后等待的时间(秒)。")] float waitAfterBounce = 0.5f; [Header("4. 收集移动阶段 (飞向终点)")] [SerializeField] [Tooltip("箱子从地面移动到最终收集位置(rectBoxEnd)所需的时间(秒)。")] float moveDuration = 0.4f; [SerializeField] [Tooltip("移动时的缓动动画类型。\n" + "Ease.InSine: 实现先慢后快的加速效果。")] Ease moveEase = Ease.InSine; // ---------------------------------------------------------------------- private void Awake() { rectTransform = GetComponent(); } /// /// 启动箱子动画序列 /// /// 动画开始的世界坐标(例如 Boss 头像位置) /// 动画结束的世界坐标(rectBoxEnd 的位置) public void StartAnimation(Vector3 startWorldPos, Vector3 endWorldPos) { /// Debug.Log($"TianziBillboradBox startWorldPos {startWorldPos} endWorldPos {endWorldPos}"); if (rectTransform == null) { rectTransform = GetComponent(); } // --- 0. 在开始动画前,将箱子移动到boss的父物体 --- Transform currentParent = transform.parent; if (currentParent != null) { Transform bossParent = currentParent.parent; // boss的父物体 if (bossParent != null) { transform.SetParent(bossParent); transform.localScale = Vector3.one; // 将scale设置为1 } } // --- 1. 计算并设置初始位置 (应用偏移) --- Vector3 actualStartPos = new Vector3( startWorldPos.x + initialPositionOffset.x / 100f, startWorldPos.y + initialPositionOffset.y / 100f, startWorldPos.z ); rectTransform.position = actualStartPos; // MODIFIED: 使用偏移后的位置 gameObject.SetActive(true); // --- 2. 随机选择抛射方向(左或右)和角度 --- bool throwRight = Random.Range(0, 2) == 1; float horizontalDirection = throwRight ? 1f : -1f; // 添加随机角度偏移 float angleOffset = Random.Range(-45f, 45f); float angleRad = angleOffset * Mathf.Deg2Rad; // 计算带随机性的抛射距离 float randomThrowDistance = throwHorizontalDistance * (1f + Random.Range(-throwDistanceRandomness, throwDistanceRandomness)); // --- 3. 计算抛射终点位置 --- //计算随机的垂直下落距离 float randomVerticalDrop = Random.Range(minThrowVerticalDrop, maxThrowVerticalDrop); Vector3 throwEndPos = new Vector3( actualStartPos.x + randomThrowDistance / 100 * Mathf.Cos(angleRad) * horizontalDirection, // MODIFIED actualStartPos.y - randomVerticalDrop / 100, // MODIFIED actualStartPos.z ); // --- 4. 计算抛射控制点(用于弧形轨迹)--- Vector3 controlPoint = new Vector3( actualStartPos.x + randomThrowDistance / 100 * Mathf.Cos(angleRad) * horizontalDirection * 0.5f, // MODIFIED actualStartPos.y + throwArcHeight / 100, // MODIFIED actualStartPos.z ); // --- 5. 创建一个动画序列 --- Sequence animSequence = DOTween.Sequence(); // --- 6. 抛射阶段(弧形轨迹)--- animSequence.Append( rectTransform.DOPath( new Vector3[] { actualStartPos, controlPoint, throwEndPos }, // MODIFIED throwDuration, PathType.CatmullRom ).SetEase(throwEase) ); // --- 7. 弹跳阶段(向抛射方向继续跳1-3次)--- Vector3 currentPos = throwEndPos; int actualBounceCount = Random.Range(1, bounceCount + 1); // 随机1-3次 for (int i = 0; i < actualBounceCount; i++) { // 计算当前弹跳的高度 float currentBounceHeight = i switch { 0 => firstBounceHeight, 1 => secondBounceHeight, _ => thirdBounceHeight }; // 计算带随机性的弹跳距离 float randomBounceDistance = bounceHorizontalDistance * (1f + Random.Range(-bounceDistanceRandomness, bounceDistanceRandomness)); // 计算弹跳终点位置(继续向抛射方向移动) Vector3 bounceEndPos = new Vector3( currentPos.x + randomBounceDistance / 100 * horizontalDirection, currentPos.y, currentPos.z ); // 弹跳控制点(弧形轨迹) Vector3 bounceControlPoint = new Vector3( currentPos.x + randomBounceDistance / 100 * horizontalDirection * 0.5f, currentPos.y + currentBounceHeight / 100, currentPos.z ); // 添加弹跳动画(弧形轨迹) animSequence.Append( rectTransform.DOPath( new Vector3[] { currentPos, bounceControlPoint, bounceEndPos }, bounceDuration, PathType.CatmullRom ).SetEase(bounceEase) ); currentPos = bounceEndPos; // 更新当前位置 } // --- 8. 等待阶段 --- animSequence.AppendInterval(waitAfterBounce); // --- 9. 移动到终点 --- animSequence.Append( rectTransform.DOMove(endWorldPos, moveDuration) .SetEase(moveEase) ); // --- 10. 动画播放完毕后销毁 --- animSequence.OnComplete(() => { if (this != null && gameObject != null) { TianziBillboradManager.Instance.PlayUiEffectAction?.Invoke(); Destroy(gameObject); } }); // --- 11. 关联生命周期 --- animSequence.SetTarget(this); } }