1
0
mirror of https://github.com/Cardidi/dotween-upm-fork.git synced 2025-12-20 09:16:02 +08:00
2015-03-25 16:24:39 +01:00

316 lines
16 KiB
C#

// Author: Daniele Giardini - http://www.demigiant.com
// Created: 2014/07/15 17:50
//
// License Copyright (c) Daniele Giardini.
// This work is subject to the terms at http://dotween.demigiant.com/license.php
using System;
using System.Collections.Generic;
using DG.Tweening.Core;
using DG.Tweening.Core.Easing;
using DG.Tweening.Core.Enums;
using UnityEngine;
namespace DG.Tweening
{
/// <summary>
/// Controls other tweens as a group
/// </summary>
public sealed class Sequence : Tween
{
// SETUP DATA ////////////////////////////////////////////////
internal readonly List<Tween> sequencedTweens = new List<Tween>(); // Only Tweens (used for despawning and validation)
readonly List<ABSSequentiable> _sequencedObjs = new List<ABSSequentiable>(); // Tweens plus SequenceCallbacks
internal float lastTweenInsertTime; // Used to insert a tween at the position of the previous one
#region Constructor
internal Sequence()
{
tweenType = TweenType.Sequence;
Reset();
}
#endregion
#region Creation Methods
internal static Sequence DoPrepend(Sequence inSequence, Tween t)
{
if (t.loops == -1) t.loops = 1;
float tFullTime = t.delay + (t.duration * t.loops);
inSequence.duration += tFullTime;
int len = inSequence._sequencedObjs.Count;
for (int i = 0; i < len; ++i) {
ABSSequentiable sequentiable = inSequence._sequencedObjs[i];
sequentiable.sequencedPosition += tFullTime;
sequentiable.sequencedEndPosition += tFullTime;
}
return DoInsert(inSequence, t, 0);
}
internal static Sequence DoInsert(Sequence inSequence, Tween t, float atPosition)
{
TweenManager.AddActiveTweenToSequence(t);
// If t has a delay add it as an interval
atPosition += t.delay;
inSequence.lastTweenInsertTime = atPosition;
t.isSequenced = t.creationLocked = true;
t.sequenceParent = inSequence;
if (t.loops == -1) t.loops = 1;
float tFullTime = t.duration * t.loops;
t.autoKill = false;
t.delay = t.elapsedDelay = 0;
t.delayComplete = true;
t.isSpeedBased = false;
t.sequencedPosition = atPosition;
t.sequencedEndPosition = atPosition + tFullTime;
if (t.sequencedEndPosition > inSequence.duration) inSequence.duration = t.sequencedEndPosition;
inSequence._sequencedObjs.Add(t);
inSequence.sequencedTweens.Add(t);
return inSequence;
}
internal static Sequence DoAppendInterval(Sequence inSequence, float interval)
{
inSequence.duration += interval;
return inSequence;
}
internal static Sequence DoPrependInterval(Sequence inSequence, float interval)
{
inSequence.duration += interval;
int len = inSequence._sequencedObjs.Count;
for (int i = 0; i < len; ++i) {
ABSSequentiable sequentiable = inSequence._sequencedObjs[i];
sequentiable.sequencedPosition += interval;
sequentiable.sequencedEndPosition += interval;
}
return inSequence;
}
internal static Sequence DoInsertCallback(Sequence inSequence, TweenCallback callback, float atPosition)
{
SequenceCallback c = new SequenceCallback(atPosition, callback);
c.sequencedPosition = c.sequencedEndPosition = atPosition;
inSequence._sequencedObjs.Add(c);
if (inSequence.duration < atPosition) inSequence.duration = atPosition;
return inSequence;
}
#endregion
internal override void Reset()
{
base.Reset();
sequencedTweens.Clear();
_sequencedObjs.Clear();
lastTweenInsertTime = 0;
}
// Called by TweenManager.Validate.
// Returns TRUE if the tween is valid
internal override bool Validate()
{
int len = sequencedTweens.Count;
for (int i = 0; i < len; i++) {
if (!sequencedTweens[i].Validate()) return false;
}
return true;
}
// CALLED BY Tween the moment the tween starts.
// Returns TRUE in case of success
internal override bool Startup()
{
return DoStartup(this);
}
internal override bool ApplyTween(float prevPosition, int prevCompletedLoops, int newCompletedSteps, bool useInversePosition, UpdateMode updateMode)
{
return DoApplyTween(this, prevPosition, prevCompletedLoops, newCompletedSteps, useInversePosition, updateMode);
}
// Called by DOTween when spawning/creating a new Sequence.
internal static void Setup(Sequence s)
{
s.autoKill = DOTween.defaultAutoKill;
s.isRecyclable = DOTween.defaultRecyclable;
s.isPlaying = DOTween.defaultAutoPlay == AutoPlay.All || DOTween.defaultAutoPlay == AutoPlay.AutoPlaySequences;
s.loopType = DOTween.defaultLoopType;
s.easeType = Ease.Linear;
s.easeOvershootOrAmplitude = DOTween.defaultEaseOvershootOrAmplitude;
s.easePeriod = DOTween.defaultEasePeriod;
}
// Returns TRUE in case of success
internal static bool DoStartup(Sequence s)
{
if (s.sequencedTweens.Count == 0 && s._sequencedObjs.Count == 0
&& s.onComplete == null && s.onKill == null && s.onPause == null && s.onPlay == null && s.onRewind == null
&& s.onStart == null && s.onStepComplete == null && s.onUpdate == null
) return false; // Empty Sequence without any callback set
s.startupDone = true;
s.fullDuration = s.loops > -1 ? s.duration * s.loops : Mathf.Infinity;
// Order sequencedObjs by start position
s._sequencedObjs.Sort(SortSequencedObjs);
return true;
}
// Applies the tween set by DoGoto.
// Returns TRUE if the tween needs to be killed
internal static bool DoApplyTween(Sequence s, float prevPosition, int prevCompletedLoops, int newCompletedSteps, bool useInversePosition, UpdateMode updateMode)
{
// Adapt to eventual ease position
float prevPos = prevPosition;
float newPos = s.position;
if (s.easeType != Ease.Linear) {
prevPos = s.duration * EaseManager.Evaluate(s.easeType, s.customEase, prevPos, s.duration, s.easeOvershootOrAmplitude, s.easePeriod);
newPos = s.duration * EaseManager.Evaluate(s.easeType, s.customEase, newPos, s.duration, s.easeOvershootOrAmplitude, s.easePeriod);
}
float from, to = 0;
// Determine if prevPos was inverse.
// Used to calculate correct "from" value when applying internal cycle
// and also in case of multiple loops within a single update
bool prevPosIsInverse = s.loopType == LoopType.Yoyo
&& (prevPos < s.duration ? prevCompletedLoops % 2 != 0 : prevCompletedLoops % 2 == 0);
if (s.isBackwards) prevPosIsInverse = !prevPosIsInverse;
// Update multiple loop cycles within the same update
if (newCompletedSteps > 0) {
// Store expected completedLoops and position, in order to check them after the update cycles.
int expectedCompletedLoops = s.completedLoops + newCompletedSteps;
float expectedPosition = s.position;
//
int cycles = newCompletedSteps;
int cyclesDone = 0;
from = prevPos;
if (updateMode == UpdateMode.Update) {
// Run all cycles elapsed since last update
while (cyclesDone < cycles) {
if (cyclesDone > 0) from = to;
else if (prevPosIsInverse && !s.isBackwards) from = s.duration - from;
to = prevPosIsInverse ? 0 : s.duration;
if (ApplyInternalCycle(s, from, to, updateMode, useInversePosition, prevPosIsInverse, true)) return true;
cyclesDone++;
if (s.loopType == LoopType.Yoyo) prevPosIsInverse = !prevPosIsInverse;
}
// If completedLoops or position were changed by some callback, exit here
if (expectedCompletedLoops != s.completedLoops || Math.Abs(expectedPosition - s.position) > Single.Epsilon) return !s.active;
} else {
// Simply determine correct prevPosition after steps
if (s.loopType == LoopType.Yoyo && newCompletedSteps % 2 != 0) {
prevPosIsInverse = !prevPosIsInverse;
prevPos = s.duration - prevPos;
}
newCompletedSteps = 0;
}
}
// Run current cycle
if (newCompletedSteps == 1 && s.isComplete) return false; // Skip update if complete because multicycle took care of it
if (newCompletedSteps > 0 && !s.isComplete) {
from = useInversePosition ? s.duration : 0;
// In case of Restart loop rewind all tweens (keep "to > 0" or remove it?)
if (s.loopType == LoopType.Restart && to > 0) ApplyInternalCycle(s, s.duration, 0, UpdateMode.Goto, false, false, false);
} else from = useInversePosition ? s.duration - prevPos : prevPos;
return ApplyInternalCycle(s, from, useInversePosition ? s.duration - newPos : newPos, updateMode, useInversePosition, prevPosIsInverse);
}
// ===================================================================================
// METHODS ---------------------------------------------------------------------------
// Returns TRUE if the tween needs to be killed
static bool ApplyInternalCycle(Sequence s, float fromPos, float toPos, UpdateMode updateMode, bool useInverse, bool prevPosIsInverse, bool multiCycleStep = false)
{
bool isBackwardsUpdate = toPos < fromPos;
// Debug.Log(Time.frameCount + " " + s.id + " " + (multiCycleStep ? "<color=#FFEC03>Multicycle</color> > " : "Cycle > ") + s.position + "/" + s.duration + " - s.isBackwards: " + s.isBackwards + ", useInverse/prevInverse: " + useInverse + "/" + prevPosIsInverse + " - " + fromPos + " > " + toPos + " - UpdateMode: " + updateMode + ", isPlaying: " + s.isPlaying);
if (isBackwardsUpdate) {
int len = s._sequencedObjs.Count - 1;
for (int i = len; i > -1; --i) {
if (!s.active) return true; // Killed by some internal callback
ABSSequentiable sequentiable = s._sequencedObjs[i];
if (sequentiable.sequencedEndPosition < toPos || sequentiable.sequencedPosition > fromPos) continue;
if (sequentiable.tweenType == TweenType.Callback) {
if (updateMode == UpdateMode.Update && prevPosIsInverse) {
// Debug.Log("<color=#FFEC03>BACKWARDS Callback > " + s.id + " - s.isBackwards: " + s.isBackwards + ", useInverse/prevInverse: " + useInverse + "/" + prevPosIsInverse + " - " + fromPos + " > " + toPos + "</color>");
OnTweenCallback(sequentiable.onStart);
}
} else {
// Nested Tweener/Sequence
float gotoPos = toPos - sequentiable.sequencedPosition;
if (gotoPos < 0) gotoPos = 0;
Tween t = (Tween)sequentiable;
if (!t.startupDone) continue; // since we're going backwards and this tween never started just ignore it
t.isBackwards = true;
if (TweenManager.Goto(t, gotoPos, false, updateMode)) return true;
// Fixes nested callbacks not being called correctly if main sequence has loops and nested ones don't
if (multiCycleStep && t.tweenType == TweenType.Sequence) {
if (s.position <= 0 && s.completedLoops == 0) t.position = 0;
else {
bool toZero = s.completedLoops == 0 || s.isBackwards && (s.completedLoops < s.loops || s.loops == -1);
if (t.isBackwards) toZero = !toZero;
if (useInverse) toZero = !toZero;
if (s.isBackwards && !useInverse && !prevPosIsInverse) toZero = !toZero;
t.position = toZero ? 0 : t.duration;
}
}
}
}
} else {
// Debug
int len = s._sequencedObjs.Count;
for (int i = 0; i < len; ++i) {
if (!s.active) return true; // Killed by some internal callback
ABSSequentiable sequentiable = s._sequencedObjs[i];
if (sequentiable.sequencedPosition > toPos || sequentiable.sequencedEndPosition < fromPos) continue;
if (sequentiable.tweenType == TweenType.Callback) {
if (updateMode == UpdateMode.Update) {
// Debug.Log("<color=#FFEC03>FORWARD Callback > " + s.id + " - s.isBackwards: " + s.isBackwards + ", useInverse/prevInverse: " + useInverse + "/" + prevPosIsInverse + " - " + fromPos + " > " + toPos + "</color>");
bool fire = !s.isBackwards && !useInverse && !prevPosIsInverse
|| s.isBackwards && useInverse && !prevPosIsInverse;
if (fire) OnTweenCallback(sequentiable.onStart);
}
} else {
// Nested Tweener/Sequence
float gotoPos = toPos - sequentiable.sequencedPosition;
if (gotoPos < 0) gotoPos = 0;
Tween t = (Tween)sequentiable;
t.isBackwards = false;
if (TweenManager.Goto(t, gotoPos, false, updateMode)) return true;
// Fixes nested callbacks not being called correctly if main sequence has loops and nested ones don't
if (multiCycleStep && t.tweenType == TweenType.Sequence) {
if (s.position <= 0 && s.completedLoops == 0) t.position = 0;
else {
bool toZero = s.completedLoops == 0 || !s.isBackwards && (s.completedLoops < s.loops || s.loops == -1);
if (t.isBackwards) toZero = !toZero;
if (useInverse) toZero = !toZero;
if (s.isBackwards && !useInverse && !prevPosIsInverse) toZero = !toZero;
t.position = toZero ? 0 : t.duration;
}
}
}
}
}
return false;
}
static int SortSequencedObjs(ABSSequentiable a, ABSSequentiable b)
{
if (a.sequencedPosition > b.sequencedPosition) return 1;
if (a.sequencedPosition < b.sequencedPosition) return -1;
return 0;
}
}
}