动画仍然可以完美地划分为逻辑和渲染。动画的抽象数据状态将是图形API渲染动画所必需的信息。
例如,在2D游戏中,它可能是一个矩形区域,该区域标记了显示需要绘制的Sprite工作表的当前部分的区域(当您的工作表由30张80x80的图形组成,其中包含角色的各个步骤跳跃,坐下,移动等)。它也可以是您不需要渲染的任何类型的数据,但可能是用于管理动画本身的数据,例如直到当前动画步骤到期为止的时间或动画的名称(“行走”,“站立”等等。所有这些都可以用您想要的任何方式表示。这就是逻辑部分。
在渲染部分,您只需照常进行操作,从模型中获取该矩形,然后使用渲染器实际执行对图形API的调用。
在代码中(此处使用C ++语法):
class Sprite //Model
{
private:
Rectangle subrect;
Vector2f position;
//etc.
public:
Rectangle GetSubrect()
{
return subrect;
}
//etc.
};
class AnimatedSprite : public Sprite, public Updatable //arbitrary interface for classes that need to change their state on a regular basis
{
AnimationController animation_controller;
//etc.
public:
void Update()
{
animation_controller.Update(); //Good OOP design ;) It will take control of changing animations in time etc. for you
this.SetSubrect(animation_controller.GetCurrentAnimation().GetRect());
}
//etc.
};
那就是数据。渲染器将获取该数据并进行绘制。由于正常的精灵和动画的精灵都是以相同的方式绘制的,因此您可以在此处使用多态!
class Renderer
{
//etc.
public:
void Draw(const Sprite &spr)
{
graphics_api_pointer->Draw(spr.GetAllTheDataThatINeed());
}
};
TMV:
我想出了另一个例子。假设您有一个RPG。例如,代表世界地图的模型可能需要将角色在世界中的位置存储为地图上的图块坐标。但是,当您移动角色时,它们一次走几像素到下一个正方形。您是否将此“拼贴之间”位置存储在动画对象中?当角色最终“到达”地图上的下一个平铺坐标时,如何更新模型?
世界地图不直接知道玩家的位置(它没有Vector2f或类似直接存储玩家position =的东西,而是直接引用了玩家对象本身,而后者又是从AnimatedSprite派生的因此您可以轻松地将其传递给渲染器,并从中获取所有必要的数据。
不过,总的来说,您的tilemap不能仅做所有事情-我会有一个“ TileMap”类来管理所有的tile,也许还可以在我移交给它的对象之间进行碰撞检测,地图上的瓷砖。然后,我将有另一个“ RPGMap”类,或者您想调用它,它同时具有对tilemap和播放器的引用,并对播放器和您的播放器进行实际的Update()调用。平铺地图。
当玩家移动时,您想如何更新模型取决于您要做什么。
是否允许您的播放器在瓷砖之间独立移动(塞尔达风格)?只需处理输入,并在每一帧中相应地移动播放器。还是您想让玩家按下“向右”,并且您的角色自动向右移动一个图块?让您的RPGMap类插值玩家的位置,直到他到达目的地,同时锁定所有的移动键输入处理。
无论哪种方式,如果您想使自己更轻松,则所有模型如果实际上需要一些逻辑来更新自身(而不只是更改变量的值),都将具有Update()方法-您不会放弃控制器以这种方式在MVC模式中,您只需将代码从“上一步”(控制器)向下移动到模型,并且所有控制器要做的就是调用模型的Update()方法(在本例中,控制器将是RPGMap)。您仍然可以轻松地交换逻辑代码-您可以直接更改类的代码,或者如果您需要完全不同的行为,则可以从模型类派生并仅重写Update()方法。
这种方法减少了方法调用和类似的事情-曾经是纯MVC模式的主要缺点之一(您最终非常频繁地调用GetThis()GetThat())-它使代码既长又冗长。一点点都更难阅读,也更慢-即使您的编译器可能会优化许多类似的东西来解决。