Skip to content

Commit

Permalink
Merge pull request #63 from AnnulusGames/feature-delay-type
Browse files Browse the repository at this point in the history
Feature: DelayType
  • Loading branch information
AnnulusGames authored Jan 17, 2024
2 parents a17bdbe + 82403c0 commit 7d6e6ac
Show file tree
Hide file tree
Showing 6 changed files with 134 additions and 24 deletions.
17 changes: 17 additions & 0 deletions src/LitMotion/Assets/LitMotion/Runtime/DelayType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace LitMotion
{
/// <summary>
/// Specifies the behavior of WithDelay.
/// </summary>
public enum DelayType : byte
{
/// <summary>
/// Delay when starting playback
/// </summary>
FirstLoop = 0,
/// <summary>
/// Delay every loop
/// </summary>
EveryLoop = 1,
}
}
11 changes: 11 additions & 0 deletions src/LitMotion/Assets/LitMotion/Runtime/DelayType.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public struct MotionData<TValue, TOptions>
public MotionTimeKind TimeKind;
public float Delay;
public int Loops;
public DelayType DelayType;
public LoopType LoopType;

public TValue StartValue;
Expand Down
18 changes: 18 additions & 0 deletions src/LitMotion/Assets/LitMotion/Runtime/MotionBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public static void Return(MotionBuilderBuffer<TValue, TOptions> buffer)
buffer.Duration = default;
buffer.Ease = default;
buffer.Delay = default;
buffer.DelayType = default;
buffer.Loops = 1;
buffer.LoopType = default;
buffer.StartValue = default;
Expand All @@ -50,6 +51,7 @@ public static void Return(MotionBuilderBuffer<TValue, TOptions> buffer)
public float Duration;
public Ease Ease;
public float Delay;
public DelayType DelayType;
public int Loops = 1;
public LoopType LoopType;

Expand Down Expand Up @@ -111,6 +113,21 @@ public readonly MotionBuilder<TValue, TOptions, TAdapter> WithDelay(float delay)
return this;
}

/// <summary>
/// Specify the delay time when the motion starts.
/// </summary>
/// <param name="delay">Delay time (seconds)</param>
/// <param name="delayType">Delay type</param>
/// <returns>This builder to allow chaining multiple method calls.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly MotionBuilder<TValue, TOptions, TAdapter> WithDelay(float delay, DelayType delayType)
{
CheckBuffer();
buffer.Delay = delay;
buffer.DelayType = delayType;
return this;
}

/// <summary>
/// Specify the number of times the motion is repeated. If specified as less than 0, the motion will continue to play until manually completed or canceled.
/// </summary>
Expand Down Expand Up @@ -344,6 +361,7 @@ internal MotionData<TValue, TOptions> BuildMotionData()
Duration = buffer.Duration,
Ease = buffer.Ease,
Delay = buffer.Delay,
DelayType = buffer.DelayType,
Loops = buffer.Loops,
LoopType = buffer.LoopType,
Status = MotionStatus.Scheduled,
Expand Down
75 changes: 57 additions & 18 deletions src/LitMotion/Assets/LitMotion/Runtime/MotionUpdateJob.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,42 +42,77 @@ public void Execute([AssumeRange(0, int.MaxValue)] int index)
};

var motionTime = currentTime - ptr->StartTime;
var time = motionTime - ptr->Delay;

double t;
bool isCompleted;
bool isDelayed;
int completedLoops;
int clampedCompletedLoops;

if (Hint.Unlikely(ptr->Duration <= 0f))
{
isCompleted = time > 0f;
if (isCompleted)
if (ptr->DelayType == DelayType.FirstLoop || ptr->Delay == 0f)
{
t = 1f;
completedLoops = ptr->Loops;
var time = motionTime - ptr->Delay;
isCompleted = ptr->Loops >= 0 && time > 0f;
if (isCompleted)
{
t = 1f;
completedLoops = ptr->Loops;
}
else
{
t = 0f;
completedLoops = time < 0f ? -1 : 0;
}
clampedCompletedLoops = ptr->Loops < 0 ? math.max(0, completedLoops) : math.clamp(completedLoops, 0, ptr->Loops);
isDelayed = time < 0;
}
else
{
t = 0f;
completedLoops = time < 0f ? -1 : 0;
completedLoops = (int)math.floor(motionTime / ptr->Delay);
clampedCompletedLoops = ptr->Loops < 0 ? math.max(0, completedLoops) : math.clamp(completedLoops, 0, ptr->Loops);
isCompleted = ptr->Loops >= 0 && clampedCompletedLoops > ptr->Loops - 1;
isDelayed = !isCompleted;
t = isCompleted ? 1f : 0f;
}
clampedCompletedLoops = ptr->Loops < 0 ? math.max(0, completedLoops) : math.clamp(completedLoops, 0, ptr->Loops);
}
else
{
completedLoops = (int)math.floor(time / ptr->Duration);
clampedCompletedLoops = ptr->Loops < 0 ? math.max(0, completedLoops) : math.clamp(completedLoops, 0, ptr->Loops);
isCompleted = ptr->Loops >= 0 && clampedCompletedLoops > ptr->Loops - 1;

if (isCompleted)
if (ptr->DelayType == DelayType.FirstLoop)
{
t = 1f;
var time = motionTime - ptr->Delay;
completedLoops = (int)math.floor(time / ptr->Duration);
clampedCompletedLoops = ptr->Loops < 0 ? math.max(0, completedLoops) : math.clamp(completedLoops, 0, ptr->Loops);
isCompleted = ptr->Loops >= 0 && clampedCompletedLoops > ptr->Loops - 1;
isDelayed = time < 0f;

if (isCompleted)
{
t = 1f;
}
else
{
var currentLoopTime = time - ptr->Duration * clampedCompletedLoops;
t = math.clamp(currentLoopTime / ptr->Duration, 0f, 1f);
}
}
else
{
var currentLoopTime = time - ptr->Duration * clampedCompletedLoops;
t = math.clamp(currentLoopTime / ptr->Duration, 0f, 1f);
var currentLoopTime = math.fmod(motionTime, ptr->Duration + ptr->Delay) - ptr->Delay;
completedLoops = (int)math.floor(motionTime / (ptr->Duration + ptr->Delay));
clampedCompletedLoops = ptr->Loops < 0 ? math.max(0, completedLoops) : math.clamp(completedLoops, 0, ptr->Loops);
isCompleted = ptr->Loops >= 0 && clampedCompletedLoops > ptr->Loops - 1;
isDelayed = currentLoopTime < 0;

if (isCompleted)
{
t = 1f;
}
else
{
t = math.clamp(currentLoopTime / ptr->Duration, 0f, 1f);
}
}
}

Expand All @@ -97,11 +132,15 @@ public void Execute([AssumeRange(0, int.MaxValue)] int index)
break;
}

if (ptr->Loops > 0 && time >= ptr->Duration * ptr->Loops)
var totalDuration = ptr->DelayType == DelayType.FirstLoop
? ptr->Delay + ptr->Duration * ptr->Loops
: (ptr->Delay + ptr->Duration) * ptr->Loops;

if (ptr->Loops > 0 && motionTime >= totalDuration)
{
ptr->Status = MotionStatus.Completed;
}
else if (motionTime < ptr->Delay)
else if (isDelayed)
{
ptr->Status = MotionStatus.Delayed;
}
Expand Down
36 changes: 30 additions & 6 deletions src/LitMotion/Assets/LitMotion/Tests/Runtime/DelayTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,47 @@ public class DelayTest
[UnityTest]
public IEnumerator Test_Delay()
{
var t = Time.time;
var t = Time.timeAsDouble;
yield return LMotion.Create(0f, 1f, 0.5f)
.WithDelay(0.5f)
.RunWithoutBinding()
.BindToUnityLogger()
.ToYieldInteraction();
Assert.IsTrue(Time.time - t >= 1f);
Assert.That(Time.timeAsDouble - t, Is.GreaterThan(0.95).And.LessThan(1.1));
}

[UnityTest]
public IEnumerator Test_Delay_WithZeroDuration()
{
var t = Time.time;
var t = Time.timeAsDouble;
yield return LMotion.Create(0f, 1f, 0f)
.WithDelay(1f)
.RunWithoutBinding()
.BindToUnityLogger()
.ToYieldInteraction();
Assert.IsTrue(Time.time - t >= 1f);
Assert.That(Time.timeAsDouble - t, Is.GreaterThan(0.95).And.LessThan(1.1));
}

[UnityTest]
public IEnumerator Test_Delay_EveryLoop()
{
var t = Time.timeAsDouble;
yield return LMotion.Create(0f, 1f, 0.5f)
.WithLoops(2)
.WithDelay(0.5f, DelayType.EveryLoop)
.BindToUnityLogger()
.ToYieldInteraction();
Assert.That(Time.timeAsDouble - t, Is.GreaterThan(1.95).And.LessThan(2.1));
}

[UnityTest]
public IEnumerator Test_Delay_EveryLoop_WithZeroDuration()
{
var t = Time.timeAsDouble;
yield return LMotion.Create(0f, 1f, 0f)
.WithLoops(3)
.WithDelay(0.5f, DelayType.EveryLoop)
.BindToUnityLogger()
.ToYieldInteraction();
Assert.That(Time.timeAsDouble - t, Is.GreaterThan(1.45).And.LessThan(1.6));
}
}
}

0 comments on commit 7d6e6ac

Please sign in to comment.