我有一个称为IComponent的基本核心接口。

public interface IComponent
{
    void Initialize();

    void Update();

    void Draw(Color tint);

    BoundingBox BoundingBox { get; }

    bool Initialized { get; }
}


任何可绘制的类(按钮,文本,纹理等)都会使用它。

我想开发一套可以应用于任何IComponent的效果,例如:

Shake(IComponent component, TimeSpan duration)


->使组件振动给定的持续时间。它的工作方式是最初存储传递的组件的中心位置,并在每次更新时偏移它,直到持续时间结束。

Translate(IComponent component, Vector2 destination, TimeSpan timeToReach)


->使组件在一定时间后移至给定的目的地。它通过在每次更新时逐渐偏移组件来工作。

您可以想象更多...

因此,假设我希望某个类(Texture:IComponent)摇动,同时还要移动到某个点。我想到可能像这样使用装饰器模式:

创建纹理=>摇动包装(持续时间为5秒)=>摇动包装(持续时间为10秒)

但这有一些问题。

首先,Shake包装器可以单独使用静态静止纹理即可正常工作,但与Translate结合使用时将失败,因为它将使纹理不断返回其原始位置并且无法正确平移。

其次,虽然翻译需要10秒钟才能完成,但振动仅需要5秒钟,所以此后我不知道如何自动从链中删除Shake包装器,并且在完成后最终也要删除Translate,因此原始纹理的后面。

第三,装饰器模式隐藏了包装对象的任何特定功能,因此在用Shake包装Texture之后,我将无法调用Texture的setPixelColor(),除非在包装它之前也创建了另一个对Texture的直接引用。

欢迎提供任何有关如何优雅地应对此类挑战的建议。谢谢。

注意:实际上,我可能会将这些效果应用到所有创建的IComponent对象的2%。

最佳答案

也许是以下方法的另一种方法。

在您的示例中,震动和平移都是动画效果。所有动画都需要某种形式的持续时间,并应用于组件。这可能导致以下首先采取的措施:

public interface IAnimationEffect
{
    IComponent targetComponent;
    int Duration { get; set; }
}


动画的持续时间似乎是一种不错的初始方法,但是,当您需要组合动画时,每个动画都需要在与其他动画相同的时间内操纵其封装的IComponent的BoundingRect。为了能够像这样堆叠动画,需要指示动画效果的draw方法来绘制动画的特定帧。

IAnimationEffect接口的改进版本为:

public interface IAnimationEffect
{
    IComponent targetComponent;
    int StartFrame { get; set; }
    int EndFrame { get; set; }
    void CalculateFrame(int frame);
}


哪个类负责绘制您的IComponents(现在将其称为DrawingEngine),现在也负责保存所有适用动画效果的内部列表。它还需要在每秒帧数中具有某种时间轴和渲染逻辑,以便可以执行特定动画帧的计算。

public class ShakeAnimationEffect : IAnimationEffect
{
    public IComponent TargetComponent { get; set; }
    public int StartFrame { get; set; }
    public int EndFrame { get; set; }
    // Some shake specific properties can be added, to control the type of vibration etc
    // (could be a rotating vibration an updown shake), but these really should have dedicated classes of their own.

    public void CalculateFrame(int frame)
    {
        // your maths manipulations for calculating the bounds of the IComponent go here
    }
}

public class TranslateAnimation : IAnimationEffect
{
    public IComponent targetComponent { get; set; }
    public int StartFrame { get; set; }
    public int EndFrame { get; set; }
    public int TranslateX { get; set; }
    public int TranslateY { get; set; }

    public void CalculateFrame(int frame)
    {
        // your maths manipulations for calculating the bounds of the IComponent go here
    }
}


如上所述,您的AnimationEffects类对绘制IComponents不承担任何责任,而这仍然是DrawingEngine的责任。

您需要在DrawingLoop之前直接引入一个循环,以运行所有动画效果,而要做的就是更新恰好具有与之关联的动画效果的对象的边界。然后,像往常一样继续绘制对象。

假设您有以下内容:

        // suppose your drawing engine has the following:
       List<IComponent> components = new List<IComponent>();

        //now just add the following
       List<IAnimationEffect> animationEffects = new List<IAnimationEffect>();

        // create some animation effects and register them to your list
        animationEffects.Add(new ShakeAnimationEffect
          {
              TargetComponent = Lorry,
              StartFrame = 0,
              EndFrame = 150, //At 30 frames per second, this would be a 5sec animation
          }
        );

        //sample pseudecode
        RunAnimationCalculations();
        RunDrawingLoop();


RunAnimationCalculations要做的全部工作就是遍历您的animationEffects集合,并在当前帧中运行Calculate传递,以便在随后绘制该帧之前更新IComponent的边界。

祝好运!

10-01 03:53