游戏中的演员应该负责吸引自己吗?


41

我对游戏开发非常陌生,但对编程却不是。

我(再次)使用JavaScript的canvas元素在玩Pong类型的游戏。

我创建了一个Paddle具有以下属性的对象...

  • width
  • height
  • x
  • y
  • colour

我也有一个Pong具有诸如...的属性的对象

  • width
  • height
  • backgroundColour
  • draw()

draw()方法当前正在重置canvas,这就是出现问题的地方。

如果Paddle对象具有draw()负责其绘制的方法,或者draw()Pong对象的负责负责其演员的绘制(我认为这是正确的术语,如果我不正确,请更正我)。

我发现在Paddle实例化两个对象Player和时,绘制自身会很有利Enemy。如果在没有Pongdraw(),我需要两次写类似的代码。

最佳做法是什么?

谢谢。


Answers:


49

让演员自己抽奖并不是一个好的设计,主要有两个原因:

1)它违反了单一责任原则,因为在您将渲染代码推入其中之前,那些参与者可能还有其他工作要做。

2)扩展困难;如果每种角色类型都实现自己的绘图,并且您通常需要更改绘图方式,则可能必须修改很多代码。避免过度使用继承可以在某种程度上(但不能完全)缓解这种情况。

最好让渲染器处理图形。毕竟,这就是成为渲染器的意义。渲染器的draw方法应采用“渲染描述”对象,该对象包含渲染事物所需的一切。引用(可能是共享的)几何数据,特定于实例的变换或材料属性(例如颜色等)。然后,它得出了结论,而不管渲染描述应该是什么。

然后,您的演员可以保留自己创建的渲染描述。由于actor通常是逻辑处理类型,因此它们可以根据需要将状态更改推送到渲染描述中,例如,当actor受到损坏时,可以将其渲染描述的颜色设置为红色以表明这一点。

然后,您可以简单地迭代每个可见的actor,将它们的渲染描述加入到renderer中,然后让它完成其工作(基本上,您可以进一步概括)。


14
+1代表“渲染器绘图代码”,但-1代表单一责任原则,这对程序员造成的危害大于好处。它有一个正确的想法(关注点分离),但是它的表述方式(“每个班级应该只有一个责任,和/或只有一个改变的理由”)错误的
BlueRaja-Danny Pflughoeft

2
-1对于1),正如BlueRaja指出的那样,该原理是理想主义者,但不切实际。-1表示2),因为它不会使扩展困难得多。如果您必须更改整个渲染引擎,那么要更改的代码将比角色代码中要包含的代码少得多。我actor.draw(renderer,x,y)renderer.draw(actor.getAttribs(),x,y)
corsiKa 2011年

1
好答案!好吧,“您不得违反单一责任原则”不在我的讨论之内,但仍然是要考虑的好原则
FxIII 2011年

@glowcoder没关系,您可以维护actor.draw(),它定义为{renderer.draw(mesh,attribs,transform); }。在OpenGL中,我struct RenderDetail { glVAO* vao; int shader; int texture; Matrix4f* transform; }为渲染器维护了更多或更少的内容。这样,渲染器就不在乎数据表示什么,而只是处理它。基于此信息,我还可以决定是否对绘制调用的顺序进行排序,以减少总体GPU调用。
deceleratedcaviar

16

访问者模式可以在这里很有用。

您可以做的是拥有一个Renderer接口,该接口知道如何绘制每个对象,并在每个actor中具有一个“绘制自己”方法,以确定要调用哪个(特定)渲染器方法,例如

interface Renderer {
    void drawPaddle(Player owner, Rectangle position);
    // Note: the Renderer chooses the Color based on which player the paddle belongs to

    // Also drawBackground, drawBall etc.
}

interface Actor {
    void draw(Renderer renderer);
}

class Paddle implements Actor {
    void draw(Renderer renderer) {
        renderer.drawPaddle(this.owner, this.getBounds());
    }
}

这样,仍然很容易移植到另一个图形库或框架(我曾经将游戏从Swing / Java2D移植到LWJGL,并且很高兴我使用了这种模式而不是传递Graphics2D周围的东西。)

还有一个优点:渲染器可以与任何actor代码分开进行测试


2

在您的示例中,您想让Pong对象实现draw()并进行渲染。

尽管您不会注意到此规模的项目有任何重大收获,但通常将游戏逻辑与其视觉表示(渲染)分开是值得的。

我的意思是,您将拥有可以被update()调用的游戏对象,但是他们不知道它们的呈现方式,它们只关心模拟。

然后,您将拥有一个PongRenderer()类,该类具有对Pong对象的引用,然后它负责呈现Pong()类,这可能涉及呈现Paddles或由PaddleRenderer类来处理它。

在这种情况下,关注点的分离是很自然的事情,这意味着您的类可以减少肿,并且更容易修改事物的呈现方式,不再需要遵循模拟所做的层次结构。


-1

如果您要在Pong'draw()中执行此操作,则无需复制代码-只需Paddle为每个板块调用'draw()函数即可。因此,在您Pong的draw()中,

humanPaddle.draw();
compPaddle.draw();

-1

每个对象都应包含自己的绘制函数,游戏更新代码或绘制代码应调用该函数。喜欢

class X
{
  public void Draw( )
  {
     // Do something 
  } 
}

class Y
{
  public void Draw( )
   { // Do Something 
   } 
}

class Game
{
  X objX;
  Y objY;

  @Override
  public void OnDraw( )
  {
      objX.Draw( );
      objY.Draw( ); 
  } 
}

这不是代码,而只是用于显示如何完成绘制对象。


2
这不是“应该如何绘制”,而是一种简单的方法。如先前的答案中所述,此方法具有一些缺陷和很少的益处,而其他模式则具有明显的益处和灵活性。
Troyseph 2015年

我很欣赏,但是由于要给出一个简单的示例来说明场景中吸引演员的各种方式,因此我提到了这种方式。
user43609 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.