支持和反对游戏对象绘制和更新的原因是什么?例如,如果你有一个游戏,玩家在屏幕上有一个位置,为什么不拥有一个包罗万象的类:
public class Player {
private int x, y, xVelocity, yVelocity;
private Sprite s;
//...
public Player() {
// load the sprite here, somehow?
}
public void draw(CustomGraphicsClass g) {
g.draw(s, x, y);
}
public void update(long timeElapsed) {
x += (xVelocity * timeElapsed);
y += (yVelocity * timeElapsed);
}
}
这个设计有什么问题?有哪些垮台或担忧?你怎么能更好地写这样的东西,或者在游戏中更好地设计这类东西呢?
另外,有点连接,你将如何实现加载Sprite图像?
此外,您如何实现两个Player
之间的碰撞?
(我应该把这两个额外的两个问题分成新问题,呵呵?)
答案 0 :(得分:18)
它将所有渲染和逻辑代码耦合在一起,除了在概念上与同一个“实体”相关联之外几乎没有共同之处。随着你的课程变得越来越大,你可以发现自己拥有一个巨大的整体Player
,这是一个难以维持的噩梦。沿着域边界(渲染,AI)拆分使得它更易于管理而不必放弃太多,因为这些域没有很多重叠。
除了可维护性之外,还有其他一些考虑因素:
如果你想在不同的线程上处理渲染和AI,将它们的状态混合到同一个类中只是讨厌令人讨厌的多线程问题。
如果你使用的是像C ++这样的语言,那么像这样的高度耦合的类可以扼杀你的编译时间。
根据您的代码和对象在内存中的布局方式,将对象拆分为每个域的单独组件可以提供更好的缓存一致性和更好的性能。
如果你很好奇,这里是lots more info。
答案 1 :(得分:3)
可能的缺点可能是违反了关注点。我认为,理想情况下,对象不应该知道它们是如何渲染的,因此渲染引擎可以单独从游戏对象中进行调整......但实际上,这些东西往往过于相互依赖以这种方式分开。
答案 2 :(得分:3)
假设每次更新对象(即播放器)时都应该
1)重绘自己
2)通知其他单位已更新
3)将“update-action”写入历史/日志/其他任何内容(也许你希望有可能在整个游戏结束后重现整个游戏,就像电影一样)。
...
n)您的Player-object与其环境之间的任何其他交互。
在所有这些情况下,您都必须更改更新方法,例如:
public void update(long timeElapsed) {
dosmth();
redraw();
notifyUnits();
updateHistory();
}
这很烦人。这就是为什么在这种情况下应该使用观察者模式。您的对象应该通知所有侦听器它已更新。监听器(GraphicsContext,History,units)将以适当的方式做出反应,并且您的程序将易于维护,因为它的所有部分仅负责1个具体任务。
答案 3 :(得分:2)
没关系,但是如果你有许多不同的对象,都需要更新的精灵,位置和速度,那么就会有很多复制。您可以将位置,速度推入基类,但这会使运动类型变硬。更好的是将动作与对象本身分开。
例如,考虑一个球上下弹跳。仅凭速度模拟并不容易。相反,创建一个Motion类,使用ConstantMotiion(用于速度)和BounceMotion用于弹跳。然后,运动类负责逐帧更新对象的位置状态。
答案 4 :(得分:1)
查看系统中的大多数对象应该只修改特定于引擎的描述符(速度,活动动画)并允许引擎处理实际效果的可能性很有用。这有一些例外(通常是子弹和其他单击效果),但总的来说这些系统依赖于更新游戏的单一通用标记。其原因主要是,正如mdma在他对Roman的回答的评论中指出的那样,实际的引擎比简单的单独渲染操作做得更多。类似地,物理引擎将更新整个世界,计算碰撞量并考虑子滴答运动。
加载精灵通常是加载整个地图的资源,然后在地图的资源库中按名称寻址该精灵。
碰撞通常由单独的物理引擎处理,见上文。
答案 5 :(得分:1)
在此之前为这种模式堕落是我的意见:
对于游戏来说,性能将是最大的缺点。您可能希望将所有绘图上下文,对象等保存在一个位置,并优化一个更大的代码块。
如果由于某种原因你想在不同的平台上渲染,那么这种方法由于缺乏分离而失败,再次插件渲染器是一种更好的方法