5

私はゲームに取り組んでおり (独自の物理エンジンを作成しています)、優れた設計に従ってゲームを作成しようとしています。最近、見つけにくいバグに遭遇することが多く、これらのバグを見つけやすくするために、単体テストを作成しようとしています。私のコンポーネントの多くが、特にgameモジュールに密結合されているため、これを行うのは困難でした。

私のgameモジュールでは、現在のゲームの状態、時間、ゲーム イベントなどを保持するシングルトン クラスのインスタンスをエクスポートします。しかし、これを読んだ後、この結合を減らす方法を調査した結果、次のようにクラスを書き直すことにしました。それはもはやシングルトンではありません。

アイデアは、GameStateクラスを使用し、これらのオブジェクトをあらゆる場所に渡すことです。これにより、単体テストがテストの最小限の状態を作成できるようになります。ほとんどの関数はゲーム状態の関数になり、新しいゲーム状態を返します。ただし、いくつかの設計上の問題に遭遇しました

私のエンティティ オブジェクトの位置と速度は、現在の時間に従って計算される Python プロパティです。これは、これを関数として書き換えずに単純にオブジェクトを渡すことができないことを意味しGameStateます (厄介な構文になります)。コード例:

class Entity:
    @property
    def position(self):
        """Interpolate the position from the last known position from that time."""
        # Here, the game class instance is referenced directly, creating coupling.
        dt = game.game_time - self._last_valid_time
        # x_f = x_i + v_i*t + 1/2 at^2
        return self._position + self._velocity * dt + .5 * self._acceleration * dt**2

これを解決する方法についていくつかの調査を行ったところ、 Dependency Injectionに遭遇しました。基本的に、GameState の「getter」オブジェクトをすべてのエンティティの初期化子に渡すことができました。すべての GameState 'getter' オブジェクトは、単に現在の状態を返します。GameStateGetterクラスの例:

class GameStateGetter:
    _CurrentState = None

    def update(self, new_state):
        GameStateGetter._CurrentState = new_state

    def __getattr__(self, name):
        # GameState object properties are immutable, so this can't cause issues
        return getattr(GameStateGetter._CurrentState, name)

さて、私の質問です。

  • GameState の「getter」オブジェクトを使用することは良い考えでしょうか?

1 つの問題は、現在のゲームの状態を更新するためのインターフェイスです (updateメソッドを定義しましたが、これは奇妙なインターフェイスのようです)。また、グローバル変数の問題は予測不可能なプログラムの状態であることを考えると、これは実際にはそれを防ぐことにはなりません。

  • クラスのプロパティにgame依存関係を「注入」する別の方法はありますか?Entityposition

理想的には、物事をシンプルに保ちたいと思っています。クラスは非常に抽象的に聞こえます(GameStateGetter実装が単純であっても)。私のプロパティが暗黙的に現在の状態を渡すことができればいいのですが。

ご協力いただきありがとうございます。

4

2 に答える 2

1

GameStateProviderコンストラクターへのa の注入Entityは、開始するのに非常に簡単な場所であり、ロジックがより複雑になるにつれてリファクタリングできるものを残します。

Entityただし、状態が変化したときに、それぞれを新しい状態で更新する必要はありません。プロバイダがオブジェクトの場合、デフォルトで変更可能です。(とにかく、常に変化するもの、つまりゲームの状態を不変にする必要があるのはなぜですか?)current_timeプロバイダーの属性を変更すると、何かが取得positionされるたびに正しい値が含まれます。

パターンは次のようになります。

class Entity(object):

   def __init__(self, game_state_provider):
      self.provider = game_state_provider

   @property
   def position(self):
      # lazily evaluate position as a function of the current time
      if self._last_valid_time == self.provider.current_time:
         return self._position
      self._last_valid_time = self.provider.current_time
      self._position = // insert physics here
      return self._position
于 2012-05-11T20:57:28.347 に答える
1

最初の質問についてはよくわかりませんが、少し大げさなように聞こえます。

2 番目の質問について: 単体テストを行う場合、最初にゲームモジュールをインポートしてから、グローバル ゲーム ステートにモンキー パッチを適用して、モック ゲーム ステートにすることができます。今後は、すべての関数/プロパティがモック ゲーム ステートを使用します。選択した単体テスト フレームワークの setUp メソッドで行います

編集:

ゲームモジュールでモジュールレベルの属性「GameState」を作成してみませんか。どこからでもアクセスできました。GameState のコピーが必要な場合は、clone (または __deepcopy__) メソッドを定義します。

于 2012-05-11T16:51:12.867 に答える