クラス間のデータと依存関係をきれいにエレガントに処理する方法


12

私はSFML 2の2Dトップダウンゲームに取り組んでおり、すべてがうまく機能し、調和するエレガントな方法を見つける必要があります。

説明させてください。drawメソッドとupdateメソッドをすべてのクラスに提供する抽象ベースから継承するクラスがいくつかあります。

ゲームループでは、updateを呼び出してから各クラスを描画します。これは非常に一般的なアプローチだと思います。タイル、コリジョン、プレーヤー、およびすべてのタイル/画像/テクスチャを含むリソースマネージャーのクラスがあります。SFMLでの入力の動作方法のため、各クラスで(必要に応じて)更新呼び出しで入力を処理することにしました。

これまで、必要に応じて依存関係を渡してきました。たとえば、移動キーが押されたときのプレーヤークラスで、衝突クラスのメソッドを呼び出して、プレーヤーが移動したい位置が衝突かどうかを確認します。衝突がない場合にのみプレーヤーを移動します。

これはほとんどの部分でうまく機能しますが、もっと良くできると信じています。どうすればいいのかわかりません。

実装する必要のあるより複雑なものがあります。例えば、プレーヤーは地面のオブジェクトに近づき、キーを押してそれを拾い上げたり、それをインベントリに表示することができます。これは、いくつかのことを行う必要があることを意味します。

  • プレイヤーがキーを押すとルート可能なアイテムの範囲内にあるかどうかを確認し、そうでない場合は先に進まないでください。
  • アイテムを見つけます。
  • アイテムのスプライトテクスチャをデフォルトのテクスチャから「looted」テクスチャに更新します。
  • アイテムの衝突を更新します。形状が変更されたか、完全に削除された可能性があります。
  • 追加されたアイテムでインベントリを更新する必要があります。

すべてを通信させるにはどうすればよいですか?現在のシステムでは、クラスがスコープ外になり、メソッド呼び出しが至る所で行われます。1つの大きなマネージャーですべてのクラスを結び付けて、それぞれに親マネージャークラスへの参照を与えることができますが、これはほんの少しだけ良いようです。

どんなヘルプ/アドバイスも大歓迎です!不明な点がある場合は、詳しく説明させていただきます。


1
ここでは、継承ではなく構成を検討することをお勧めします。作曲例を見回すと、いくつかのアイデアが得られるかもしれません。子のイディオムは、物事を整理するのにも役立つかもしれません。
OriginalDaemon

5
作曲に関するキヤノンの記事の1つ:階層の進化
-doppelgreener

ローカライズされているようです。Code Review SEをお試しください?
アンコ

回答:


5

構成がすべての問題を解決するかどうかはわかりません。多分部分的に助けることができます。しかし、クラスを分離したい場合は、イベント駆動型のロジックを検討します。このように、例えば、最も近いプレイヤーを見つけるためにプレイヤーの位置とルートに関する情報が必要なOnLoot機能があります。次に、関数は、ルートされたアイテムにイベントを送信します。イベント処理サイクルで略奪されたアイテムはこのイベントを処理するため、アイテムは自分自身を更新する方法を知るだけで済みます。OnLoot関数は、プレーヤーのインベントリを更新することも、アイテム自体がupdateInventory / * OnLootSucess *イベントを送信することもあり、プレーヤー/インベントリは独自のプロセスイベントサイクルでそれを処理します。

長所:クラスの一部を分離しました

短所:追加されたイベントクラス、不要なコードオーバーヘッド。

ここは 1、それが見えるかもしれ方法の可能な方法の:

case LOOT_KEY:
   OnLoot(PLayer->getPos(), &inventoryItems);
....

// note onLoot do not needs to know anything about InvItem class (forward decl in enough)
int onLoot(vec3 pos, InvItems& pitems)
{
    InvItem* pitem = findInRange(pos, pitems, LOOT_RANGE);
    if(pitem)
     EventManager::Instance->post( Event::makeLootEvent(pitem));
}
....

// knows only about EventManager
InvItem::processEvents()
{
    while(!events.empty())
    {
        Event* pev = events.pop();
        ...
        case LOOT_EVENT:
            // in case you broadcasted it, but better is to sort all posted/sent events and add them only if they addressed to particular item 
            if(pev->item == this && handleLoot((LootEvent)pev))
            {
                EventManager::Instance->post(Event::makeLootSuccessEvent(this));
            }
    }
}

int handleLoot(LootEvent* plootev)
{
    InvItem* pi = plootev->item;
    if(pi->canLoot())
    {
        updateTexture(pi->icon, LOOTED_ICON_RES);
        return true;
    }
    return false; 
}


...
// knows only LootSuccessEvent and player
Inventory::processEvents()
{
    while(!events.empty())
    {
        Event* pev = events.pop();
        ...
        case LOOT_SUCCESS_EVENT:
             player->GetInventory()->add( ((LootSuccessEvent*)pev)->item );
        ...
}

これは可能な方法の1つにすぎません。おそらく、それほど多くのイベントは必要ありません。そして、あなたはあなたのデータをより良く知ることができると確信しています、これは多くの方法の1つにすぎません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.