我正在开发C#XNA游戏,并且有以下代码根据线性插值的结果更新Sprite的alpha级别:
class AlphaProcess : GameProcess {
Sprite sprite;
public Sprite Sprite {
get { return sprite; }
set { sprite = value; }
}
float start;
public float Start {
get { return start; }
set { start = value;}
}
...<snip>...
public override void Update(float elapsed) {
if (FirstUpdate) {
Start = sprite.Alpha;
FirstUpdate = false;
}
Time += elapsed;
if (Time >= Duration)
Finished = true;
sprite.Alpha = MathHelper.Lerp(start,end,Time/Duration);
}
}
'开始'(浮点),'FirstUpdate'(bool),'时间'(浮点数),'持续时间'(浮点数)是AlphaProcess类的所有字段。 'Sprite'是一个包含比例,位置,方向,旋转等细节的类。
'Update'方法每秒调用60次,在指定时间段内为精灵计算新的alpha值。完成后,GameProcess将从队列中删除。
此代码工作正常,但它非常具体。我所拥有的Sprite类包含许多属性,它们可以在一段固定的时间内(旋转,位置(用于缓入和缩小))轻松地进行搜索。其中许多变量也是浮点数。创建多个“GameProcess”扩展类几乎完全相同,只是在一个不同的变量上似乎很愚蠢。但是,我想不出如何最好地重构这个,以便我可以只有一个指向浮动值的指针被修改,而不是特定的精灵和它的alpha / rotation / scale /无论什么值。
我已经通过C#reference / value /参数传递SO上的问题,并且在C#中知道你不能将引用存储为字段。调用Update的'GameProcessQueue'不知道正在更新的'GameProcess'的内部结构。 有没有一种更聪明的方法可以这样做,所以我可以概括/抽象类来更新另一个类的字段(比如这里,'Sprite'的'Alpha'字段)?
答案 0 :(得分:2)
基本上,您将回调函数传递给Update类 - 在C#术语中,您使用委托函数。在类中添加一个类似于
的属性public delegate SetThis(float value) { get; set; }
要设置它,在客户端代码中执行类似
的操作myAlphaProcess.SetThis = ( x => sprite.Alpha = x; )
在Update()函数中,你有
SetThis(MathHelper.Lerp(start,end,Time/Duration));
这个答案中的语法是 approximate ,因为我在这台计算机上没有编译器,但这就是你的做法。好的谷歌搜索将是“lambda C#”或“委托C#”。
答案 1 :(得分:2)
您的主要问题是您的AlphaProcess类具有低内聚力。它有三个主要角色,Tweening,Lerping和获取/设置Sprite Alpha Value。要改进您的设计,您需要将它们拆分为单独的分离类。
目标:将AlphaProcess转换为TweenProcess。
不是传递精灵,而是传递一个接口,通过它可以获取并设置它希望操作的浮点数。而不是直接调用Lerp,将其传递给接口或委托给Tween函数。 (我使用了一个接口而不是委托,因为我发现委托经常会产生垃圾,导致你的游戏口吃。)
补间课程为:
interface IFloatPropertySource
{
float FloatProperty { get; set; }
}
interface ITweenFunction
{
float Tween(float start, float end, float t);
}
class TweenProcess : GameProcess
{
float start;
IFloatPropertySource floatSource;
ITweenFunction tweenFunction;
public TweenProcess(IFloatPropertySource floatSource, ITweenFunction tweenFunction)
{
this.floatSource = floatSource;
this.tweenFunction = tweenFunction;
}
public override void Update(float elapsed) {
if (FirstUpdate) {
start = floatSource.FloatProperty;
FirstUpdate = false;
}
Time += elapsed;
if (Time >= Duration)
Finished = true;
floatSource.FloatProperty = tweenFunction.Tween(start, end, Time / Duration);
}
}
获取/设置alpha的类是:
class SpriteAlphaSource : IFloatPropertySource
{
Sprite sprite;
public SpriteAlphaSource(Sprite sprite)
{
this.sprite = sprite;
}
public float FloatProperty
{
get
{
return sprite.Alpha;
}
set
{
sprite.Alpha = value;
}
}
}
我知道你有点想避免上这堂课。但是围绕它的所有方式,例如通过反射都非常昂贵。在一个具有更多渲染而不仅仅是精灵的项目中,您可能希望将其与精灵分离,并使其在IRenderable等基础界面上运行。
规范机制是:
class Lerp : ITweenFunction
{
public float Tween(float start, float end, float t)
{
return MathHelper.Lerp(start, end, t);
}
}
答案 2 :(得分:1)
这可能是一个通用的方法......也许你应该做一个TweenManager并将Tweenables添加到一个集合中,只更新活动的Tweenables ......
这应该是正确的,但我没有检查过......也许泛型会给出任何问题......使用计算,但是应该避免使用通用委托......:)
Tweenable<float> Alpha = new Tweenable(defaultvalue);
public class Tweenable<T>
{
public T Value {
get { return Function(elapsed, source, target-source, duration);}
private set { source = value; elapsed = 0;}
}
public TweeningFunction<T> Function;
float elapsed, duration;
T source, target;
public void Update(float Elapsed)
{
if (elapsed<duration)
{
elapsed+= Elapsed;
if (elapsed > duration) elapsed = duration;
}
}
public void Start(T Source, T Target, float Duration)
{
...
}
}
public delegate T TweeningFunction( float timeElapsed,
T start,
T change,
float duration );
public static partial class Tweening {
public static class Quartic {
public static float EaseIn( float t, float b, float c, float d ) {
return c * (t /= d) * t * t * t + b;
}
public static float EaseOut( float t, float b, float c, float d ) {
return -c * ((t = t / d - 1) * t * t * t - 1) + b;
}
public static float EaseInOut( float t, float b, float c, float d ) {
if ( (t /= d / 2) < 1 ) {
return c / 2 * t * t * t * t + b;
}
return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
}
}
}