2

作成中のゲームの新しいパワーアップ システムを設計しています。サイド スクローラーです。パワーアップは円形のオブジェクトとして表示され、プレイヤーはそれらに触れたり移動したりしてパワーを取得する必要があります。その後、パワーアップが有効になり、数秒後に無効になります。各パワーアップには、独自の期間が定義されています。簡単にするために、パワーアップは X 秒ごとに生成されます (画面に配置されます)。

私は PowerUpManager を作成しました。これは、新しいパワーアップをいつ作成し、どこに配置するかを決定する仕事をするシングルトンです。

次に、Powerup 基本クラスと、新しい Powerup ごとにその基本クラスを継承するクラスを作成しました。すべてのパワーアップは、3 つの状態のいずれかになります: 無効、画面に配置、プレイヤーが拾う。プレーヤーがパワーアップを拾わずに移動した場合、パワーアップは画面を終了し、配置された状態から無効な状態に戻る必要があるため、再度配置できます。

(私が) 設定した要件の 1 つは、新しい Power up クラスをコーディングするときにコードの変更を最小限に抑えることです。私ができる最善のことは、1 つのコードでした: PowerUpManager のコンストラクター。ここで、すべてのパワーアップを保持するコンテナーに新しいパワーアップを追加する必要があります。

PowerupManager::PowerupManager()
{
    available = {
        new PowerupSpeed(),
        new PowerupAltWeapon(),
        ...
    };
}

PowerUpManager の詳細 (質問は近日公開予定です!): 使用可能と呼ばれる PowerUp (基本クラス) へのポインターのベクトルを保持します。これは、ゲーム内の各パワーアップのコピーを 1 つ保持する最初のコンテナーです。さまざまな状態を処理するために、いくつかのリストがあります: 現在配置されているパワーアップへのポインターを保持するリストと、現在アクティブなパワーアップへのポインターを保持する別のリストです。また、新しいパワーアップを配置するかどうか、どこに配置するか、およびピックアップされなかったパワーアップをクリーンアップするかどうかを決定するすべてのゲーム ティックが呼び出されるメソッドもあります。最後に、プレイヤーがパワーアップに遭遇したときに呼び出され、パワーアップをアクティブにするメソッドがあります (配置リストからアクティブ リストに移動し、パワーアップの activate メソッドを呼び出します)。

最後に、全体像を理解したら、質問: クライアント コードが特定のパワーアップが現在アクティブかどうかを確認する方法が必要でした。例: プレイヤーは武器を持っていますが、その武器を一時的に置き換えるパワーアップがあります。入力をポーリングし、プレーヤーが武器を発射したいことを認識した場合、正しい発射メソッドを呼び出す必要があります。通常の武器発射メソッドではなく、代替武器のパワーアップ発射メソッドです。

この特定の需要についてしばらく考えた結果、次のように思いつきました。

template <typename T>
T* isActivated() // Returns a pointer to the derived Powerup if it exists in the activated list, or nullptr if it doesn't
{
    for(Powerup *i : active) // Active is a list of currently active power ups
    {
        T *result = dynamic_cast<T*>(i);

        if(result)
            return result;
    }

    return nullptr;
}

したがって、クライアント コードは次のようになります。

PowerUpAltWeapon *weapon = powerUpManager->isActivated<PowerUpAltWeapon>();
if(weapon)
    ...

解決策はエレガントですっきりしていると思いましたが、基本的には基本型を派生型に変換しようとしています。それが機能しない場合は、次の派生型を試します... if / else if の長いチェーンは、ループで偽装されているだけです。これは、先ほど説明したガイドラインに違反していますか? ヒットするまで、if / else if の長いチェーンで基本型をすべての派生型にキャストしませんか? 別の解決策はありますか?

2 つ目の質問は、PowerupManager コンストラクターでさまざまなパワーアップをすべて構築する必要をなくす方法はありますか? 新しいパワーアップを導入したい場合、現在変更が必要な場所はここだけです。そこを解消できたら面白いのに…

4

1 に答える 1

1

これはあなたの設計に基づいていますが、私なら各 PowerUp の ID とクライアントの ID セットを選択し、ユーザーが PowerUp を所有するたびにその ID がそのセットに追加されます...残り。この手法を使用すると、すべての PowerUp をすばやく検索して次のことを回避できdynamic_castます。

std::set<PowerUp::ID> my_powerUps;
template< class T > bool isActivated() {
    return my_powerUps.find( T::id() ) != my_powerUps.end();
}

そして、2番目の質問について、代わりにいくつかのプラグインをロードする同様のプログラムがあります。そのプラグインにPowerUp必要なすべてのメソッドを含む純粋な仮想基本クラスがあり、それを共有モジュールに実装し、起動時に特定のフォルダ。たとえば、各共有モジュールには acreate_objectを返す aが含まれておりplugin*(もちろん、あなたの場合PowerUp*)、フォルダーを繰り返し、モジュールをロードcreate_objectし、それらからプラグインを作成して自分に登録します。plugin_manager

于 2012-10-14T10:44:45.660 に答える