Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 18 additions & 4 deletions src/LitMotion/Assets/LitMotion/Runtime/ManualMotionDispatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,14 @@ public sealed class ManualMotionDispatcher
readonly ManualMotionDispatcherScheduler scheduler;
readonly Dictionary<Type, IUpdateRunner> runners = new();

// Reused buffer so Update/Reset iterate a snapshot of the runners instead of the live
// dictionary. A motion callback fired during iteration can schedule the first motion of a
// new type, which lazily adds a runner (see GetOrCreateRunner) and would otherwise throw
// "InvalidOperationException: Collection was modified" mid-enumeration. Reused rather than
// allocated per call; AddRange(runners.Values) copies via ICollection.CopyTo and the
// Dictionary caches its ValueCollection, so this stays allocation-free after warmup.
readonly List<IUpdateRunner> runnerBuffer = new();

public ManualMotionDispatcher()
{
scheduler = new(this);
Expand Down Expand Up @@ -79,21 +87,27 @@ public void Update(double deltaTime)
{
time += deltaTime;

foreach (var kv in runners)
runnerBuffer.Clear();
runnerBuffer.AddRange(runners.Values);
for (int i = 0; i < runnerBuffer.Count; i++)
{
kv.Value.Update(time, time, time);
runnerBuffer[i].Update(time, time, time);
}
runnerBuffer.Clear();
}

/// <summary>
/// Cancel all motions and reset data.
/// </summary>
public void Reset()
{
foreach (var kv in runners)
runnerBuffer.Clear();
runnerBuffer.AddRange(runners.Values);
for (int i = 0; i < runnerBuffer.Count; i++)
{
kv.Value.Reset();
runnerBuffer[i].Reset();
}
runnerBuffer.Clear();

time = 0;
}
Expand Down