ハースストーンは関連性のあることをしているようで、正直なところ、データ指向設計のECSエンジンを使用することで、柔軟性を実現する最良の方法だと思います。ハースストーンのクローンを作ろうとしていたが、そうでなければ不可能だった。すべてのエッジケース。あなたがこれらの奇妙なエッジケースの多くに直面している場合、それはおそらくそれについて行くための最良の方法です。このテクニックを試した最近の経験から私はかなり偏見があります。
編集:ECSは、必要な柔軟性と最適化の種類によっては必要ない場合もあります。これを実現する方法の1つにすぎません。DOD私は手続き型プログラミングと誤解していましたが、それらは多く関連しています。私が言いたいのは OOPを完全にまたは少なくともほとんど廃止することを検討し、代わりにデータとその編成方法に注意を集中する必要があること。継承とメソッドを避けてください。代わりに、公開データ(システム)に焦点を合わせて、カードデータを操作します。各アクションは、テンプレート化されたものやあらゆる種類のロジックではなく、代わりに生データです。システムがそれを使用してロジックを実行する場所。整数の切り替えの場合、または整数を使用して関数ポインターの配列にアクセスすると、入力データから目的のロジックを効率的に把握できます。
従うべき基本的なルールは、ロジックをデータと直接結び付けないようにすること、データが相互に依存することを可能な限り避けること(例外が適用される場合があります)、および手が届かないと感じる柔軟なロジックが必要な場合です...データに変換することを検討してください。
これを行うことには利点があります。各カードには、アクションを表す列挙値または文字列が含まれている場合があります。このインターンを使用すると、テキストファイルまたはjsonファイルを使用してカードを設計し、プログラムでカードを自動的にインポートできます。プレイヤーのアクションをデータのリストにすると、特にハースストーンのような過去のロジックにカードが依存している場合や、ゲームを保存したい場合やゲームのリプレイを任意の時点で保存したい場合に、これにより柔軟性がさらに高まります。AIをより簡単に作成できる可能性があります。特に、「ビヘイビアツリー」ではなく「ユーティリティシステム」を使用する場合。ポリモーフィックな可能性のあるオブジェクト全体をネットワーク経由で転送する方法や、事後のシリアル化の設定方法を把握する必要がないため、ネットワーキングも簡単になります。すでにゲームオブジェクトを持っているのは、単純なデータにすぎず、最終的には簡単に移動できます。最後になりますが、間違いなく、これにより、コードを心配する時間を無駄にする代わりに、プロセッサがデータをより簡単に処理できるように、データをより適切に整理できるため、最適化が容易になります。Pythonには問題があるかもしれませんが、「キャッシュライン」とゲーム開発との関係を調べてください。おそらくプロトタイプの作成には重要ではありませんが、将来的には大いに役立つでしょう。
いくつかの便利なリンク。
注:ECSでは、実行時に変数(コンポーネントと呼ばれる)を動的に追加/削除できます。ECSがどのように見えるかを示すCプログラムの例(それを行う方法がたくさんあります)。
unsigned int textureID = ECSRegisterComponent("texture", sizeof(struct Texture));
unsigned int positionID = ECSRegisterComponent("position", sizeof(struct Point2DI));
for (unsigned int i = 0; i < 10; i++) {
void *newEnt = ECSGetNewEntity();
struct Point2DI pos = { 0 + i * 64, 0 };
struct Texture tex;
getTexture("test.png", &tex);
ECSAddComponentToEntity(newEnt, &pos, positionID);
ECSAddComponentToEntity(newEnt, &tex, textureID);
}
void *ent = ECSGetParentEntity(textureID, 3);
ECSDestroyEntity(ent);
テクスチャと位置データを含むエンティティの束を作成し、最後にテクスチャコンポーネント配列の3番目のインデックスにあるテクスチャコンポーネントを持つエンティティを破棄します。風変わりですが、物事を行う1つの方法です。テクスチャコンポーネントを持つすべてをレンダリングする方法の例を次に示します。
unsigned int textureCount;
unsigned int positionID = ECSGetComponentTypeFromName("position");
unsigned int textureID = ECSGetComponentTypeFromName("texture");
struct Texture *textures = ECSGetAllComponentsOfType(textureID, &textureCount);
for (unsigned int i = 0; i < textureCount; i++) {
void *parentEntity = ECSGetParentEntity(textureID, i);
struct Point2DI *drawPos = ECSGetComponentFromEntity(positionID, parentEntity);
if (drawPos) {
struct Texture *t = &textures[i];
drawTexture(t, drawPos->x, drawPos->y);
}
}