如何在Unity3D中使用参数更新Tweener?

问题描述

我想通过DoTween的DOMoveY快捷方式上下移动2D敌人太空飞船的某个部分。到目前为止,效果很好-几乎可以正常工作...但是当我尝试在游戏过程中更改transitionTarget的值时,动画不会相应地发生变化。

所以问题似乎是代码没有更新Tweener。现在,我想知道我需要更改什么代码,以便当我在玩游戏时(通过检查器)更改transitionTarget的值时,Tweener得以更新?

这是代码

public float transitionTarget = 0f;

private Tweener transitionTweener;
private bool toggleTransition = true;

void Start()
{
    TransitionTween(transitionTarget);

    transitionTweener.OnRewind(() => {
        Debug.Log("<<- Transition played backward and completed!");
        toggleTransition = true;
    });

    transitionTweener.OnComplete(() => {
        Debug.Log("->> Transition played and completed!");
        if (toggleTransition) toggleTransition = false;
        else toggleTransition = true;
    });
}

void TransitionTween(float targetY) {
    transitionTweener = this.transform.DOMoveY(targetY,3f,false)
    .SetEase(Ease.Linear)
    .SetAutoKill(false)
    .SetId("tran")
    .Pause();
}

void Update() {
    if (toggleTransition) {
        transitionTweener.PlayForward();
    }
    else {
        transitionTweener.PlayBackwards();
    }
}

解决方法

解决方案 1: 假设您在动画周期开始时让更新的 transitionTarget 生效就足够了,那么您的问题有一个简单的解决方案 使用 ChangeEndValue 方法。

此外,我建议简化: 而不是手动倒带补间,我建议你使用

SetLoops(-1,LoopType.Yoyo)

使用 OnStepCompleted 回调可以实现相同的控制量。

public class EnemySpaceship : MonoBehaviour {
    private Tweener transitionTweener;
    public float transitionTarget = 0f;

    private Vector3 Endvalue => new Vector3(0.0f,transitionTarget,0.0f);
    
    void Start()
    {
        transitionTweener = transform.DOMove(Endvalue,3f,false)
            .SetOptions(AxisConstraint.Y)
            .SetEase(Ease.Linear)
            .SetLoops(-1,LoopType.Yoyo)
            .SetAutoKill(false);

        transitionTweener.OnStepComplete(OnStepCompleted);
    }

    private void OnStepCompleted() {
        if (transitionTweener.CompletedLoops() % 2 == 0) {
            Debug.Log("->> Transition played and completed!");
            transitionTweener.ChangeEndValue(Endvalue);            
        } else {
            Debug.Log("<<- Transition played backward and completed!");
        }
    }

}

解决方案 2: 如果您确实需要动画在动画仍在播放时立即使用更新的目标值,那么我们需要更多说明。 对于 transitionTarget 值的变化时间,请考虑以下情况:

  1. 在从起始位置到 transitionTarget 的过渡期间。
    1. 尚未达到新的 transitionTarget
    2. 已超过新的 transitionTarget
  2. 在从 transitionTarget 返回到起始位置的过渡期间。

我将完全忽略第 2 种情况,因为无论如何飞船都必须返回起始位置,而且该位置永远不会改变 - 所以在这个解决方案中,就像第一个一样,更改只会生效,只要到达起始位置时循环结束。

关于1.,我想我们通常可以假设,飞船的运动应该是连续的,所以我们将避免任何形式的“传送”。

不幸的是,ChangeEndValue 方法会倒带补间。 因此,如果我们想在案例 1.1 中使用这种方法,我们将不得不手动“擦洗”补间到正确的位置,这可以通过 Goto 方法实现。 但是,Goto 方法似乎无法与无限循环 (which should be reported as issue) 一起正常工作。该解决方案将只执行一个循环,仍然使用 LoopType.Yoyo(循环次数必须为 2 才能来回一次),然后重新启动补间(这更接近于示例代码中的问题)。

在 1.2 的情况下,我们只是及时向前移动到位置,在那里我们移回起始位置,而我们在同一位置,即 2 * duration - transitionTweener.position

不用多说,解决方案:

public class EnemySpaceship : MonoBehaviour {
    private Tweener transitionTweener;
    public float transitionTarget = 0f;
    private float lastValue;
    private Vector3 startValue;

    private Vector3 Endvalue => new Vector3(0.0f,0.0f);
    private const float duration = 3f;
    
    void Start() {
        lastValue = transitionTarget;
        startValue = transform.position;
        transitionTweener = transform.DOMove(Endvalue,duration)
            .SetOptions(AxisConstraint.Y)
            .SetEase(Ease.Linear)
            .SetLoops(2,LoopType.Yoyo)
            .SetAutoKill(false);

        transitionTweener.OnComplete(RestartTween);
    }

    private void RestartTween() {
        transitionTweener.ChangeEndValue(Endvalue);
        transitionTweener.Restart();
    }


    private void Update() {
        if (lastValue != transitionTarget && transitionTweener.CompletedLoops() == 0) {
            var t = duration * (transform.position.y - startValue.y) / (transitionTarget - startValue.y);
            if (t > duration) {
                transitionTweener.Goto(2 * duration - transitionTweener.position,true);
            } else {
                transitionTweener.ChangeEndValue(Endvalue);
                transitionTweener.Goto(t,true);
            }
            lastValue = transitionTarget;
        }
    }
}

注意: 为了符合您的规范,即当 transitionTarget 从检查器中更改时补间应该更新,解决方案将 lastValuetransitionTarget 在更新方法中的每一帧进行比较。 为了在实际应用中避免这种情况,您可能可以执行以下操作:

  • 将更新方法重命名为 UpdateTarget 之类的名称,以便您手动触发它。
  • 使用属性封装transitionTarget并使用属性setter调用UpdateTarget
  • 在补间完成时另外调用 UpdateTargettransitionTweener.OnComplete(RestartTween);

本答案中未考虑的另一种选择是在 transitionTarget 发生更改时重新创建补间。

,

比 IARI 更简单的解决方案(但可能效率较低)是在您需要转换并使用新目标创建新补间时终止当前补间。