summaryrefslogtreecommitdiff
path: root/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tweening/Tweener.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Plugins/MonoGame.Extended/source/MonoGame.Extended.Tweening/Tweener.cs')
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tweening/Tweener.cs133
1 files changed, 133 insertions, 0 deletions
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tweening/Tweener.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tweening/Tweener.cs
new file mode 100644
index 0000000..61b55f9
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tweening/Tweener.cs
@@ -0,0 +1,133 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Reflection;
+using Microsoft.Xna.Framework;
+
+namespace MonoGame.Extended.Tweening
+{
+ public class Tweener : IDisposable
+ {
+ public Tweener()
+ {
+ }
+
+ public void Dispose()
+ {
+ CancelAll();
+ _activeTweens.Clear();
+ _memberCache.Clear();
+ }
+
+ public long AllocationCount { get; private set; }
+
+ private readonly List<Tween> _activeTweens = new List<Tween>();
+
+ public Tween<TMember> TweenTo<TTarget, TMember>(TTarget target, Expression<Func<TTarget, TMember>> expression, TMember toValue, float duration, float delay = 0f)
+ where TTarget : class
+ where TMember : struct
+ {
+ switch (toValue)
+ {
+ case Color toValueColor:
+ return (Tween<TMember>)(object)TweenTo<TTarget, Color, ColorTween>(target, expression as Expression<Func<TTarget, Color>>, toValueColor, duration, delay);
+ default:
+ return TweenTo<TTarget, TMember, LinearTween<TMember>>(target, expression, toValue, duration, delay);
+ }
+
+ }
+
+ public Tween<TMember> TweenTo<TTarget, TMember, TTween>(TTarget target, Expression<Func<TTarget, TMember>> expression, TMember toValue, float duration, float delay = 0f)
+ where TTarget : class
+ where TMember : struct
+ where TTween : Tween<TMember>
+ {
+ var memberExpression = (MemberExpression)expression.Body;
+ var memberInfo = memberExpression.Member;
+ var member = GetMember<TMember>(target, memberInfo.Name);
+ var activeTween = FindTween(target, member.Name);
+
+ activeTween?.Cancel();
+
+ AllocationCount++;
+ var tween = (TTween)Activator.CreateInstance(typeof(TTween),
+ BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null,
+ new object[]{target, duration, delay, member, toValue}, null);
+ _activeTweens.Add(tween);
+ return tween;
+ }
+
+ public void Update(float elapsedSeconds)
+ {
+ for (var i = _activeTweens.Count - 1; i >= 0; i--)
+ {
+ var tween = _activeTweens[i];
+
+ tween.Update(elapsedSeconds);
+
+ if (!tween.IsAlive)
+ _activeTweens.RemoveAt(i);
+ }
+ }
+
+ public Tween FindTween(object target, string memberName)
+ {
+ return _activeTweens.FirstOrDefault(t => t.Target == target && t.MemberName == memberName);
+ }
+
+ public void CancelAll()
+ {
+ foreach (var tween in _activeTweens)
+ tween.Cancel();
+ }
+
+ public void CancelAndCompleteAll()
+ {
+ foreach (var tween in _activeTweens)
+ tween.CancelAndComplete();
+ }
+
+ private struct TweenMemberKey
+ {
+#pragma warning disable 414
+ public object Target;
+ public string MemberName;
+#pragma warning restore 414
+ }
+
+ private readonly Dictionary<TweenMemberKey, TweenMember> _memberCache = new Dictionary<TweenMemberKey, TweenMember>();
+
+ private TweenMember<T> GetMember<T>(object target, string memberName)
+ where T : struct
+ {
+ var key = new TweenMemberKey { Target = target, MemberName = memberName };
+
+ if (_memberCache.TryGetValue(key, out var member))
+ return (TweenMember<T>) member;
+
+ member = CreateMember<T>(target, memberName);
+ _memberCache.Add(key, member);
+ return (TweenMember<T>) member;
+ }
+
+ private TweenMember<T> CreateMember<T>(object target, string memberName)
+ where T : struct
+ {
+ AllocationCount++;
+
+ var type = target.GetType();
+ var property = type.GetTypeInfo().GetProperty(memberName);
+
+ if (property != null)
+ return new TweenPropertyMember<T>(target, property);
+
+ var field = type.GetTypeInfo().GetField(memberName);
+
+ if (field != null)
+ return new TweenFieldMember<T>(target, field);
+
+ throw new InvalidOperationException($"'{memberName}' is not a property or field of the target");
+ }
+ }
+}