6

もう 1 つ質問があります。できれば私の考えを要約してください。

次の 3 つのクラスがあるとします。

クラスプレーヤー:

class Player {
private:
    int positionX, positionY;
public:
    void move(Board& b) {
      // player changes its position on the board(move)
      b.removeCharFromBoard(positionX, positionY);
      positionX++;
      positionY++;
      // 'P' indicates a Player in the Board....
      b.insertCharToBoard(positionX, positionY, 'P');
    }
};

クラス委員会:

class Board {
private:
    // BOARD_C and BOARD_R are both "#define ..." for some integer number.
    char board[BOARD_C][BOARD_R];
};

クラスゲームエンジン:

class GameEngine {
private:
     Board* board;
public:
     void playTurn(const Player& p) {
        p.move(board);
     }
};

GameBoard の playTurn 関数が Player の move 関数をパラメータ「board」で呼び出すのは理にかなっていると思いますか? プレーヤーが自分の位置を変更したことをボード データ メンバーにマークするために、これを行う必要があります。OOPの基本的なルールを守っていますか?

みなさんありがとう、シンジケーター!

4

4 に答える 4

4

はい、この場合は妥当と思われます (「この場合」とは、「あなたGameEngineBoardクラスのセマンティクスとそれらの関連付け/集約関係の性質について推測できることを考慮して」という意味です):

  1. Boardにオブジェクトを保持するには、生のポインターではなくスマート ポインターを使用しますGameEngineunique_ptr他のすべてのエイリアスは単なるオブザーバーのように見え、ボード オブジェクトの有効期間はオブジェクトの有効期間にバインドされているため、この場合はおそらく必要なものですGameEngine。ただし、所有権の共有が必要な場合は、 を選択してshared_ptrください。生のポインター 、new、およびを使用しないようにしてくださいdelete。バグのあるコードにつながるためです。
  2. Boardボードを変更するには、クラスのインターフェイスにパブリック関数を提供する必要があります。これPlayerは、プライベート メンバー変数にアクセスできないためです (boardたまたま 1 つです)。
  3. #defines ではなくconstexpr、ボードのサイズの値を使用します (C++11 を使用している場合)。安全な二次元 C スタイル配列を作成するためにBoost.MultiArrayを検討することもできます。
于 2013-01-28T21:50:37.140 に答える
3

あなたのアプローチは問題ありません。GameEngine は、ゲームのある種のコントローラーとして使用されます。そのおかげで、たとえば、プレーヤーの動きをフィルタリングしたり、この種の動きが可能かどうかを確認したり、特定のプレーヤー操作の場合に他の種類のものを実行したりできます。

第二に、このソリューションのおかげで、プレーヤーを特定のボードに接続する必要がなくなり、ボード間でプレーヤーを簡単に転送するなど、他のオプションの可能性が広がります。順調に進んでいると思います:)

于 2013-01-28T21:50:27.370 に答える
1

すでにいろいろ言われていますが、一言付け加えさせてください。

Board(それであろうとなかろうとprivate) を に渡すこと自体Playerは悪くなく、そのような設計はいくつかのアーキテクチャで使用されています (SFML 2.0 から取られたコード):

void Sprite::draw(RenderTarget& target, RenderStates states) const
{
    if (m_texture)
    {
        states.transform *= getTransform();
        states.texture = m_texture;
        target.draw(m_vertices, 4, Quads, states);
    }
}

RenderTargetあなたのBoardです。ここで理解しておくべきことは、(アクセスできる)Board パブリック インターフェイスを使用してのみ操作することです。上記のコードでは、強制的に何かを描画するためにdraw()使用されるメソッドです。target

内部オブジェクトをより高いレベルのクラス ( など) に渡すというこの全体的な考え方は、ブリッジ OO パターンPlayerとして解釈できます。この場合、インターフェイスの実装が複数あり、複数のクラスが実装できます(またはこのようなもの)。BoardIBoardManipulator

そうは言っても、ゲームエンジンの一般的な考え方に従う方がはるかに良いと思います。

  1. ゲームのエンティティを登録します (この場合はプレイヤー)
  2. プレーヤーの入力をキャプチャする
  3. プレイヤーの入力を消化して反応する (プレイヤーの移動を要求する)
  4. ゲーム ロジックの処理 (プレイヤーが特定の場所に移動できるかどうかを確認し、移動できる場合は移動します)
  5. 登録されたエンティティごとに、エンティティが使用できるターゲットとしてpassGameEngineを呼び出します。draw()Board
  6. 手順 2 から繰り返します

単純なアーキテクチャにこれが必要だと言っているわけではありませんが、長い目で見れば、 のPlayerようなクラスのそれぞれがBoard.

于 2013-01-29T08:08:44.090 に答える
1

アプリケーションがどのように変化し、どのような機能を導入したいかを考える必要があります。このコードからは問題ないように見えますが、新しい機能を導入するときにそのように見えるでしょうか?

他の解決策は、プレーヤーのみの移動ロジックを配置することです。これにより、その位置が更新され、GameEngine がすべてのプレーヤーの現在の位置に基づいてボード エントリを更新します。しばらくすると、衝突検出を実装する必要があり、各プレーヤーが位置または動きを更新した後、衝突検出が行われ、それらの動きが修正され、後でボード上で正しく更新されることを想像してください。

于 2013-01-28T21:50:36.790 に答える