我有一个名为IComponent的基本核心接口。
public interface IComponent
{
void Initialize();
void Update();
void Draw(Color tint);
BoundingBox BoundingBox { get; }
bool Initialized { get; }
}
它可以被任何可绘制的类使用(按钮,文本,纹理......)
我想开发一组可应用于任何IComponent的效果,例如:
Shake(IComponent component, TimeSpan duration)
- >使Component在给定的持续时间内振动。它的工作原理是,首先存储传递的组件的中心位置,并在每次更新时将其抵消,直到持续时间结束。
Translate(IComponent component, Vector2 destination, TimeSpan timeToReach)
- >使Component在一定时间后移动到给定目标。它通过在每次更新时逐步抵消组件来工作。
你可以想象更多......
所以假设我想要一个特定的类(纹理:IComponent)来震动,同时也要移动到某一点。我想过可能像这样使用装饰器模式:
创建纹理=>将其包裹在摇动中(持续5秒)=>将其包裹在翻译中(持续10秒)
但是存在一些问题。
首先,Shake包装器可以单独使用静态静态纹理,但与Translate结合使用会失败,因为它会将纹理恢复到原来的位置并且不会正常翻译。
其次,虽然翻译需要10秒才能完成,但振动只需5秒钟,所以之后我不知道如何从链内自动移除Shake包装,最后当它翻译时也会移除完成后,留下原始纹理。
第三,装饰器模式隐藏了包装对象的任何特定功能,因此在使用Shake包装Texture后,我将无法调用Texture的setPixelColor(),除非我之前还创建了第二个对Texture的直接引用包裹它。
欢迎任何关于如何优雅地应对这种挑战的建议。感谢。
注意:实际上,我很可能将这些效果应用于所有IComponent创建对象的2%。
答案 0 :(得分:1)
沿着以下方向可能是另一种方法。
在你的例子中,摇动和平移都是动画效果。所有动画都需要某种形式的持续时间并应用于组件。这可能导致以下第一次采取:
public interface IAnimationEffect
{
IComponent targetComponent;
int Duration { get; set; }
}
动画的持续时间似乎是一种很好的初始方法,但是,当您需要组合动画时,每个动画需要在与其他动画相同的时间段内操纵其封装的IComponent的BoundingRect。为了能够像这样堆叠动画,需要指示动画效果的绘制方法来绘制动画的特定帧。
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的边界。
祝你好运!答案 1 :(得分:0)
你可以这样:
public Shake : ITransformation
{
}
public Translate : ITransformation
{
}
IComponent
接口还定义了应用于它的转换集合:
public interface IComponent
{
public IEnumerable<ITransformation> GetTransformation();
}
在Component
public MyCompo : IComponennt
{
public Draw(Color tint) {
//apply here transformations available in list of GetTransformation()
....
// draw
}
}
只是一个想法。
希望这有帮助。