Tweener.cs 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Linq.Expressions;
  5. using System.Reflection;
  6. using Microsoft.Xna.Framework;
  7. namespace MonoGame.Extended.Tweening
  8. {
  9. public class Tweener : IDisposable
  10. {
  11. public Tweener()
  12. {
  13. }
  14. public void Dispose()
  15. {
  16. CancelAll();
  17. _activeTweens.Clear();
  18. _memberCache.Clear();
  19. }
  20. public long AllocationCount { get; private set; }
  21. private readonly List<Tween> _activeTweens = new List<Tween>();
  22. public Tween<TMember> TweenTo<TTarget, TMember>(TTarget target, Expression<Func<TTarget, TMember>> expression, TMember toValue, float duration, float delay = 0f)
  23. where TTarget : class
  24. where TMember : struct
  25. {
  26. switch (toValue)
  27. {
  28. case Color toValueColor:
  29. return (Tween<TMember>)(object)TweenTo<TTarget, Color, ColorTween>(target, expression as Expression<Func<TTarget, Color>>, toValueColor, duration, delay);
  30. default:
  31. return TweenTo<TTarget, TMember, LinearTween<TMember>>(target, expression, toValue, duration, delay);
  32. }
  33. }
  34. public Tween<TMember> TweenTo<TTarget, TMember, TTween>(TTarget target, Expression<Func<TTarget, TMember>> expression, TMember toValue, float duration, float delay = 0f)
  35. where TTarget : class
  36. where TMember : struct
  37. where TTween : Tween<TMember>
  38. {
  39. var memberExpression = (MemberExpression)expression.Body;
  40. var memberInfo = memberExpression.Member;
  41. var member = GetMember<TMember>(target, memberInfo.Name);
  42. var activeTween = FindTween(target, member.Name);
  43. activeTween?.Cancel();
  44. AllocationCount++;
  45. var tween = (TTween)Activator.CreateInstance(typeof(TTween),
  46. BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null,
  47. new object[]{target, duration, delay, member, toValue}, null);
  48. _activeTweens.Add(tween);
  49. return tween;
  50. }
  51. public void Update(float elapsedSeconds)
  52. {
  53. for (var i = _activeTweens.Count - 1; i >= 0; i--)
  54. {
  55. var tween = _activeTweens[i];
  56. tween.Update(elapsedSeconds);
  57. if (!tween.IsAlive)
  58. _activeTweens.RemoveAt(i);
  59. }
  60. }
  61. public Tween FindTween(object target, string memberName)
  62. {
  63. return _activeTweens.FirstOrDefault(t => t.Target == target && t.MemberName == memberName);
  64. }
  65. public void CancelAll()
  66. {
  67. foreach (var tween in _activeTweens)
  68. tween.Cancel();
  69. }
  70. public void CancelAndCompleteAll()
  71. {
  72. foreach (var tween in _activeTweens)
  73. tween.CancelAndComplete();
  74. }
  75. private struct TweenMemberKey
  76. {
  77. #pragma warning disable 414
  78. public object Target;
  79. public string MemberName;
  80. #pragma warning restore 414
  81. }
  82. private readonly Dictionary<TweenMemberKey, TweenMember> _memberCache = new Dictionary<TweenMemberKey, TweenMember>();
  83. private TweenMember<T> GetMember<T>(object target, string memberName)
  84. where T : struct
  85. {
  86. var key = new TweenMemberKey { Target = target, MemberName = memberName };
  87. if (_memberCache.TryGetValue(key, out var member))
  88. return (TweenMember<T>) member;
  89. member = CreateMember<T>(target, memberName);
  90. _memberCache.Add(key, member);
  91. return (TweenMember<T>) member;
  92. }
  93. private TweenMember<T> CreateMember<T>(object target, string memberName)
  94. where T : struct
  95. {
  96. AllocationCount++;
  97. var type = target.GetType();
  98. var property = type.GetTypeInfo().GetProperty(memberName);
  99. if (property != null)
  100. return new TweenPropertyMember<T>(target, property);
  101. var field = type.GetTypeInfo().GetField(memberName);
  102. if (field != null)
  103. return new TweenFieldMember<T>(target, field);
  104. throw new InvalidOperationException($"'{memberName}' is not a property or field of the target");
  105. }
  106. }
  107. }