GameManager神オブジェクトを回避する方法は?


51

ゲームコードの構造化に関する質問の答えを読んだところです。ユビキタスGameManagerクラスについて、そしてそれが本番環境でどのように頻繁に問題になるのかと思いました。これについて説明しましょう。

まず、プロトタイピングがあります。素晴らしいコードを書くことを誰も気にせず、ゲームプレイが積み重なるかどうかを確認するために何かを実行しようとします。

その後、グリーンライトがあり、物事をきれいにするために、誰かがを書きますGameManager。おそらく、たくさんのを保持するために、おそらくGameStatesいくつかを保存するためGameObjectsに、実際には大きなものはありません。かわいい、小さな、マネージャー。

プリプロダクションの穏やかな領域では、ゲームはうまく形成されています。コーダーには、適切な睡眠の時間と、素晴らしいデザインパターンを使用して物事を構築するための多くのアイデアがあります。

その後、生産が開始され、もちろん、すぐにクランチタイムがあります。バランスの取れた食事はなくなり、バグトラッカーは問題を抱えて割れ、人々はストレスを感じ、昨日ゲームをリリースする必要があります。

その時点で、通常、GameManagerある本物のビッグ(滞在丁寧に)混乱。

その理由は簡単です。結局のところ、ゲームを作成するときは、すべてのソースコードが実際にゲーム管理するためにここにあります。この小さな追加機能またはバグ修正をに追加するだけで、他のすべてがとにかく保存されます。時間が問題になると、別のクラスを記述したり、この巨大なマネージャーをサブマネージャーに分割する方法はありません。GameManager

もちろん、これは古典的なアンチパターン、つまり神オブジェクトです。それは悪いことであり、合併する痛み、維持する痛み、理解する痛み、変革する痛みです。

これを防ぐために何を提案しますか?

編集

名前のせいにするのは魅力的だと思います。もちろん、GameManagerクラスを作成することはそれほど素晴らしい考えではありません。しかし、同じことがクリーンに起こることができるGameApplicationか、GameStateMachineまたはGameSystem、ダクトテープの好きなプロジェクトの終わりまでになってしまうこと。命名に関係なく、ゲームコードのどこかにそのクラスがあります。これは現時点では単なる胚に過ぎません。どのモンスターになるかはまだわかりません。したがって、「ネーミングのせいにする」という答えは期待していません。これが発生しないようにする方法、コーディングアーキテクチャ、および/またはチームが本番のいずれかの時点で発生することを知って従うことができるプロセスが必要です。1か月間のバグ修正と土壇場での機能が、それらがすべて1つの巨大な保守不能なマネージャーになったという理由だけで、言うのを捨てるのはあまりにも悪いことです。


GameManagerまたはControllerOfTheGameと呼ばれるかどうか、またはあなたがそれを何と呼んでも、ゲーム全体のロジックの制御を担当するクラスを避けることを意味します。あなたは、あなたがそれを呼ぶことを計画していることに関係なく、あなたがそれをするとき、あなたはそれをしていることを知っています。私の最後のゲームには、保存するためにレベルの状態を保持するためだけに開始したレベルコントローラーがあったと思いますが、停止して考えた場合、このオブジェクトが必要以上に詳細を処理するためのものであることは明らかでした。
ブランドン

@brandon私に質問を言い換えさせてください。GameManager上記で説明した- 効果にさらされることなく、ゲーム全体のロジックをどのように制御しますか。
ローランCouvidou

ロジックに依存します。プレーヤー、敵、構造など、明らかに独自のロジックを持つ明白なものは、それぞれのクラスに入れられます。一番質問しているのは、ゲームの状態を処理することです。通常、ゲーム全体の状態を追跡するクラスがありますが、私の経験では、プロトタイピング時に心配する必要がある最後の1つであり、その目的は非常に明確である必要があります。基本的には、プロトタイピングの前に初期計画を行うか、プロダクションを開始するときにゼロから開始して、テスト専用のジャンククラスを使用しないようにします。
ブランドン

興味深いことに、XNAはこの潜在的な災害をあなたにもたらします。デフォルトでは、すべてを管理するためのGameクラスが作成されます。メインの更新、描画、初期化、および読み込みメソッドが含まれています。私の(それほど複雑ではない)ゲームは、そのクラスに詰め込まれたものすべてを管理するのが難しくなりすぎたため、実際には新しいプロジェクトに移行する過程にあります。さらに、Microsoftのチュートリアルはそれを奨励しているようです。
Fibericon

回答:


23

それから青信号があり、物事を整理するために、誰かがGameManagerを作成します。おそらくGameStateの束を保持するために、おそらくいくつかのGameObjectを格納するために、実際には大きなものは何もありません。かわいい、小さな、マネージャー。

ご存知のように、これを読んでいると、頭の中で少し警報が鳴りました。「GameManager」という名前のオブジェクトは、決してかわいい、または小さなものにはなりません。そして誰かがこれをしてコードをクリーンアップしましたか?以前はどのように見えましたか?冗談はさておき、クラスの名前はクラスが何をするのかを明確に示すものである必要があり、これは1つのことです(別名:単一責任の原則)。

また、GameManagerのようなオブジェクトで終了する可能性がありますが、明らかに、非常に高いレベルで存在し、高レベルのタスクに関与する必要があります。ゲームオブジェクトのカタログを維持していますか?おそらく。ゲームオブジェクト間のコミュニケーションを促進しますか?承知しました。オブジェクト間の衝突を計算しますか?番号!これが、名前マネージャーが眉をひそめている理由でもあります-それは広すぎて、そのバナーの下で多くの乱用を可能にします。

クラスサイズに関する簡単な経験則:クラスごとに数百行のコードが実行されている場合、何かがおかしくなり始めています。過度に熱心にならずに、たとえば、300 LOCを超えるコードは私にとってコードの匂いです。1000を超える場合は、警告音が鳴ります。どういうわけか、1000行のコードは、それぞれ250の4つの適切に構造化されたクラスよりも理解しやすいと信じることで、自分を欺いていることになります。

時間が問題になると、別のクラスを記述したり、この巨大なマネージャーをサブマネージャーに分割する方法はありません。

これは、すべての問題が完全に混乱するところまで問題が伝播することを許可されているためだと思います。リファクタリングの実践は本当にあなたが探しているものです- あなたは継続的に小さな増分でコードの設計を改善する必要があります

これを防ぐために何を提案しますか?

問題は技術的なものではないため、技術的な修正を探すべきではありません。問題はこれです。チームにはモノリシックなコードを作成する傾向があり、中長期的にはこのように動作することが何らかの形で有益であるという信念があります。また、ゲームのアーキテクチャを操作する強力なアーキテクチャリードがチームにないようです(少なくとも、この人は忙しすぎてこのタスクを実行できません)。基本的に、唯一の方法は、この考え方が間違っていることをチームメンバーに認識させることです。それは誰も好意しません。製品の品質が低下し、チームはさらに多くの夜を費やして問題を修正します。

幸いなことに、クリーンなコードを作成することの直接的な具体的なメリットは非常に大きいため、ほとんどすべての開発者がそのメリットをすぐに実感します。しばらくこの方法で作業するようにチームを説得し、結果は残りを行います。

難しい部分は、悪いコードの構成要素(およびすぐに優れたデザインを思い付く才能)の感覚を開発することは、開発で学ぶのが難しいスキルの1つであるということです。私の提案は、チーム内でこれを行うことができる十分な年長の誰かがいるという希望にかかっています-そのように人々を説得することははるかに簡単です。

編集-もう少し情報:

一般的に、あなたの問題はゲーム開発に限定されるとは思いません。根本的に、それはソフトウェアエンジニアリングの問題なので、その方向での私のコメントです。異なるのは、ゲーム開発業界の性質であり、他のタイプの開発よりも多くの結果と期限が必要かどうかはわかりません。

ただし、特にゲーム開発については、「特にゲームアーキテクチャ」のヒントに関するStackOverflowのこの質問に対する受け入れられた回答は次のように述べています。

オブジェクト指向設計の堅実な原則に従う....

これは基本的にまさに私が言っていることです。プレッシャーにさらされていると、大きなコードを書いていることに気づきますが、技術的な負債であることを頭に掘り下げました。私にとってはうまくいく傾向がありますが、1日の前半(または4分の3)を費やして中品質のコードを大量に生成し、しばらく休んでからしばらく考えます。コードを少し改善する方法について、頭の中で、または紙/ホワイトボードで少しデザインをしてください。多くの場合、繰り返しコードに気づき、物事を分割することでコードの総行数を実際に減らすことができますが、それでも読みやすさが向上します。今回は投資額が非常に速いので、「投資」と呼ぶのは馬鹿げているように思えます。多くの場合、半日(1週間後)に無駄になった可能性のあるバグを拾います。

  • コードを作成したその日に修正します。
  • あなたは数時間以内に喜んでいるでしょう。

上記を実際に信じることは困難です。何度も何度も効果を経験したからこそ、自分の仕事のためになんとかできました。それでも、コードを修正することを正当化することは、私にとってまだ難しいことです。残念ながら、このアドバイスはおそらくあまりにも一般的すぎて、簡単ではありません。しかし、私はそれを強く信じています!:)

特定の例に答えるには:

ボブおじさんのクリーンコードは、高品質のコードがどのようなものであるかを要約するという素晴らしい仕事をしています。私はたまたまその内容のほぼすべてに同意します。したがって、30000 LOCマネージャークラスの例を考えると、「正当な理由」の部分には本当に同意できません。私は不快に聞こえたくありませんが、そのように考えると問題につながります。単一のファイルにそれほど多くのコードがある理由はありません-それはほぼ1000ページのテキストです!ローカリティの利点(実行速度、または設計の「単純さ」)は、開発者がその怪物をナビゲートしようとして完全に動きが取れなくなることによって直ちに無効になります。

確信が持てない場合は、上記の本のコピーを入手して、それを一読することをお勧めします。この種の考え方を適用すると、きれいで自明なコードを自発的に作成することにつながります。


これは私が一度も見た欠陥ではありません。いくつかのプロジェクトで、いくつかのチームでこれを見てきました。私はこれに苦労している多くの有能で構造化された人々を見てきました。プレッシャーがかかっている場合、300行のコード制限はプロデューサーが気にすることではありません。プレイヤーも。確かにこれは素晴らしくて甘いですが、悪魔は詳細にあります。GameManager私がこれまでに見た最悪のケースは約30.000行のコードであり、そのほとんどが非常に正当な理由でここにあったと確信しています。もちろんこれは極端なことですが、それらは現実の世界で起こります。このGameManager効果を制限する方法を求めています。
ローランCouvidou

@lorancou-かなり多くの追加情報を投稿に追加しましたが、コメントするには少なすぎました。例。
ダニエルB

ああ、私はその本を読んでいなかったので、それは間違いなく良い提案です。ああ、そして私はコードを数千行のクラスに追加する正当な理由があるという意味ではありませんでした。私は、そのような神オブジェクトのすべてのコード何か有用なことをすることを意味しました。私はおそらく少し速すぎると書いた;)
ローランクービドゥー

@lorancou Hehe、それは良いことです...その方法は狂気にあります:)
ダニエルB

「リファクタリングの実践は本当にあなたが探しているものです-あなたは継続的に小さな増分でコードの設計を改善する必要があります。」そこには非常に強い点があると思います。混乱は混乱を引き付けます。それがこの背後にある本当の理由であるため、混乱は長期的に戦わなければなりません。私はこの答えを受け入れますが、それは他に価値がないという意味ではありません!
ローランクーヴィドゥ

7

これは実際にはゲームプログラミングの問題ではなく、一般的なプログラミングの問題です。

ゲームを管理するために、すべてのソースコードが実際にここにあります。

これが、名前に「Manager」が含まれるクラスを提案するオブジェクト指向コーダーに眉をひそめる必要がある理由です。公平を期すために、ゲーム内で「半神」オブジェクトを避けることは事実上不可能だと思いますが、単に「システム」と呼びます。

期限が近づくと、ソフトウェアが汚れる傾向があります。それは仕事の一部です。また、「ダクト型プログラミング」に本質的に問題はありません。特に、数時間で製品を出荷する必要がある場合はそうです。この問題を完全に取り除くことはできず、単に減らすことができます。

当然ですが、GameManagerに物事を入れようとするのであれば、それは非常に健全なコードベースを持っていないことを意味します。2つの問題がある可能性があります。

  • コードが複雑すぎます。あまりにも多くのパターン、時期尚早の最適化、もう誰も流れを理解していません。可能であれば、開発時にTDDをより多く使用するようにしてください。複雑さと戦うための非常に優れたソリューションだからです。

  • コードが適切に構成されていません。あなたは(ab)グローバル変数を使用しています。クラスは疎結合ではなく、インターフェースもイベントシステムもありません。

コードが問題ないようであれば、プロジェクトごとに「何が間違っていたのか」を覚えて、次回はそれを避ける必要があります。

最後に、チーム管理の問題である可能性もあります。十分な時間はありましたか?誰もがあなたが目指していたアーキテクチャについて知っていましたか?地獄は、「Manager」という単語を含むクラスでコミットを許可しましたか?


ダクトテーププログラミングに問題はありません。しかし、私はかなり確信しています-マネージャークラスはゲームエンジンの周りにあります。少なくとも私はそれらをよく見ています。大きくなりすぎない限り、便利です。a SceneManagerまたはaの何が問題なのNetworkManagerですか?なぜそれらを防ぐ必要があるのか​​、または-Managerを-Systemに置き換えるとどうなるのかわかりません。私は通常GameManager、コードの肥大化が少ないものに移行するための代替アーキテクチャの提案を探しています。
ローランCouvidou

SceneManagerの何が問題になっていますか?さて、SceneとSceneManagerの違いは何ですか?ネットワークとネットワークマネージャー?他の側面については、これはアーキテクチャの問題ではないと思いますが、GameManagerにあるものの具体的な例を挙げて、そこにいる必要がなければ、解決策を提案する方が簡単です。
Raveline

OK、-Managerのことは忘れてください。ゲームの状態を処理するクラスがあり、それを呼び出すとしましょうGameStatesCollection。最初は、いくつかのゲーム状態とそれらを切り替える方法があります。次に、ロード画面があり、これがすべて複雑になります。それからプログラマーは、Level_AからLevel_Bに切り替えるときにユーザーがディスクを取り出すときにバグに対処する必要があり、半日リファクタリングせずに修正する唯一の方法はにありGameStatesCollectionます。ブーム、神のオブジェクト。
ローランCouvidou

5

それで、より多くのEnterprise-yの世界から1つの可能なソリューションをもたらします。制御/依存性注入の反転をチェックアウトしましたか?

基本的に、このフレームワークはゲームの他のすべてのコンテナです。それだけで、すべてを結び付ける方法と、オブジェクト間の関係を知っています。どのオブジェクトも、独自のコンストラクター内でインスタンスを作成しません。必要なものを作成するのではなく、IoCフレームワークから必要なものを求めます。IoCフレームワークは、作成時に依存関係を満たすために必要なオブジェクトを「認識」し、それを埋めます。疎結合FTW。

EnemyTreasureクラスがコンテナにGameLevelオブジェクトを要求すると、フレームワークはそれを与えるインスタンスを認識します。どうやってわかるの?以前にインスタンス化したのかもしれませんし、近くのGameLevelFactoryに尋ねるのかもしれません。ポイントは、EnemyTreasureはGameLevelインスタンスがどこから来たかを知らないということです。依存関係が現在満たされていることを知っているだけで、その生活を続けることができます。

ああ、あなたのPlayerSpriteクラスはIUpdateableを実装しています。フレームワークはすべてのIUpdateableオブジェクトをタイマーに自動的にサブスクライブするため、メインのゲームループが存在します。すべてのTimer :: tick()は、すべてのIUpdatedable :: update()メソッドを自動的に呼び出します。簡単ですね。

現実的には、これを行うためにこのbighugeohmygodフレームワークは必要ありません。その重要な部分を自分で行うことができます。ポイントは、-Managerタイプのオブジェクトを作成していることに気付いた場合、おそらく、クラス間で責任を正しく分散していないか、どこかでいくつかのクラスが欠落しているということです。


興味深いのは、IoCについて初めて聞いたときです。これはゲームプログラミングには大きすぎるかもしれませんが、私はそれをチェックします!
ローランクーヴィドゥ

IoC、私たちはそれを常識と呼んでいませんでしたか?
ブライス

半分のチャンスがあれば、エンジニアは何からでもフレームワークを作成します。(=それに、私はフレームワークの使用を支持しておらず、建築パターンを支持しています。
drhayes

3

過去に、次のことを行うと、通常、神のクラスになります。

  • クラスダイアグラム(または単に主要なオブジェクトのスケッチ)を書き出す前に、ゲームエディター/ IDE /メモ帳に座ってください。
  • ゲームの非常に曖昧なアイデアから始めてすぐにコーディングし、それから新しいアイデアが登場するたびに繰り返し始めます
  • GameManagerが実際にドメインオブジェクトであるターンベースの戦略のような状態ベースのゲームを作成していないときに、GameManagerというクラスファイルを作成します。
  • 早い段階でコード編成を気にしない

これは議論の余地があるかもしれませんが、そうであれば笑いますが、プロトタイプを作成するときでさえ、プレイ可能なプロトタイプを書く前に基本的なクラス図を書くべきではないということは考えられません。私は計画する前に技術的なアイデアをテストする傾向がありますが、それはそれについてです。したがって、ポータルを作成したい場合は、ポータルを機能させるための非常に基本的なコードを作成し、いくつかのマイナーな計画に大丈夫と言ってから、プレイ可能なプロトタイプに取り組むことができます。


1

私はこの質問が今では古いことを知っていますが、2セントを足したいと思いました。

ゲームを設計するとき、私はほとんど常にGameオブジェクトを持っています。ただし、それは非常に高レベルであり、実際には「何もしません」。

たとえば、GraphicsクラスにRenderを指示しますが、レンダリングプロセスとは関係ありません。LevelManagerに新しいレベルをロードするように要求する場合がありますが、ロードプロセスとは関係ありません。

要するに、私は他のクラスの使用を調整するために半神のようなオブジェクトを使用します。


2
これはまったく神のようなものではありません-これは抽象化と呼ばれます。:)
ヴォーンヒルツ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.