Skip to content

Commit

Permalink
Merge pull request #36 from AnnulusGames/feature-multi-state
Browse files Browse the repository at this point in the history
Add: multi-state support for BindWithState
  • Loading branch information
yn01-dev authored Jan 8, 2024
2 parents 0b102d4 + 01001c4 commit e567775
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,34 @@ namespace LitMotion
/// </summary>
public struct MotionCallbackData
{
public bool HasState;
public byte StateCount;
public bool IsCallbackRunning;
public bool CancelOnError;
public object State;

public object State1;
public object State2;
public object State3;

public object UpdateAction;
public Action OnCompleteAction;
public Action OnCancelAction;

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void InvokeUnsafe<TValue>(in TValue value) where TValue : unmanaged
{
if (HasState)
{
UnsafeUtility.As<object, Action<TValue, object>>(ref UpdateAction)?.Invoke(value, State);
}
else
switch (StateCount)
{
UnsafeUtility.As<object, Action<TValue>>(ref UpdateAction)?.Invoke(value);
case 0:
UnsafeUtility.As<object, Action<TValue>>(ref UpdateAction)?.Invoke(value);
break;
case 1:
UnsafeUtility.As<object, Action<TValue, object>>(ref UpdateAction)?.Invoke(value, State1);
break;
case 2:
UnsafeUtility.As<object, Action<TValue, object, object>>(ref UpdateAction)?.Invoke(value, State1, State2);
break;
case 3:
UnsafeUtility.As<object, Action<TValue, object, object, object>>(ref UpdateAction)?.Invoke(value, State1, State2, State3);
break;
}
}
}
Expand Down
85 changes: 83 additions & 2 deletions src/LitMotion/Assets/LitMotion/Runtime/MotionBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,47 @@ public MotionHandle BindWithState<TState>(TState state, Action<TValue, TState> a
return Schedule(scheduler, data, callbacks);
}

/// <summary>
/// Create motion and bind it to a specific object. Unlike the regular Bind method, it avoids allocation by closure by passing an object.
/// </summary>
/// <typeparam name="TState1">Type of state</typeparam>
/// <typeparam name="TState2">Type of state</typeparam>
/// <param name="state">Motion state</param>
/// <param name="action">Action that handles binding</param>
/// <returns>Handle of the created motion data.</returns>
public MotionHandle BindWithState<TState1, TState2>(TState1 state1, TState2 state2, Action<TValue, TState1, TState2> action)
where TState1 : class
where TState2 : class
{
CheckBuffer();
var callbacks = BuildCallbackData(state1, state2, action);
var scheduler = buffer.Scheduler;
var data = BuildMotionData();
return Schedule(scheduler, data, callbacks);
}


/// <summary>
/// Create motion and bind it to a specific object. Unlike the regular Bind method, it avoids allocation by closure by passing an object.
/// </summary>
/// <typeparam name="TState1">Type of state</typeparam>
/// <typeparam name="TState2">Type of state</typeparam>
/// <typeparam name="TState3">Type of state</typeparam>
/// <param name="state">Motion state</param>
/// <param name="action">Action that handles binding</param>
/// <returns>Handle of the created motion data.</returns>
public MotionHandle BindWithState<TState1, TState2, TState3>(TState1 state1, TState2 state2, TState3 state3, Action<TValue, TState1, TState2, TState3> action)
where TState1 : class
where TState2 : class
where TState3 : class
{
CheckBuffer();
var callbacks = BuildCallbackData(state1, state2, state3, action);
var scheduler = buffer.Scheduler;
var data = BuildMotionData();
return Schedule(scheduler, data, callbacks);
}

/// <summary>
/// Preserves the internal buffer and prevents the builder from being automatically destroyed after creating the motion data.
/// Calling this allows you to create the motion multiple times, but you must call the Dispose method to destroy the builder after use.
Expand Down Expand Up @@ -338,8 +379,48 @@ internal MotionCallbackData BuildCallbackData<TState>(TState state, Action<TValu
{
var callbacks = new MotionCallbackData
{
HasState = true,
State = state,
StateCount = 1,
State1 = state,
UpdateAction = action,
OnCancelAction = buffer.OnCancel,
OnCompleteAction = buffer.OnComplete,
CancelOnError = buffer.CancelOnError
};

return callbacks;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal MotionCallbackData BuildCallbackData<TState1, TState2>(TState1 state1, TState2 state2, Action<TValue, TState1, TState2> action)
where TState1 : class
where TState2 : class
{
var callbacks = new MotionCallbackData
{
StateCount = 2,
State1 = state1,
State2 = state2,
UpdateAction = action,
OnCancelAction = buffer.OnCancel,
OnCompleteAction = buffer.OnComplete,
CancelOnError = buffer.CancelOnError
};

return callbacks;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal MotionCallbackData BuildCallbackData<TState1, TState2, TState3>(TState1 state1, TState2 state2, TState3 state3, Action<TValue, TState1, TState2, TState3> action)
where TState1 : class
where TState2 : class
where TState3 : class
{
var callbacks = new MotionCallbackData
{
StateCount = 3,
State1 = state1,
State2 = state2,
State3 = state3,
UpdateAction = action,
OnCancelAction = buffer.OnCancel,
OnCompleteAction = buffer.OnComplete,
Expand Down
42 changes: 40 additions & 2 deletions src/LitMotion/Assets/LitMotion/Tests/Runtime/BindTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,49 @@ public IEnumerator Test_BindWithState()
{
var target = new TestClass();
var endValue = 10f;
LMotion.Create(0f, endValue, 1f).BindWithState(target, (x, target) => target.Value = x);
yield return new WaitForSeconds(1.1f);
LMotion.Create(0f, endValue, 0.5f).BindWithState(target, (x, target) =>
{
target.Value = x;
});
yield return new WaitForSeconds(0.6f);
Assert.AreApproximatelyEqual(target.Value, endValue);
}

[UnityTest]
public IEnumerator Test_BindWithState_2()
{
var target1 = new TestClass();
var target2 = new TestClass();
var endValue = 10f;
LMotion.Create(0f, endValue, 0.5f).BindWithState(target1, target2, (x, target1, target2) =>
{
target1.Value = x;
target2.Value = x;
});
yield return new WaitForSeconds(0.6f);
Assert.AreApproximatelyEqual(target1.Value, endValue);
Assert.AreApproximatelyEqual(target2.Value, endValue);
}

[UnityTest]
public IEnumerator Test_BindWithState_3()
{
var target1 = new TestClass();
var target2 = new TestClass();
var target3 = new TestClass();
var endValue = 10f;
LMotion.Create(0f, endValue, 0.5f).BindWithState(target1, target2, target3, (x, target1, target2, target3) =>
{
target1.Value = x;
target2.Value = x;
target3.Value = x;
});
yield return new WaitForSeconds(0.6f);
Assert.AreApproximatelyEqual(target1.Value, endValue);
Assert.AreApproximatelyEqual(target2.Value, endValue);
Assert.AreApproximatelyEqual(target3.Value, endValue);
}

[UnityTest]
public IEnumerator Test_BindToProgress()
{
Expand Down

0 comments on commit e567775

Please sign in to comment.