问题描述
我想通过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
值的变化时间,请考虑以下情况:
- 在从起始位置到
transitionTarget
的过渡期间。- 尚未达到新的
transitionTarget
值 - 已超过新的
transitionTarget
值
- 尚未达到新的
- 在从 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
从检查器中更改时补间应该更新,解决方案将 lastValue
与 transitionTarget
在更新方法中的每一帧进行比较。
为了在实际应用中避免这种情况,您可能可以执行以下操作:
- 将更新方法重命名为
UpdateTarget
之类的名称,以便您手动触发它。 - 使用属性封装
transitionTarget
并使用属性setter调用UpdateTarget
- 在补间完成时另外调用
UpdateTarget
:transitionTweener.OnComplete(RestartTween);
†本答案中未考虑的另一种选择是在 transitionTarget
发生更改时重新创建补间。
比 IARI 更简单的解决方案(但可能效率较低)是在您需要转换并使用新目标创建新补间时终止当前补间。