Executing a sequence of actions over time in Unity

Clash Royale CLAN TAG#URR8PPP
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;
up vote
1
down vote
favorite
This script executes a sequence of actions over time and is based on the methods exposed by the UrhoSharp framework. I am new to Unity, but well acquainted with C# so I know what I'm doing for the most part. I'm primarily concerned with performance. Is this how such problems are usually approached in Unity or can we do better?
Using the functions published here, I was able to make Vector3 interpolations work. I will be honest and admit I know nothing about quaternions so I just use Unity's built-in helper function for linear interpolations and throw an exception if some other interpolation is specified.
Ultimately, this solution aims to provide a proper framework for animating anything in a easy to understand way.
This is how you'd use it.
myGameObject.AddComponent<Animate>()
.Begin(Repeat.Forever,
new MoveTo(TimeSpan.FromSeconds(5), myGameObject.transform.position + new Vector3(15, 0, 0), Easing.EaseInBounce, TransformOrigin.World),
new DelayTime(TimeSpan.FromSeconds(3)),
new MoveTo(TimeSpan.FromSeconds(5), myGameObject.transform.position, Easing.EaseOutQuint, TransformOrigin.World),
new DelayTime(TimeSpan.FromSeconds(3)),
new MoveTo(TimeSpan.FromSeconds(5), myGameObject.transform.position - new Vector3(15, 0, 0), Easing.EaseInBack, TransformOrigin.World),
new DelayTime(TimeSpan.FromSeconds(3)),
new MoveTo(TimeSpan.FromSeconds(5), myGameObject.transform.position, Easing.Spring, TransformOrigin.World),
new DelayTime(TimeSpan.FromSeconds(3)));
Or to perform actions in parallel:
myGameObject.AddComponent<Animate>()
.Begin(Repeat.Forever, new Parallel(
new MoveTo(TimeSpan.FromSeconds(5), myGameObject.transform.position - new Vector3(0, 5, 0)),
new ScaleTo(TimeSpan.FromSeconds(5), Vector3.zero)));
And here is the code (not including the code here).
Animate:
public class Animate : MonoBehaviour
public const Repeat DefaultRepeat = Repeat.Once;
class ActionSequence
/// <summary>
/// The actions to perform.
/// </summary>
readonly ActionBase _actions;
/// <summary>
/// The current action.
/// </summary>
public ActionBase Current;
/// <summary>
/// The arguments to evaluate for the current action.
/// </summary>
public object CurrentArguments;
/// <summary>
/// The index of the current action.
/// </summary>
public uint CurrentIndex = 0;
/// <summary>
/// The "journey" of the current action.
/// </summary>
public float CurrentJourney = 0;
/// <summary>
/// Whether or not the current action has started.
/// </summary>
public bool CurrentStarted;
/// <summary>
/// The number of actions in the sequence.
/// </summary>
public readonly uint Length;
/// <summary>
/// The manner in which the sequence of actions should repeat.
/// </summary>
public Repeat Repeat;
public ActionSequence(Repeat repeat, ActionBase actions)
_actions = actions;
Current = _actions[0];
Length = Convert.ToUInt32(_actions.Length);
Repeat = repeat;
public ActionBase this[uint index]
get
return _actions[index];
public void Next()
CurrentArguments = null;
CurrentIndex++;
CurrentJourney = 0;
CurrentStarted = false;
public enum Repeat
Once,
Forever
List<ActionSequence> _sequences = new List<ActionSequence>();
void Update()
var count = _sequences.Count;
if (count == 0)
return;
var deltaTime = Time.deltaTime;
for (int i = 0; i < count; i++)
var sequence = _sequences[i];
if (sequence.Current.Duration <= TimeSpan.Zero)
goto Done;
if (!sequence.CurrentStarted)
sequence.CurrentArguments = sequence.Current.GetArguments(gameObject);
sequence.CurrentStarted = true;
if (sequence.CurrentJourney >= sequence.Current.Duration.TotalSeconds)
goto Done;
sequence.CurrentJourney += deltaTime;
sequence.Current.Update(gameObject, deltaTime, sequence.CurrentJourney, sequence.CurrentArguments);
continue;
//Is the action done?
Done:
//If so, get the next
sequence.Next();
if (sequence.Repeat == Repeat.Forever)
//If done, prepare for next iteration
if (sequence.CurrentIndex == sequence.Length)
sequence.CurrentIndex = 0;
//Get next action now so we don't have to on subsequent updates
sequence.Current = sequence[sequence.CurrentIndex];
else if (sequence.Repeat == Repeat.Once)
//If done, remove current sequence
if (sequence.CurrentIndex == sequence.Length)
_sequences.RemoveAt(i);
count--;
i--;
//Otherwise, get next action now so we don't have to on subsequent updates
else sequence.Current = sequence[sequence.CurrentIndex];
/// <summary>
/// Performs the given sequence of actions once.
/// </summary>
/// <param name="actions"></param>
public void Begin(params ActionBase actions)
Begin(DefaultRepeat, actions);
/// <summary>
/// Performs the given sequence of actions once or forever.
/// </summary>
/// <param name="repeat"></param>
/// <param name="actions"></param>
public void Begin(Repeat repeat, params ActionBase actions)
var sequence = new ActionSequence(repeat, actions);
_sequences.Add(sequence);
/// <summary>
/// Performs the given sequence of actions in parallel once.
/// </summary>
/// <param name="repeat"></param>
/// <param name="actions"></param>
public void Begin(Parallel parallel)
Begin(DefaultRepeat, parallel);
/// <summary>
/// Performs the given sequence of actions in parallel once or forever.
/// </summary>
/// <param name="repeat"></param>
/// <param name="parallel"></param>
public void Begin(Repeat repeat, Parallel parallel)
for (uint i = 0; i < parallel.Count; i++)
Begin(repeat, parallel[i]);
ActionBase + some base actions:
public abstract class ActionBase
protected readonly TimeSpan _duration;
public TimeSpan Duration
get
return _duration;
protected ActionBase(TimeSpan duration)
_duration = duration;
internal abstract object GetArguments(GameObject go);
internal abstract void Update(GameObject go, float deltaTime, float journey, object arguments);
public abstract class Interpolate<TValue> : ActionBase
protected readonly Easing _easing;
protected readonly TValue _target;
protected Interpolate(TimeSpan duration, TValue target, Easing easing) : base(duration)
_easing = easing;
_target = target;
protected float GetValue(Easing easing, float origin, float target, float percent)
switch (_easing)
case Easing.EaseInBack:
return EasingFunction.EaseInBack(origin, target, percent);
case Easing.EaseInBounce:
return EasingFunction.EaseInBounce(origin, target, percent);
case Easing.EaseInCirc:
return EasingFunction.EaseInCirc(origin, target, percent);
case Easing.EaseInCubic:
return EasingFunction.EaseInCubic(origin, target, percent);
case Easing.EaseInElastic:
return EasingFunction.EaseInElastic(origin, target, percent);
case Easing.EaseInExpo:
return EasingFunction.EaseInExpo(origin, target, percent);
case Easing.EaseInOutBack:
return EasingFunction.EaseInOutBack(origin, target, percent);
case Easing.EaseInOutBounce:
return EasingFunction.EaseInOutBounce(origin, target, percent);
case Easing.EaseInOutCirc:
return EasingFunction.EaseInOutCirc(origin, target, percent);
case Easing.EaseInOutCubic:
return EasingFunction.EaseInOutCubic(origin, target, percent);
case Easing.EaseInOutElastic:
return EasingFunction.EaseInOutElastic(origin, target, percent);
case Easing.EaseInOutExpo:
return EasingFunction.EaseInOutExpo(origin, target, percent);
case Easing.EaseInOutQuad:
return EasingFunction.EaseInOutQuad(origin, target, percent);
case Easing.EaseInOutQuart:
return EasingFunction.EaseInOutQuart(origin, target, percent);
case Easing.EaseInOutQuint:
return EasingFunction.EaseInOutQuint(origin, target, percent);
case Easing.EaseInOutSine:
return EasingFunction.EaseInOutSine(origin, target, percent);
case Easing.EaseInQuad:
return EasingFunction.EaseInQuad(origin, target, percent);
case Easing.EaseInQuart:
return EasingFunction.EaseInQuart(origin, target, percent);
case Easing.EaseInQuint:
return EasingFunction.EaseInQuint(origin, target, percent);
case Easing.EaseInSine:
return EasingFunction.EaseInSine(origin, target, percent);
case Easing.EaseOutBack:
return EasingFunction.EaseOutBack(origin, target, percent);
case Easing.EaseOutBounce:
return EasingFunction.EaseOutBounce(origin, target, percent);
case Easing.EaseOutCirc:
return EasingFunction.EaseOutCirc(origin, target, percent);
case Easing.EaseOutCubic:
return EasingFunction.EaseOutCubic(origin, target, percent);
case Easing.EaseOutElastic:
return EasingFunction.EaseOutElastic(origin, target, percent);
case Easing.EaseOutExpo:
return EasingFunction.EaseOutExpo(origin, target, percent);
case Easing.EaseOutQuad:
return EasingFunction.EaseOutQuad(origin, target, percent);
case Easing.EaseOutQuart:
return EasingFunction.EaseOutQuart(origin, target, percent);
case Easing.EaseOutQuint:
return EasingFunction.EaseOutQuint(origin, target, percent);
case Easing.EaseOutSine:
return EasingFunction.EaseOutSine(origin, target, percent);
case Easing.Linear:
return EasingFunction.Linear(origin, target, percent);
case Easing.Spring:
return EasingFunction.Spring(origin, target, percent);
throw new NotSupportedException();
protected abstract TValue GetValue(TValue origin, float percent);
protected abstract void SetValue(GameObject go, TValue value);
internal override void Update(GameObject go, float deltaTime, float journey, object arguments)
var origin = (TValue)arguments[0];
var percent = Mathf.Clamp01(journey / Convert.ToSingle(_duration.TotalSeconds));
var value = GetValue(origin, percent);
SetValue(go, value);
public abstract class InterpolateVector3 : Interpolate<Vector3>
protected InterpolateVector3(TimeSpan duration, Vector3 target, Easing easing) : base(duration, target, easing)
protected override Vector3 GetValue(Vector3 origin, float percent)
var value = new float[3];
for (var i = 0; i < 3; i++)
var origin_
= i == 0
? origin.x
: i == 1
? origin.y
: origin.z;
var target_
= i == 0
? _target.x
: i == 1
? _target.y
: _target.z;
value[i] = GetValue(_easing, origin_, target_, percent);
return new Vector3(value[0], value[1], value[2]);
public abstract class InterpolateVector4 : Interpolate<Vector4>
protected InterpolateVector4(TimeSpan duration, Vector3 target, Easing easing) : base(duration, target, easing)
protected override Vector4 GetValue(Vector4 origin, float percent)
var value = new float[4];
for (var i = 0; i < 4; i++)
var origin_
= i == 0
? origin.w
: i == 1
? origin.x
: i == 2
? origin.y
: origin.z;
var target_
= i == 0
? _target.w
: i == 1
? _target.x
: i == 2
? _target.y
: _target.z;
value[i] = GetValue(_easing, origin_, target_, percent);
return new Vector4(value[0], value[1], value[2], value[3]);
public abstract class InterpolateQuaternion : Interpolate<Quaternion>
protected InterpolateQuaternion(TimeSpan duration, Quaternion target, Easing easing) : base(duration, target, easing)
protected override Quaternion GetValue(Quaternion origin, float percent)
switch (_easing)
case Easing.Linear:
return Quaternion.Lerp(origin, _target, percent);
throw new NotSupportedException();
Some default actions:
public class DelayTime : ActionBase
public DelayTime(TimeSpan duration) : base(duration)
internal override object GetArguments(GameObject go)
return default(object);
internal override void Update(GameObject go, float deltaTime, float journey, object arguments)
public class MoveTo : InterpolateVector3
protected readonly TransformOrigin _transformOrigin;
public MoveTo(TimeSpan duration, Vector3 target, Easing easing = Easing.Linear, TransformOrigin transformOrigin = TransformOrigin.Local) : base(duration, target, easing)
_transformOrigin = transformOrigin;
internal override object GetArguments(GameObject go)
return new object
_transformOrigin == TransformOrigin.Local
? go.transform.localPosition
: go.transform.position
;
protected override void SetValue(GameObject go, Vector3 value)
switch (_transformOrigin)
case TransformOrigin.Local:
go.transform.localPosition = value;
break;
case TransformOrigin.World:
go.transform.position = value;
break;
public class RotateTo : InterpolateQuaternion
protected readonly TransformOrigin _transformOrigin;
public RotateTo(TimeSpan duration, Quaternion target, Easing easing = Easing.Linear, TransformOrigin transformOrigin = TransformOrigin.Local) : base(duration, target, easing)
_transformOrigin = transformOrigin;
internal override object GetArguments(GameObject go)
return new object
_transformOrigin == TransformOrigin.Local
? go.transform.localRotation
: go.transform.rotation
;
protected override void SetValue(GameObject go, Quaternion value)
switch (_transformOrigin)
case TransformOrigin.Local:
go.transform.localRotation = value;
break;
case TransformOrigin.World:
go.transform.rotation = value;
break;
public class ScaleTo : InterpolateVector3
public ScaleTo(TimeSpan duration, Vector3 target, Easing easing = Easing.Linear) : base(duration, target, easing)
internal override object GetArguments(GameObject go)
return new object
go.transform.localScale
;
protected override void SetValue(GameObject go, Vector3 value)
go.transform.localScale = value;
For executing actions in parallel:
public class Parallel
readonly ActionBase _actions;
public uint Count
get
return Convert.ToUInt32(_actions.Length);
public Parallel(params ActionBase actions)
_actions = actions;
public ActionBase this[uint index]
get
return _actions[index];
Other:
public enum TransformOrigin
Local,
World
c# unity3d
add a comment |Â
up vote
1
down vote
favorite
This script executes a sequence of actions over time and is based on the methods exposed by the UrhoSharp framework. I am new to Unity, but well acquainted with C# so I know what I'm doing for the most part. I'm primarily concerned with performance. Is this how such problems are usually approached in Unity or can we do better?
Using the functions published here, I was able to make Vector3 interpolations work. I will be honest and admit I know nothing about quaternions so I just use Unity's built-in helper function for linear interpolations and throw an exception if some other interpolation is specified.
Ultimately, this solution aims to provide a proper framework for animating anything in a easy to understand way.
This is how you'd use it.
myGameObject.AddComponent<Animate>()
.Begin(Repeat.Forever,
new MoveTo(TimeSpan.FromSeconds(5), myGameObject.transform.position + new Vector3(15, 0, 0), Easing.EaseInBounce, TransformOrigin.World),
new DelayTime(TimeSpan.FromSeconds(3)),
new MoveTo(TimeSpan.FromSeconds(5), myGameObject.transform.position, Easing.EaseOutQuint, TransformOrigin.World),
new DelayTime(TimeSpan.FromSeconds(3)),
new MoveTo(TimeSpan.FromSeconds(5), myGameObject.transform.position - new Vector3(15, 0, 0), Easing.EaseInBack, TransformOrigin.World),
new DelayTime(TimeSpan.FromSeconds(3)),
new MoveTo(TimeSpan.FromSeconds(5), myGameObject.transform.position, Easing.Spring, TransformOrigin.World),
new DelayTime(TimeSpan.FromSeconds(3)));
Or to perform actions in parallel:
myGameObject.AddComponent<Animate>()
.Begin(Repeat.Forever, new Parallel(
new MoveTo(TimeSpan.FromSeconds(5), myGameObject.transform.position - new Vector3(0, 5, 0)),
new ScaleTo(TimeSpan.FromSeconds(5), Vector3.zero)));
And here is the code (not including the code here).
Animate:
public class Animate : MonoBehaviour
public const Repeat DefaultRepeat = Repeat.Once;
class ActionSequence
/// <summary>
/// The actions to perform.
/// </summary>
readonly ActionBase _actions;
/// <summary>
/// The current action.
/// </summary>
public ActionBase Current;
/// <summary>
/// The arguments to evaluate for the current action.
/// </summary>
public object CurrentArguments;
/// <summary>
/// The index of the current action.
/// </summary>
public uint CurrentIndex = 0;
/// <summary>
/// The "journey" of the current action.
/// </summary>
public float CurrentJourney = 0;
/// <summary>
/// Whether or not the current action has started.
/// </summary>
public bool CurrentStarted;
/// <summary>
/// The number of actions in the sequence.
/// </summary>
public readonly uint Length;
/// <summary>
/// The manner in which the sequence of actions should repeat.
/// </summary>
public Repeat Repeat;
public ActionSequence(Repeat repeat, ActionBase actions)
_actions = actions;
Current = _actions[0];
Length = Convert.ToUInt32(_actions.Length);
Repeat = repeat;
public ActionBase this[uint index]
get
return _actions[index];
public void Next()
CurrentArguments = null;
CurrentIndex++;
CurrentJourney = 0;
CurrentStarted = false;
public enum Repeat
Once,
Forever
List<ActionSequence> _sequences = new List<ActionSequence>();
void Update()
var count = _sequences.Count;
if (count == 0)
return;
var deltaTime = Time.deltaTime;
for (int i = 0; i < count; i++)
var sequence = _sequences[i];
if (sequence.Current.Duration <= TimeSpan.Zero)
goto Done;
if (!sequence.CurrentStarted)
sequence.CurrentArguments = sequence.Current.GetArguments(gameObject);
sequence.CurrentStarted = true;
if (sequence.CurrentJourney >= sequence.Current.Duration.TotalSeconds)
goto Done;
sequence.CurrentJourney += deltaTime;
sequence.Current.Update(gameObject, deltaTime, sequence.CurrentJourney, sequence.CurrentArguments);
continue;
//Is the action done?
Done:
//If so, get the next
sequence.Next();
if (sequence.Repeat == Repeat.Forever)
//If done, prepare for next iteration
if (sequence.CurrentIndex == sequence.Length)
sequence.CurrentIndex = 0;
//Get next action now so we don't have to on subsequent updates
sequence.Current = sequence[sequence.CurrentIndex];
else if (sequence.Repeat == Repeat.Once)
//If done, remove current sequence
if (sequence.CurrentIndex == sequence.Length)
_sequences.RemoveAt(i);
count--;
i--;
//Otherwise, get next action now so we don't have to on subsequent updates
else sequence.Current = sequence[sequence.CurrentIndex];
/// <summary>
/// Performs the given sequence of actions once.
/// </summary>
/// <param name="actions"></param>
public void Begin(params ActionBase actions)
Begin(DefaultRepeat, actions);
/// <summary>
/// Performs the given sequence of actions once or forever.
/// </summary>
/// <param name="repeat"></param>
/// <param name="actions"></param>
public void Begin(Repeat repeat, params ActionBase actions)
var sequence = new ActionSequence(repeat, actions);
_sequences.Add(sequence);
/// <summary>
/// Performs the given sequence of actions in parallel once.
/// </summary>
/// <param name="repeat"></param>
/// <param name="actions"></param>
public void Begin(Parallel parallel)
Begin(DefaultRepeat, parallel);
/// <summary>
/// Performs the given sequence of actions in parallel once or forever.
/// </summary>
/// <param name="repeat"></param>
/// <param name="parallel"></param>
public void Begin(Repeat repeat, Parallel parallel)
for (uint i = 0; i < parallel.Count; i++)
Begin(repeat, parallel[i]);
ActionBase + some base actions:
public abstract class ActionBase
protected readonly TimeSpan _duration;
public TimeSpan Duration
get
return _duration;
protected ActionBase(TimeSpan duration)
_duration = duration;
internal abstract object GetArguments(GameObject go);
internal abstract void Update(GameObject go, float deltaTime, float journey, object arguments);
public abstract class Interpolate<TValue> : ActionBase
protected readonly Easing _easing;
protected readonly TValue _target;
protected Interpolate(TimeSpan duration, TValue target, Easing easing) : base(duration)
_easing = easing;
_target = target;
protected float GetValue(Easing easing, float origin, float target, float percent)
switch (_easing)
case Easing.EaseInBack:
return EasingFunction.EaseInBack(origin, target, percent);
case Easing.EaseInBounce:
return EasingFunction.EaseInBounce(origin, target, percent);
case Easing.EaseInCirc:
return EasingFunction.EaseInCirc(origin, target, percent);
case Easing.EaseInCubic:
return EasingFunction.EaseInCubic(origin, target, percent);
case Easing.EaseInElastic:
return EasingFunction.EaseInElastic(origin, target, percent);
case Easing.EaseInExpo:
return EasingFunction.EaseInExpo(origin, target, percent);
case Easing.EaseInOutBack:
return EasingFunction.EaseInOutBack(origin, target, percent);
case Easing.EaseInOutBounce:
return EasingFunction.EaseInOutBounce(origin, target, percent);
case Easing.EaseInOutCirc:
return EasingFunction.EaseInOutCirc(origin, target, percent);
case Easing.EaseInOutCubic:
return EasingFunction.EaseInOutCubic(origin, target, percent);
case Easing.EaseInOutElastic:
return EasingFunction.EaseInOutElastic(origin, target, percent);
case Easing.EaseInOutExpo:
return EasingFunction.EaseInOutExpo(origin, target, percent);
case Easing.EaseInOutQuad:
return EasingFunction.EaseInOutQuad(origin, target, percent);
case Easing.EaseInOutQuart:
return EasingFunction.EaseInOutQuart(origin, target, percent);
case Easing.EaseInOutQuint:
return EasingFunction.EaseInOutQuint(origin, target, percent);
case Easing.EaseInOutSine:
return EasingFunction.EaseInOutSine(origin, target, percent);
case Easing.EaseInQuad:
return EasingFunction.EaseInQuad(origin, target, percent);
case Easing.EaseInQuart:
return EasingFunction.EaseInQuart(origin, target, percent);
case Easing.EaseInQuint:
return EasingFunction.EaseInQuint(origin, target, percent);
case Easing.EaseInSine:
return EasingFunction.EaseInSine(origin, target, percent);
case Easing.EaseOutBack:
return EasingFunction.EaseOutBack(origin, target, percent);
case Easing.EaseOutBounce:
return EasingFunction.EaseOutBounce(origin, target, percent);
case Easing.EaseOutCirc:
return EasingFunction.EaseOutCirc(origin, target, percent);
case Easing.EaseOutCubic:
return EasingFunction.EaseOutCubic(origin, target, percent);
case Easing.EaseOutElastic:
return EasingFunction.EaseOutElastic(origin, target, percent);
case Easing.EaseOutExpo:
return EasingFunction.EaseOutExpo(origin, target, percent);
case Easing.EaseOutQuad:
return EasingFunction.EaseOutQuad(origin, target, percent);
case Easing.EaseOutQuart:
return EasingFunction.EaseOutQuart(origin, target, percent);
case Easing.EaseOutQuint:
return EasingFunction.EaseOutQuint(origin, target, percent);
case Easing.EaseOutSine:
return EasingFunction.EaseOutSine(origin, target, percent);
case Easing.Linear:
return EasingFunction.Linear(origin, target, percent);
case Easing.Spring:
return EasingFunction.Spring(origin, target, percent);
throw new NotSupportedException();
protected abstract TValue GetValue(TValue origin, float percent);
protected abstract void SetValue(GameObject go, TValue value);
internal override void Update(GameObject go, float deltaTime, float journey, object arguments)
var origin = (TValue)arguments[0];
var percent = Mathf.Clamp01(journey / Convert.ToSingle(_duration.TotalSeconds));
var value = GetValue(origin, percent);
SetValue(go, value);
public abstract class InterpolateVector3 : Interpolate<Vector3>
protected InterpolateVector3(TimeSpan duration, Vector3 target, Easing easing) : base(duration, target, easing)
protected override Vector3 GetValue(Vector3 origin, float percent)
var value = new float[3];
for (var i = 0; i < 3; i++)
var origin_
= i == 0
? origin.x
: i == 1
? origin.y
: origin.z;
var target_
= i == 0
? _target.x
: i == 1
? _target.y
: _target.z;
value[i] = GetValue(_easing, origin_, target_, percent);
return new Vector3(value[0], value[1], value[2]);
public abstract class InterpolateVector4 : Interpolate<Vector4>
protected InterpolateVector4(TimeSpan duration, Vector3 target, Easing easing) : base(duration, target, easing)
protected override Vector4 GetValue(Vector4 origin, float percent)
var value = new float[4];
for (var i = 0; i < 4; i++)
var origin_
= i == 0
? origin.w
: i == 1
? origin.x
: i == 2
? origin.y
: origin.z;
var target_
= i == 0
? _target.w
: i == 1
? _target.x
: i == 2
? _target.y
: _target.z;
value[i] = GetValue(_easing, origin_, target_, percent);
return new Vector4(value[0], value[1], value[2], value[3]);
public abstract class InterpolateQuaternion : Interpolate<Quaternion>
protected InterpolateQuaternion(TimeSpan duration, Quaternion target, Easing easing) : base(duration, target, easing)
protected override Quaternion GetValue(Quaternion origin, float percent)
switch (_easing)
case Easing.Linear:
return Quaternion.Lerp(origin, _target, percent);
throw new NotSupportedException();
Some default actions:
public class DelayTime : ActionBase
public DelayTime(TimeSpan duration) : base(duration)
internal override object GetArguments(GameObject go)
return default(object);
internal override void Update(GameObject go, float deltaTime, float journey, object arguments)
public class MoveTo : InterpolateVector3
protected readonly TransformOrigin _transformOrigin;
public MoveTo(TimeSpan duration, Vector3 target, Easing easing = Easing.Linear, TransformOrigin transformOrigin = TransformOrigin.Local) : base(duration, target, easing)
_transformOrigin = transformOrigin;
internal override object GetArguments(GameObject go)
return new object
_transformOrigin == TransformOrigin.Local
? go.transform.localPosition
: go.transform.position
;
protected override void SetValue(GameObject go, Vector3 value)
switch (_transformOrigin)
case TransformOrigin.Local:
go.transform.localPosition = value;
break;
case TransformOrigin.World:
go.transform.position = value;
break;
public class RotateTo : InterpolateQuaternion
protected readonly TransformOrigin _transformOrigin;
public RotateTo(TimeSpan duration, Quaternion target, Easing easing = Easing.Linear, TransformOrigin transformOrigin = TransformOrigin.Local) : base(duration, target, easing)
_transformOrigin = transformOrigin;
internal override object GetArguments(GameObject go)
return new object
_transformOrigin == TransformOrigin.Local
? go.transform.localRotation
: go.transform.rotation
;
protected override void SetValue(GameObject go, Quaternion value)
switch (_transformOrigin)
case TransformOrigin.Local:
go.transform.localRotation = value;
break;
case TransformOrigin.World:
go.transform.rotation = value;
break;
public class ScaleTo : InterpolateVector3
public ScaleTo(TimeSpan duration, Vector3 target, Easing easing = Easing.Linear) : base(duration, target, easing)
internal override object GetArguments(GameObject go)
return new object
go.transform.localScale
;
protected override void SetValue(GameObject go, Vector3 value)
go.transform.localScale = value;
For executing actions in parallel:
public class Parallel
readonly ActionBase _actions;
public uint Count
get
return Convert.ToUInt32(_actions.Length);
public Parallel(params ActionBase actions)
_actions = actions;
public ActionBase this[uint index]
get
return _actions[index];
Other:
public enum TransformOrigin
Local,
World
c# unity3d
2
This isn't directly related to a code review, but just because you said you are new to Unity, I wanted to point out a few existing tweening libraries, such as DoTween, GoKit, iTween, LeanTween. Personally I'm using DoTween and the source is on GitHub. If you are concerned about performance, LeanTween emphasizes that it is efficient and lightweight. In case you don't get a response here, it may be good to review these libraries' code to get a sense of how things are done there.
â sonny
May 3 at 15:29
Thanks for the recommendations. Why isn't this related to code review? I published my code and I'm asking the community to review it? Personally, I won't fuss with DoTween or any library I have to pay for. I might go with iTween since it is free, but it would still be nice to learn how this all works.
â James M
May 3 at 18:19
1
Oops sorry, I meant my comment isn't related to code review. Your post is fine of course. All the libraries I mentioned are free (though DoTween does have a "Pro" version). iTween is one of the most popular options, though I think it's also known for having a messy codebase. I've used both DOTween and iTween and I find DOTween much easier to use. I definitely think it's good to learn how it all works. I was just suggesting that you go look at the source code of these libraries to see how it works, and how they did it differently compared to you.
â sonny
May 3 at 18:32
Here is the DOTween GitHub repository and here is the LeanTween Github repository
â sonny
May 3 at 18:34
add a comment |Â
up vote
1
down vote
favorite
up vote
1
down vote
favorite
This script executes a sequence of actions over time and is based on the methods exposed by the UrhoSharp framework. I am new to Unity, but well acquainted with C# so I know what I'm doing for the most part. I'm primarily concerned with performance. Is this how such problems are usually approached in Unity or can we do better?
Using the functions published here, I was able to make Vector3 interpolations work. I will be honest and admit I know nothing about quaternions so I just use Unity's built-in helper function for linear interpolations and throw an exception if some other interpolation is specified.
Ultimately, this solution aims to provide a proper framework for animating anything in a easy to understand way.
This is how you'd use it.
myGameObject.AddComponent<Animate>()
.Begin(Repeat.Forever,
new MoveTo(TimeSpan.FromSeconds(5), myGameObject.transform.position + new Vector3(15, 0, 0), Easing.EaseInBounce, TransformOrigin.World),
new DelayTime(TimeSpan.FromSeconds(3)),
new MoveTo(TimeSpan.FromSeconds(5), myGameObject.transform.position, Easing.EaseOutQuint, TransformOrigin.World),
new DelayTime(TimeSpan.FromSeconds(3)),
new MoveTo(TimeSpan.FromSeconds(5), myGameObject.transform.position - new Vector3(15, 0, 0), Easing.EaseInBack, TransformOrigin.World),
new DelayTime(TimeSpan.FromSeconds(3)),
new MoveTo(TimeSpan.FromSeconds(5), myGameObject.transform.position, Easing.Spring, TransformOrigin.World),
new DelayTime(TimeSpan.FromSeconds(3)));
Or to perform actions in parallel:
myGameObject.AddComponent<Animate>()
.Begin(Repeat.Forever, new Parallel(
new MoveTo(TimeSpan.FromSeconds(5), myGameObject.transform.position - new Vector3(0, 5, 0)),
new ScaleTo(TimeSpan.FromSeconds(5), Vector3.zero)));
And here is the code (not including the code here).
Animate:
public class Animate : MonoBehaviour
public const Repeat DefaultRepeat = Repeat.Once;
class ActionSequence
/// <summary>
/// The actions to perform.
/// </summary>
readonly ActionBase _actions;
/// <summary>
/// The current action.
/// </summary>
public ActionBase Current;
/// <summary>
/// The arguments to evaluate for the current action.
/// </summary>
public object CurrentArguments;
/// <summary>
/// The index of the current action.
/// </summary>
public uint CurrentIndex = 0;
/// <summary>
/// The "journey" of the current action.
/// </summary>
public float CurrentJourney = 0;
/// <summary>
/// Whether or not the current action has started.
/// </summary>
public bool CurrentStarted;
/// <summary>
/// The number of actions in the sequence.
/// </summary>
public readonly uint Length;
/// <summary>
/// The manner in which the sequence of actions should repeat.
/// </summary>
public Repeat Repeat;
public ActionSequence(Repeat repeat, ActionBase actions)
_actions = actions;
Current = _actions[0];
Length = Convert.ToUInt32(_actions.Length);
Repeat = repeat;
public ActionBase this[uint index]
get
return _actions[index];
public void Next()
CurrentArguments = null;
CurrentIndex++;
CurrentJourney = 0;
CurrentStarted = false;
public enum Repeat
Once,
Forever
List<ActionSequence> _sequences = new List<ActionSequence>();
void Update()
var count = _sequences.Count;
if (count == 0)
return;
var deltaTime = Time.deltaTime;
for (int i = 0; i < count; i++)
var sequence = _sequences[i];
if (sequence.Current.Duration <= TimeSpan.Zero)
goto Done;
if (!sequence.CurrentStarted)
sequence.CurrentArguments = sequence.Current.GetArguments(gameObject);
sequence.CurrentStarted = true;
if (sequence.CurrentJourney >= sequence.Current.Duration.TotalSeconds)
goto Done;
sequence.CurrentJourney += deltaTime;
sequence.Current.Update(gameObject, deltaTime, sequence.CurrentJourney, sequence.CurrentArguments);
continue;
//Is the action done?
Done:
//If so, get the next
sequence.Next();
if (sequence.Repeat == Repeat.Forever)
//If done, prepare for next iteration
if (sequence.CurrentIndex == sequence.Length)
sequence.CurrentIndex = 0;
//Get next action now so we don't have to on subsequent updates
sequence.Current = sequence[sequence.CurrentIndex];
else if (sequence.Repeat == Repeat.Once)
//If done, remove current sequence
if (sequence.CurrentIndex == sequence.Length)
_sequences.RemoveAt(i);
count--;
i--;
//Otherwise, get next action now so we don't have to on subsequent updates
else sequence.Current = sequence[sequence.CurrentIndex];
/// <summary>
/// Performs the given sequence of actions once.
/// </summary>
/// <param name="actions"></param>
public void Begin(params ActionBase actions)
Begin(DefaultRepeat, actions);
/// <summary>
/// Performs the given sequence of actions once or forever.
/// </summary>
/// <param name="repeat"></param>
/// <param name="actions"></param>
public void Begin(Repeat repeat, params ActionBase actions)
var sequence = new ActionSequence(repeat, actions);
_sequences.Add(sequence);
/// <summary>
/// Performs the given sequence of actions in parallel once.
/// </summary>
/// <param name="repeat"></param>
/// <param name="actions"></param>
public void Begin(Parallel parallel)
Begin(DefaultRepeat, parallel);
/// <summary>
/// Performs the given sequence of actions in parallel once or forever.
/// </summary>
/// <param name="repeat"></param>
/// <param name="parallel"></param>
public void Begin(Repeat repeat, Parallel parallel)
for (uint i = 0; i < parallel.Count; i++)
Begin(repeat, parallel[i]);
ActionBase + some base actions:
public abstract class ActionBase
protected readonly TimeSpan _duration;
public TimeSpan Duration
get
return _duration;
protected ActionBase(TimeSpan duration)
_duration = duration;
internal abstract object GetArguments(GameObject go);
internal abstract void Update(GameObject go, float deltaTime, float journey, object arguments);
public abstract class Interpolate<TValue> : ActionBase
protected readonly Easing _easing;
protected readonly TValue _target;
protected Interpolate(TimeSpan duration, TValue target, Easing easing) : base(duration)
_easing = easing;
_target = target;
protected float GetValue(Easing easing, float origin, float target, float percent)
switch (_easing)
case Easing.EaseInBack:
return EasingFunction.EaseInBack(origin, target, percent);
case Easing.EaseInBounce:
return EasingFunction.EaseInBounce(origin, target, percent);
case Easing.EaseInCirc:
return EasingFunction.EaseInCirc(origin, target, percent);
case Easing.EaseInCubic:
return EasingFunction.EaseInCubic(origin, target, percent);
case Easing.EaseInElastic:
return EasingFunction.EaseInElastic(origin, target, percent);
case Easing.EaseInExpo:
return EasingFunction.EaseInExpo(origin, target, percent);
case Easing.EaseInOutBack:
return EasingFunction.EaseInOutBack(origin, target, percent);
case Easing.EaseInOutBounce:
return EasingFunction.EaseInOutBounce(origin, target, percent);
case Easing.EaseInOutCirc:
return EasingFunction.EaseInOutCirc(origin, target, percent);
case Easing.EaseInOutCubic:
return EasingFunction.EaseInOutCubic(origin, target, percent);
case Easing.EaseInOutElastic:
return EasingFunction.EaseInOutElastic(origin, target, percent);
case Easing.EaseInOutExpo:
return EasingFunction.EaseInOutExpo(origin, target, percent);
case Easing.EaseInOutQuad:
return EasingFunction.EaseInOutQuad(origin, target, percent);
case Easing.EaseInOutQuart:
return EasingFunction.EaseInOutQuart(origin, target, percent);
case Easing.EaseInOutQuint:
return EasingFunction.EaseInOutQuint(origin, target, percent);
case Easing.EaseInOutSine:
return EasingFunction.EaseInOutSine(origin, target, percent);
case Easing.EaseInQuad:
return EasingFunction.EaseInQuad(origin, target, percent);
case Easing.EaseInQuart:
return EasingFunction.EaseInQuart(origin, target, percent);
case Easing.EaseInQuint:
return EasingFunction.EaseInQuint(origin, target, percent);
case Easing.EaseInSine:
return EasingFunction.EaseInSine(origin, target, percent);
case Easing.EaseOutBack:
return EasingFunction.EaseOutBack(origin, target, percent);
case Easing.EaseOutBounce:
return EasingFunction.EaseOutBounce(origin, target, percent);
case Easing.EaseOutCirc:
return EasingFunction.EaseOutCirc(origin, target, percent);
case Easing.EaseOutCubic:
return EasingFunction.EaseOutCubic(origin, target, percent);
case Easing.EaseOutElastic:
return EasingFunction.EaseOutElastic(origin, target, percent);
case Easing.EaseOutExpo:
return EasingFunction.EaseOutExpo(origin, target, percent);
case Easing.EaseOutQuad:
return EasingFunction.EaseOutQuad(origin, target, percent);
case Easing.EaseOutQuart:
return EasingFunction.EaseOutQuart(origin, target, percent);
case Easing.EaseOutQuint:
return EasingFunction.EaseOutQuint(origin, target, percent);
case Easing.EaseOutSine:
return EasingFunction.EaseOutSine(origin, target, percent);
case Easing.Linear:
return EasingFunction.Linear(origin, target, percent);
case Easing.Spring:
return EasingFunction.Spring(origin, target, percent);
throw new NotSupportedException();
protected abstract TValue GetValue(TValue origin, float percent);
protected abstract void SetValue(GameObject go, TValue value);
internal override void Update(GameObject go, float deltaTime, float journey, object arguments)
var origin = (TValue)arguments[0];
var percent = Mathf.Clamp01(journey / Convert.ToSingle(_duration.TotalSeconds));
var value = GetValue(origin, percent);
SetValue(go, value);
public abstract class InterpolateVector3 : Interpolate<Vector3>
protected InterpolateVector3(TimeSpan duration, Vector3 target, Easing easing) : base(duration, target, easing)
protected override Vector3 GetValue(Vector3 origin, float percent)
var value = new float[3];
for (var i = 0; i < 3; i++)
var origin_
= i == 0
? origin.x
: i == 1
? origin.y
: origin.z;
var target_
= i == 0
? _target.x
: i == 1
? _target.y
: _target.z;
value[i] = GetValue(_easing, origin_, target_, percent);
return new Vector3(value[0], value[1], value[2]);
public abstract class InterpolateVector4 : Interpolate<Vector4>
protected InterpolateVector4(TimeSpan duration, Vector3 target, Easing easing) : base(duration, target, easing)
protected override Vector4 GetValue(Vector4 origin, float percent)
var value = new float[4];
for (var i = 0; i < 4; i++)
var origin_
= i == 0
? origin.w
: i == 1
? origin.x
: i == 2
? origin.y
: origin.z;
var target_
= i == 0
? _target.w
: i == 1
? _target.x
: i == 2
? _target.y
: _target.z;
value[i] = GetValue(_easing, origin_, target_, percent);
return new Vector4(value[0], value[1], value[2], value[3]);
public abstract class InterpolateQuaternion : Interpolate<Quaternion>
protected InterpolateQuaternion(TimeSpan duration, Quaternion target, Easing easing) : base(duration, target, easing)
protected override Quaternion GetValue(Quaternion origin, float percent)
switch (_easing)
case Easing.Linear:
return Quaternion.Lerp(origin, _target, percent);
throw new NotSupportedException();
Some default actions:
public class DelayTime : ActionBase
public DelayTime(TimeSpan duration) : base(duration)
internal override object GetArguments(GameObject go)
return default(object);
internal override void Update(GameObject go, float deltaTime, float journey, object arguments)
public class MoveTo : InterpolateVector3
protected readonly TransformOrigin _transformOrigin;
public MoveTo(TimeSpan duration, Vector3 target, Easing easing = Easing.Linear, TransformOrigin transformOrigin = TransformOrigin.Local) : base(duration, target, easing)
_transformOrigin = transformOrigin;
internal override object GetArguments(GameObject go)
return new object
_transformOrigin == TransformOrigin.Local
? go.transform.localPosition
: go.transform.position
;
protected override void SetValue(GameObject go, Vector3 value)
switch (_transformOrigin)
case TransformOrigin.Local:
go.transform.localPosition = value;
break;
case TransformOrigin.World:
go.transform.position = value;
break;
public class RotateTo : InterpolateQuaternion
protected readonly TransformOrigin _transformOrigin;
public RotateTo(TimeSpan duration, Quaternion target, Easing easing = Easing.Linear, TransformOrigin transformOrigin = TransformOrigin.Local) : base(duration, target, easing)
_transformOrigin = transformOrigin;
internal override object GetArguments(GameObject go)
return new object
_transformOrigin == TransformOrigin.Local
? go.transform.localRotation
: go.transform.rotation
;
protected override void SetValue(GameObject go, Quaternion value)
switch (_transformOrigin)
case TransformOrigin.Local:
go.transform.localRotation = value;
break;
case TransformOrigin.World:
go.transform.rotation = value;
break;
public class ScaleTo : InterpolateVector3
public ScaleTo(TimeSpan duration, Vector3 target, Easing easing = Easing.Linear) : base(duration, target, easing)
internal override object GetArguments(GameObject go)
return new object
go.transform.localScale
;
protected override void SetValue(GameObject go, Vector3 value)
go.transform.localScale = value;
For executing actions in parallel:
public class Parallel
readonly ActionBase _actions;
public uint Count
get
return Convert.ToUInt32(_actions.Length);
public Parallel(params ActionBase actions)
_actions = actions;
public ActionBase this[uint index]
get
return _actions[index];
Other:
public enum TransformOrigin
Local,
World
c# unity3d
This script executes a sequence of actions over time and is based on the methods exposed by the UrhoSharp framework. I am new to Unity, but well acquainted with C# so I know what I'm doing for the most part. I'm primarily concerned with performance. Is this how such problems are usually approached in Unity or can we do better?
Using the functions published here, I was able to make Vector3 interpolations work. I will be honest and admit I know nothing about quaternions so I just use Unity's built-in helper function for linear interpolations and throw an exception if some other interpolation is specified.
Ultimately, this solution aims to provide a proper framework for animating anything in a easy to understand way.
This is how you'd use it.
myGameObject.AddComponent<Animate>()
.Begin(Repeat.Forever,
new MoveTo(TimeSpan.FromSeconds(5), myGameObject.transform.position + new Vector3(15, 0, 0), Easing.EaseInBounce, TransformOrigin.World),
new DelayTime(TimeSpan.FromSeconds(3)),
new MoveTo(TimeSpan.FromSeconds(5), myGameObject.transform.position, Easing.EaseOutQuint, TransformOrigin.World),
new DelayTime(TimeSpan.FromSeconds(3)),
new MoveTo(TimeSpan.FromSeconds(5), myGameObject.transform.position - new Vector3(15, 0, 0), Easing.EaseInBack, TransformOrigin.World),
new DelayTime(TimeSpan.FromSeconds(3)),
new MoveTo(TimeSpan.FromSeconds(5), myGameObject.transform.position, Easing.Spring, TransformOrigin.World),
new DelayTime(TimeSpan.FromSeconds(3)));
Or to perform actions in parallel:
myGameObject.AddComponent<Animate>()
.Begin(Repeat.Forever, new Parallel(
new MoveTo(TimeSpan.FromSeconds(5), myGameObject.transform.position - new Vector3(0, 5, 0)),
new ScaleTo(TimeSpan.FromSeconds(5), Vector3.zero)));
And here is the code (not including the code here).
Animate:
public class Animate : MonoBehaviour
public const Repeat DefaultRepeat = Repeat.Once;
class ActionSequence
/// <summary>
/// The actions to perform.
/// </summary>
readonly ActionBase _actions;
/// <summary>
/// The current action.
/// </summary>
public ActionBase Current;
/// <summary>
/// The arguments to evaluate for the current action.
/// </summary>
public object CurrentArguments;
/// <summary>
/// The index of the current action.
/// </summary>
public uint CurrentIndex = 0;
/// <summary>
/// The "journey" of the current action.
/// </summary>
public float CurrentJourney = 0;
/// <summary>
/// Whether or not the current action has started.
/// </summary>
public bool CurrentStarted;
/// <summary>
/// The number of actions in the sequence.
/// </summary>
public readonly uint Length;
/// <summary>
/// The manner in which the sequence of actions should repeat.
/// </summary>
public Repeat Repeat;
public ActionSequence(Repeat repeat, ActionBase actions)
_actions = actions;
Current = _actions[0];
Length = Convert.ToUInt32(_actions.Length);
Repeat = repeat;
public ActionBase this[uint index]
get
return _actions[index];
public void Next()
CurrentArguments = null;
CurrentIndex++;
CurrentJourney = 0;
CurrentStarted = false;
public enum Repeat
Once,
Forever
List<ActionSequence> _sequences = new List<ActionSequence>();
void Update()
var count = _sequences.Count;
if (count == 0)
return;
var deltaTime = Time.deltaTime;
for (int i = 0; i < count; i++)
var sequence = _sequences[i];
if (sequence.Current.Duration <= TimeSpan.Zero)
goto Done;
if (!sequence.CurrentStarted)
sequence.CurrentArguments = sequence.Current.GetArguments(gameObject);
sequence.CurrentStarted = true;
if (sequence.CurrentJourney >= sequence.Current.Duration.TotalSeconds)
goto Done;
sequence.CurrentJourney += deltaTime;
sequence.Current.Update(gameObject, deltaTime, sequence.CurrentJourney, sequence.CurrentArguments);
continue;
//Is the action done?
Done:
//If so, get the next
sequence.Next();
if (sequence.Repeat == Repeat.Forever)
//If done, prepare for next iteration
if (sequence.CurrentIndex == sequence.Length)
sequence.CurrentIndex = 0;
//Get next action now so we don't have to on subsequent updates
sequence.Current = sequence[sequence.CurrentIndex];
else if (sequence.Repeat == Repeat.Once)
//If done, remove current sequence
if (sequence.CurrentIndex == sequence.Length)
_sequences.RemoveAt(i);
count--;
i--;
//Otherwise, get next action now so we don't have to on subsequent updates
else sequence.Current = sequence[sequence.CurrentIndex];
/// <summary>
/// Performs the given sequence of actions once.
/// </summary>
/// <param name="actions"></param>
public void Begin(params ActionBase actions)
Begin(DefaultRepeat, actions);
/// <summary>
/// Performs the given sequence of actions once or forever.
/// </summary>
/// <param name="repeat"></param>
/// <param name="actions"></param>
public void Begin(Repeat repeat, params ActionBase actions)
var sequence = new ActionSequence(repeat, actions);
_sequences.Add(sequence);
/// <summary>
/// Performs the given sequence of actions in parallel once.
/// </summary>
/// <param name="repeat"></param>
/// <param name="actions"></param>
public void Begin(Parallel parallel)
Begin(DefaultRepeat, parallel);
/// <summary>
/// Performs the given sequence of actions in parallel once or forever.
/// </summary>
/// <param name="repeat"></param>
/// <param name="parallel"></param>
public void Begin(Repeat repeat, Parallel parallel)
for (uint i = 0; i < parallel.Count; i++)
Begin(repeat, parallel[i]);
ActionBase + some base actions:
public abstract class ActionBase
protected readonly TimeSpan _duration;
public TimeSpan Duration
get
return _duration;
protected ActionBase(TimeSpan duration)
_duration = duration;
internal abstract object GetArguments(GameObject go);
internal abstract void Update(GameObject go, float deltaTime, float journey, object arguments);
public abstract class Interpolate<TValue> : ActionBase
protected readonly Easing _easing;
protected readonly TValue _target;
protected Interpolate(TimeSpan duration, TValue target, Easing easing) : base(duration)
_easing = easing;
_target = target;
protected float GetValue(Easing easing, float origin, float target, float percent)
switch (_easing)
case Easing.EaseInBack:
return EasingFunction.EaseInBack(origin, target, percent);
case Easing.EaseInBounce:
return EasingFunction.EaseInBounce(origin, target, percent);
case Easing.EaseInCirc:
return EasingFunction.EaseInCirc(origin, target, percent);
case Easing.EaseInCubic:
return EasingFunction.EaseInCubic(origin, target, percent);
case Easing.EaseInElastic:
return EasingFunction.EaseInElastic(origin, target, percent);
case Easing.EaseInExpo:
return EasingFunction.EaseInExpo(origin, target, percent);
case Easing.EaseInOutBack:
return EasingFunction.EaseInOutBack(origin, target, percent);
case Easing.EaseInOutBounce:
return EasingFunction.EaseInOutBounce(origin, target, percent);
case Easing.EaseInOutCirc:
return EasingFunction.EaseInOutCirc(origin, target, percent);
case Easing.EaseInOutCubic:
return EasingFunction.EaseInOutCubic(origin, target, percent);
case Easing.EaseInOutElastic:
return EasingFunction.EaseInOutElastic(origin, target, percent);
case Easing.EaseInOutExpo:
return EasingFunction.EaseInOutExpo(origin, target, percent);
case Easing.EaseInOutQuad:
return EasingFunction.EaseInOutQuad(origin, target, percent);
case Easing.EaseInOutQuart:
return EasingFunction.EaseInOutQuart(origin, target, percent);
case Easing.EaseInOutQuint:
return EasingFunction.EaseInOutQuint(origin, target, percent);
case Easing.EaseInOutSine:
return EasingFunction.EaseInOutSine(origin, target, percent);
case Easing.EaseInQuad:
return EasingFunction.EaseInQuad(origin, target, percent);
case Easing.EaseInQuart:
return EasingFunction.EaseInQuart(origin, target, percent);
case Easing.EaseInQuint:
return EasingFunction.EaseInQuint(origin, target, percent);
case Easing.EaseInSine:
return EasingFunction.EaseInSine(origin, target, percent);
case Easing.EaseOutBack:
return EasingFunction.EaseOutBack(origin, target, percent);
case Easing.EaseOutBounce:
return EasingFunction.EaseOutBounce(origin, target, percent);
case Easing.EaseOutCirc:
return EasingFunction.EaseOutCirc(origin, target, percent);
case Easing.EaseOutCubic:
return EasingFunction.EaseOutCubic(origin, target, percent);
case Easing.EaseOutElastic:
return EasingFunction.EaseOutElastic(origin, target, percent);
case Easing.EaseOutExpo:
return EasingFunction.EaseOutExpo(origin, target, percent);
case Easing.EaseOutQuad:
return EasingFunction.EaseOutQuad(origin, target, percent);
case Easing.EaseOutQuart:
return EasingFunction.EaseOutQuart(origin, target, percent);
case Easing.EaseOutQuint:
return EasingFunction.EaseOutQuint(origin, target, percent);
case Easing.EaseOutSine:
return EasingFunction.EaseOutSine(origin, target, percent);
case Easing.Linear:
return EasingFunction.Linear(origin, target, percent);
case Easing.Spring:
return EasingFunction.Spring(origin, target, percent);
throw new NotSupportedException();
protected abstract TValue GetValue(TValue origin, float percent);
protected abstract void SetValue(GameObject go, TValue value);
internal override void Update(GameObject go, float deltaTime, float journey, object arguments)
var origin = (TValue)arguments[0];
var percent = Mathf.Clamp01(journey / Convert.ToSingle(_duration.TotalSeconds));
var value = GetValue(origin, percent);
SetValue(go, value);
public abstract class InterpolateVector3 : Interpolate<Vector3>
protected InterpolateVector3(TimeSpan duration, Vector3 target, Easing easing) : base(duration, target, easing)
protected override Vector3 GetValue(Vector3 origin, float percent)
var value = new float[3];
for (var i = 0; i < 3; i++)
var origin_
= i == 0
? origin.x
: i == 1
? origin.y
: origin.z;
var target_
= i == 0
? _target.x
: i == 1
? _target.y
: _target.z;
value[i] = GetValue(_easing, origin_, target_, percent);
return new Vector3(value[0], value[1], value[2]);
public abstract class InterpolateVector4 : Interpolate<Vector4>
protected InterpolateVector4(TimeSpan duration, Vector3 target, Easing easing) : base(duration, target, easing)
protected override Vector4 GetValue(Vector4 origin, float percent)
var value = new float[4];
for (var i = 0; i < 4; i++)
var origin_
= i == 0
? origin.w
: i == 1
? origin.x
: i == 2
? origin.y
: origin.z;
var target_
= i == 0
? _target.w
: i == 1
? _target.x
: i == 2
? _target.y
: _target.z;
value[i] = GetValue(_easing, origin_, target_, percent);
return new Vector4(value[0], value[1], value[2], value[3]);
public abstract class InterpolateQuaternion : Interpolate<Quaternion>
protected InterpolateQuaternion(TimeSpan duration, Quaternion target, Easing easing) : base(duration, target, easing)
protected override Quaternion GetValue(Quaternion origin, float percent)
switch (_easing)
case Easing.Linear:
return Quaternion.Lerp(origin, _target, percent);
throw new NotSupportedException();
Some default actions:
public class DelayTime : ActionBase
public DelayTime(TimeSpan duration) : base(duration)
internal override object GetArguments(GameObject go)
return default(object);
internal override void Update(GameObject go, float deltaTime, float journey, object arguments)
public class MoveTo : InterpolateVector3
protected readonly TransformOrigin _transformOrigin;
public MoveTo(TimeSpan duration, Vector3 target, Easing easing = Easing.Linear, TransformOrigin transformOrigin = TransformOrigin.Local) : base(duration, target, easing)
_transformOrigin = transformOrigin;
internal override object GetArguments(GameObject go)
return new object
_transformOrigin == TransformOrigin.Local
? go.transform.localPosition
: go.transform.position
;
protected override void SetValue(GameObject go, Vector3 value)
switch (_transformOrigin)
case TransformOrigin.Local:
go.transform.localPosition = value;
break;
case TransformOrigin.World:
go.transform.position = value;
break;
public class RotateTo : InterpolateQuaternion
protected readonly TransformOrigin _transformOrigin;
public RotateTo(TimeSpan duration, Quaternion target, Easing easing = Easing.Linear, TransformOrigin transformOrigin = TransformOrigin.Local) : base(duration, target, easing)
_transformOrigin = transformOrigin;
internal override object GetArguments(GameObject go)
return new object
_transformOrigin == TransformOrigin.Local
? go.transform.localRotation
: go.transform.rotation
;
protected override void SetValue(GameObject go, Quaternion value)
switch (_transformOrigin)
case TransformOrigin.Local:
go.transform.localRotation = value;
break;
case TransformOrigin.World:
go.transform.rotation = value;
break;
public class ScaleTo : InterpolateVector3
public ScaleTo(TimeSpan duration, Vector3 target, Easing easing = Easing.Linear) : base(duration, target, easing)
internal override object GetArguments(GameObject go)
return new object
go.transform.localScale
;
protected override void SetValue(GameObject go, Vector3 value)
go.transform.localScale = value;
For executing actions in parallel:
public class Parallel
readonly ActionBase _actions;
public uint Count
get
return Convert.ToUInt32(_actions.Length);
public Parallel(params ActionBase actions)
_actions = actions;
public ActionBase this[uint index]
get
return _actions[index];
Other:
public enum TransformOrigin
Local,
World
c# unity3d
edited May 3 at 23:02
asked May 3 at 7:57
James M
1263
1263
2
This isn't directly related to a code review, but just because you said you are new to Unity, I wanted to point out a few existing tweening libraries, such as DoTween, GoKit, iTween, LeanTween. Personally I'm using DoTween and the source is on GitHub. If you are concerned about performance, LeanTween emphasizes that it is efficient and lightweight. In case you don't get a response here, it may be good to review these libraries' code to get a sense of how things are done there.
â sonny
May 3 at 15:29
Thanks for the recommendations. Why isn't this related to code review? I published my code and I'm asking the community to review it? Personally, I won't fuss with DoTween or any library I have to pay for. I might go with iTween since it is free, but it would still be nice to learn how this all works.
â James M
May 3 at 18:19
1
Oops sorry, I meant my comment isn't related to code review. Your post is fine of course. All the libraries I mentioned are free (though DoTween does have a "Pro" version). iTween is one of the most popular options, though I think it's also known for having a messy codebase. I've used both DOTween and iTween and I find DOTween much easier to use. I definitely think it's good to learn how it all works. I was just suggesting that you go look at the source code of these libraries to see how it works, and how they did it differently compared to you.
â sonny
May 3 at 18:32
Here is the DOTween GitHub repository and here is the LeanTween Github repository
â sonny
May 3 at 18:34
add a comment |Â
2
This isn't directly related to a code review, but just because you said you are new to Unity, I wanted to point out a few existing tweening libraries, such as DoTween, GoKit, iTween, LeanTween. Personally I'm using DoTween and the source is on GitHub. If you are concerned about performance, LeanTween emphasizes that it is efficient and lightweight. In case you don't get a response here, it may be good to review these libraries' code to get a sense of how things are done there.
â sonny
May 3 at 15:29
Thanks for the recommendations. Why isn't this related to code review? I published my code and I'm asking the community to review it? Personally, I won't fuss with DoTween or any library I have to pay for. I might go with iTween since it is free, but it would still be nice to learn how this all works.
â James M
May 3 at 18:19
1
Oops sorry, I meant my comment isn't related to code review. Your post is fine of course. All the libraries I mentioned are free (though DoTween does have a "Pro" version). iTween is one of the most popular options, though I think it's also known for having a messy codebase. I've used both DOTween and iTween and I find DOTween much easier to use. I definitely think it's good to learn how it all works. I was just suggesting that you go look at the source code of these libraries to see how it works, and how they did it differently compared to you.
â sonny
May 3 at 18:32
Here is the DOTween GitHub repository and here is the LeanTween Github repository
â sonny
May 3 at 18:34
2
2
This isn't directly related to a code review, but just because you said you are new to Unity, I wanted to point out a few existing tweening libraries, such as DoTween, GoKit, iTween, LeanTween. Personally I'm using DoTween and the source is on GitHub. If you are concerned about performance, LeanTween emphasizes that it is efficient and lightweight. In case you don't get a response here, it may be good to review these libraries' code to get a sense of how things are done there.
â sonny
May 3 at 15:29
This isn't directly related to a code review, but just because you said you are new to Unity, I wanted to point out a few existing tweening libraries, such as DoTween, GoKit, iTween, LeanTween. Personally I'm using DoTween and the source is on GitHub. If you are concerned about performance, LeanTween emphasizes that it is efficient and lightweight. In case you don't get a response here, it may be good to review these libraries' code to get a sense of how things are done there.
â sonny
May 3 at 15:29
Thanks for the recommendations. Why isn't this related to code review? I published my code and I'm asking the community to review it? Personally, I won't fuss with DoTween or any library I have to pay for. I might go with iTween since it is free, but it would still be nice to learn how this all works.
â James M
May 3 at 18:19
Thanks for the recommendations. Why isn't this related to code review? I published my code and I'm asking the community to review it? Personally, I won't fuss with DoTween or any library I have to pay for. I might go with iTween since it is free, but it would still be nice to learn how this all works.
â James M
May 3 at 18:19
1
1
Oops sorry, I meant my comment isn't related to code review. Your post is fine of course. All the libraries I mentioned are free (though DoTween does have a "Pro" version). iTween is one of the most popular options, though I think it's also known for having a messy codebase. I've used both DOTween and iTween and I find DOTween much easier to use. I definitely think it's good to learn how it all works. I was just suggesting that you go look at the source code of these libraries to see how it works, and how they did it differently compared to you.
â sonny
May 3 at 18:32
Oops sorry, I meant my comment isn't related to code review. Your post is fine of course. All the libraries I mentioned are free (though DoTween does have a "Pro" version). iTween is one of the most popular options, though I think it's also known for having a messy codebase. I've used both DOTween and iTween and I find DOTween much easier to use. I definitely think it's good to learn how it all works. I was just suggesting that you go look at the source code of these libraries to see how it works, and how they did it differently compared to you.
â sonny
May 3 at 18:32
Here is the DOTween GitHub repository and here is the LeanTween Github repository
â sonny
May 3 at 18:34
Here is the DOTween GitHub repository and here is the LeanTween Github repository
â sonny
May 3 at 18:34
add a comment |Â
active
oldest
votes
active
oldest
votes
active
oldest
votes
active
oldest
votes
active
oldest
votes
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f193534%2fexecuting-a-sequence-of-actions-over-time-in-unity%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
2
This isn't directly related to a code review, but just because you said you are new to Unity, I wanted to point out a few existing tweening libraries, such as DoTween, GoKit, iTween, LeanTween. Personally I'm using DoTween and the source is on GitHub. If you are concerned about performance, LeanTween emphasizes that it is efficient and lightweight. In case you don't get a response here, it may be good to review these libraries' code to get a sense of how things are done there.
â sonny
May 3 at 15:29
Thanks for the recommendations. Why isn't this related to code review? I published my code and I'm asking the community to review it? Personally, I won't fuss with DoTween or any library I have to pay for. I might go with iTween since it is free, but it would still be nice to learn how this all works.
â James M
May 3 at 18:19
1
Oops sorry, I meant my comment isn't related to code review. Your post is fine of course. All the libraries I mentioned are free (though DoTween does have a "Pro" version). iTween is one of the most popular options, though I think it's also known for having a messy codebase. I've used both DOTween and iTween and I find DOTween much easier to use. I definitely think it's good to learn how it all works. I was just suggesting that you go look at the source code of these libraries to see how it works, and how they did it differently compared to you.
â sonny
May 3 at 18:32
Here is the DOTween GitHub repository and here is the LeanTween Github repository
â sonny
May 3 at 18:34