ゲームの俳優は自分自身を描く責任がありますか?


41

私はゲーム開発は初めてですが、プログラミングは初めてです。

私は(再び)JavaScriptのcanvas要素を使用してPongタイプのゲームで遊んでいます。

Paddle次のプロパティを持つオブジェクトを作成しました...

  • width
  • height
  • x
  • y
  • colour

次のPongようなプロパティを持つオブジェクトもあります...

  • width
  • height
  • backgroundColour
  • draw()

draw()現在、メソッドはをリセットしてcanvasおり、そこで問題が発生しました。

必要があるPaddleオブジェクトが持つdraw()その描画方法は責任、または必要があるdraw()Pongオブジェクトがその役者を描くための責任がある(私が間違ってる場合、私はそれが正しい用語であると仮定し、私を修正してください)。

私はのためにそれがadvantagousだろうと考え出しPaddle自体を描画するために、私は2つのオブジェクトをインスタンス化するように、PlayerEnemy。それはでなかった場合Pongdraw()、私は二度同様のコードを記述する必要があると思います。

ここでのベストプラクティスは何ですか?

ありがとう。


回答:


49

俳優に自分自身を描いてもらうことは、次の2つの主な理由から、良い設計ではありません。

1)単一の責任原則に違反します。これらのアクターは、レンダリングコードを押し込む前に別の仕事をする必要があったためです。

2)拡張が困難になる。すべてのアクタタイプが独自の描画を実装し、描画方法を一般的に変更する必要がある場合、多くのコードを変更する必要があります。継承の過剰使用を回避すると、これをある程度緩和できますが、完全には緩和できません。

レンダラーが描画を処理する方が適切です。結局のところ、それがレンダラーであることの意味です。レンダラーのdrawメソッドは、物をレンダリングするために必要なすべてを含む「レンダリング記述」オブジェクトを取る必要があります。(おそらく共有されている)ジオメトリデータ、インスタンス固有の変換、または色などのマテリアルプロパティへの参照。次に、それを描画し、そのレンダリング記述が「あるべき」ものを気にしません。

アクターは、自分で作成したレンダー記述を保持できます。通常、アクターはロジック処理タイプであるため、必要に応じて状態の変更をレンダー記述にプッシュできます。たとえば、アクターがダメージを受けた場合、レンダー記述の色を赤に設定してこれを示すことができます。

次に、表示されているすべてのアクタを単純に反復処理し、レンダーの説明をレンダラーに入力して、その処理を実行させます(基本的には、これをさらに一般化できます)。


14
+1た単一責任の原則のための「描画コードをレンダリング、」しかし、-1のためのプログラマーに良いよりも害を行って。正しい考え(懸念の分離)がありますが、それが述べられている方法(「すべてのクラスに1つの責任のみ、および/または1つの変更理由のみ」)間違っています。
BlueRaja-ダニーPflughoeft

2
-1 for 1)、BlueRajaが指摘したように、この原則は理想主義者ですが、実用的ではありません。2)の場合は-1になります。これにより、拡張が大幅に難しくなることはありません。レンダリングエンジン全体を変更する必要がある場合、変更するコードはアクターコードにあるものよりもはるかに多くなります。私は多くの希望actor.draw(renderer,x,y)よりもrenderer.draw(actor.getAttribs(),x,y)
corsiKa

1
いい答えだ!さて、「あなたは単一の責任原則に違反してはならない」ということは私の十戒の中にありませんが、それでも考慮すべき良い原則
です-FxIII

@glowcoderそれはポイントではありません。{renderer.draw(mesh、attribs、transform);として定義されているactor.draw()を維持できます。}。OpenGLでは、struct RenderDetail { glVAO* vao; int shader; int texture; Matrix4f* transform; }レンダラーの多かれ少なかれを維持します。このように、レンダラーはデータが何を表すかを気にすることなく、単に処理します。この情報に基づいて、描画呼び出しの順序を並べ替えて全体的なGPU呼び出しを削減するかどうかも決定できます。
減速

16

ここでは、訪問者パターンが役立ちます。

できることは、各オブジェクトの描画方法を知っているRendererインターフェイスと、呼び出す(特定の)レンダラーメソッドを決定する各アクター内の「自分で描画」メソッドなどです。

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ます。

別の利点があります:レンダラーは、任意のアクターコードとは別にテストできます


2

この例では、Pongオブジェクトにdraw()を実装させ、自分自身をレンダリングしたいと思います。

このサイズのプロジェクトでは大きなメリットはありませんが、一般的にゲームロジックとその視覚的表現(レンダリング)を分離することは価値のあるアクティビティです。

これにより、update()できるゲームオブジェクトが得られますが、レンダリングの方法がわからず、シミュレーションのみに関心があります。

次に、Pongオブジェクトへの参照を持つPongRenderer()クラスを作成し、Pong()クラスのレンダリングを担当します。これには、Paddlesのレンダリング、またはPaddleRendererクラスによる処理が含まれます。

この場合の懸念の分離はかなり自然なものであり、クラスの肥大化を抑えることができ、レンダリング方法を簡単に変更でき、シミュレーションのような階層に従う必要がなくなります。


-1

Pong's draw()でそれを行う場合、コードを複製する必要はありません- Paddle各パドルに対して' s 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
これは「描画の実行方法」ではなく、単一の実行方法です。前の回答で述べたように、この方法にはいくつかの欠点といくつかの利点がありますが、他のパターンには大きな利点と柔軟性があります。
トロイセフ

感謝していますが、シーンで俳優を描くさまざまな方法の簡単な例が与えられたので、私はこの方法に言及しました。
user43609
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.