C#类组织/结构 - 推广执行循环线性插值的类

时间:2012-04-10 14:55:11

标签: c# oop parameters xna

我正在开发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'字段)?

3 个答案:

答案 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;
            }
        }
    }