在Lua中,由于对象通常实现为哈希表(如JS),因此我可以使用以下签名编写补间函数(或者更确切地说,它已经被编写了):
timer.tween(delay_in_s, object, table_of_target_values, algorithm)
timer.tween
将能够访问table_of_target_values
中枚举object
字段以保存其初始值。 然后,timer.update
还可以引用它们并设置它们的值。
如果是值类型,有没有办法在 C# 中保存对类字段的引用?我知道你不能通过引用传递属性(虽然你可以在 VB.NET 中传递),但字段是可以的。但是,如何在不使用反射的情况下保存它以供以后使用呢?
一种解决方法是传递 setter 闭包而不是对象本身,因此签名如下所示:
Timer.Tween<T>(double delay, T initialValue, T targetValue, Action<T> setter, Algorithm algorithm)
并像这样称呼它:
Timer.Tween(delay, obj.fld, targetValue, (x)=>obj.fld=x, Algorithm.Linear);
这是唯一的选择吗?
编辑:为了澄清我的情况,有Timer.Update(double delta)
更新Timer.Tween()
调用中引用的所有变量,直到它们达到目标值,这不仅仅是传递对Timer.Tween()
的引用的问题。
可以使用 ref
关键字在 C# 中使用引用调用:
public void Swap(ref int a, ref int b)
{
int c = b;
b = a;
a = c;
}
public void DoSwap()
{
int x = 1;
int y = 2;
Console.WriteLine(x + " " + y); // should write 1 2
Swap(ref x, ref y);
Console.WriteLine(x + " " + y); // should write 2 1
}
现在,仅仅因为您可以这样做并不意味着您应该这样做。通常,您应该尝试适应该语言的常见习语并接受这些习语,而不是通过ref
几乎每种方法。
我决定继续我的闭包方法,它看起来还不错,我什至会说它看起来很优雅。
/// <summary>
/// A static class that manages all active tweens.
/// </summary>
public abstract class Tween
{
private static HashSet<Tween> tweens = new HashSet<Tween>();
private Tween() {}
public static void Add<T>(double delay, T initialValue, T targetValue, Action<T> setter, Algorithm algorithm, Action onCompletion)
{
var t = new TweenImpl<T>(delay, initialValue, targetValue, setter, algorithm, onCompletion);
tweens.Add(t);
}
public static void Update(double delta)
{
var toRemove = new List<Tween>();
foreach (var t in tweens) {
if (t.UpdateTween(delta)) {
toRemove.Add(t);
}
}
foreach (var t in toRemove) {
tweens.Remove(t);
}
}
internal abstract bool UpdateTween(double delta);
/// <summary>
/// A class that represents tweens.
/// </summary>
private class TweenImpl<T> : Tween
{
private double delay;
private double accumulatedDelta;
private T initialValue;
private T valueRange;
private Action<T> setter;
private Algorithm algorithm;
private Action onCompletion;
internal TweenImpl(double delay, T initialValue, T targetValue, Action<T> setter, Algorithm algorithm, Action onCompletion)
{
this.delay = delay;
this.initialValue = initialValue;
this.valueRange = Operator.Subtract(targetValue, initialValue);
this.setter = setter;
this.algorithm = algorithm;
this.onCompletion = onCompletion;
this.accumulatedDelta = 0.0;
this.setter.Invoke(this.initialValue);
}
internal override bool UpdateTween(double delta) {
var toRemove = false;
this.accumulatedDelta += delta;
if (this.accumulatedDelta >= this.delay){
this.accumulatedDelta = this.delay;
toRemove = true;
}
var percentage = (double)this.accumulatedDelta/this.delay;
percentage = this.algorithm.Adjust(percentage);
var result = Operator.AddAlternative(this.initialValue, Operator.MultiplyAlternative(percentage, this.valueRange));
this.setter.Invoke(result);
if (toRemove) {
this.onCompletion.Invoke();
}
return toRemove;
}
}
}
我遇到的唯一问题是你不能将 T 限制为数字类型,所以我使用 John Skeet 的 MiscUtil 在运行时生成 lambda。当然,这意味着如果有人试图补间字符串,程序将崩溃,但这是我愿意忍受的。
你这样称呼它(我在进度条上测试了这个):
Tween.Add(15.0, prgTest.Minimum, prgTest.Maximum, x=>prgTest.Value=x, Algorithm.Linear, ()=>{
running = false;
MessageBox.Show("Done");
});
然后简单地打电话
Tween.Update(delta);
以自动补间变量。