グローバル/シングルレットがゲーム開発に役立つケースはありますか?[閉まっている]


11

グローバル変数またはシングルトンクラスがあると、テスト/管理が困難なケースが作成されることはわかっています。コードでこれらのパターンを使用することで逮捕されましたが、しばしば出荷されました。

では、グローバル変数やシングルトンが実際にゲーム開発に役立つケースはありますか?

回答:


30

これらの事柄は常に役立ちます。それが最もかっこいい、または最も安全なソリューションであるかどうかは別の問題ですが、ゲーム開発にはある程度の実用性が含まれていると思います。


私のマントラに非常に近い。
オラフルWaage

15

間違いなく非網羅的なリストですが、ここに行きます:

短所

  • 生涯管理。シングルトンとグローバルは、設定方法によっては、キーシステム(ヒープなど)が初期化される前に起動する場合があります。それらを破棄したい場合(たとえば、リーク追跡を実行している場合に便利です)、破棄の順序に注意するか、フェニックスシングルトンなどに取り掛かる必要があります。(静的な初期化順序の失敗を参照してください。)
  • アクセス制御。更新は非constを取得しますが、レンダリングはゲームデータへのconstアクセスのみを持つ必要がありますか?これは、シングルトンやグローバルで実施するのが難しいかもしれません。
  • 。Kajが指摘したように、非共有メモリアーキテクチャで機能するようにシングルトンを機能的に分解および変換することは困難です。また、他のタイプのNUMAシステム(ローカルではないメモリにアクセスする)のパフォーマンスに影響を与える可能性もあります。シングルトンは通常、集中化された状態を表すため、純粋さによって簡単にできる変換の正反対です。

賛否両論

  • 並行性。同時実行環境では、シングルトンは苦痛(データの競合/再入可能性の問題を考慮する必要があります)または祝福(集中化が容易で、ロックとリソース管理について推論できます)のいずれかになります。スレッドローカルストレージなどの賢い使い方は、潜在的な問題を多少軽減できますが、通常は簡単な問題ではありません。
  • Codegen(コンパイラー、アーキテクチャーなどに依存):単純な初回使用時に作成するシングルトン実装の場合、アクセスごとに追加の条件付きブランチを評価している可能性があります。(これは追加される場合があります。)関数で使用される複数のグローバルは、リテラルプールを膨らませる場合があります。「すべてのグローバルの構造体」アプローチでは、リテラルプールのスペースを節約できます。構造体のベースへのエントリは1つだけであり、ロード命令でエンコードされたオフセットです。最後に、グローバルとシングルトンをすべてのコストで回避する場合は、通常、ポインタ、参照、またはコピーを渡すために、少なくとも少し余分なメモリ(レジスタ、スタック、またはヒープ)を使用する必要があります。

長所

  • シンプルさ。上記の短所がそれほど影響しないことがわかっている場合(たとえば、シングルCPUハンドヘルドプラットフォームのシングルスレッド環境で作業している場合)、一部のアーキテクチャ(前述の引数渡しなど)を避けます。シングルトンとグローバルは、経験の浅いコーダーにとっては理解しやすいかもしれません(ただし、それらを誤って使用する方が簡単かもしれません)。
  • 生涯管理(再び)。「グローバルの構造体」または最初のリクエストで作成以外のメカニズムを使用する場合、読み取りが簡単で、初期化と破棄の順序をきめ細かく制御できます。これをある程度自動化するか、手動で管理します(管理する必要があるのは、グローバル/シングルレットンの数とそれらの相互依存関係に応じて、賛成か反対かです)。

ハンドヘルドタイトルでは、「グローバルシングルトンの構造体」をよく使用します。PCとコンソールのタイトルは、それらにあまり依存しない傾向があります。さらに、イベント駆動型/メッセージングアーキテクチャに切り替えます。そうは言っても、pc / consoleのタイトルは依然として中央のTextureManagerをしばしば使用します。通常は単一の共有リソース(テクスチャメモリ)をラップするので、これは私たちにとって理にかなっています。

APIを比較的クリーンに保つ場合、必要なときにシングルトンパターンからリファクタリングする(またはシングルトンパターンにリファクタリングする)ことはそれほど難しくありません...


素晴らしいリスト。個人的には、共有された(おそらく変更可能な)状態に起因する問題のため、シングルトンとグローバルでは負の値しか見つかりません。「codegen」の問題が問題であるとは思えません。レジスターを介してポインターを渡す必要があるか、それを取得するためにいくつかの操作シーケンスを実行する必要があります。コードとデータサイズの違いはわずかですが、合計はほぼ同じです。
ダッシュトムバン

うん、codegenに関して言えば、実際に大きな違いが見られるのは、個々のグローバルからグローバルテーブルへの変更だけでした。リテラルプール、つまり.textセクションサイズが数パーセント縮小されました。これは、小さなシステムでは非常に良いことです。=)リテラルでdcacheにそれほど影響を与えないことのパフォーマンス上の利点は、(ほとんどの場合はごくわずかですが)良い副次的な利点でした。グローバルテーブルに移動するのもう一つの利点は、本当に簡単に...高速なメモリに常駐セクションに移動する能力だった
リアンダー

4

それらは、特にプロトタイピングや実験的な実装の際に非常に役立ちますが、一般的には、構造体のようなマネージャーに参照を渡すことをお勧めします。これにより、少なくともどこからアクセスするかをある程度制御できます。グローバルとシングルトン(私の意見では)の最大の問題は、それらがマルチスレッドに対応していないことであり、それらを使用するコードは、SPUなどの非共有メモリへの移植がはるかに困難です。


2

シングルトン設計自体はまったく役に立たないと思います。グローバル変数は明らかに便利ですが、適切に記述されたインターフェースの背後に隠されているので、それらの存在を意識する必要はありません。シングルトンを使用すると、その存在を明確に認識できます。

エンジン全体にアクセスする必要があるものには、しばしばグローバル変数を使用します。私のパフォーマンスツールは、エンジン全体で使用する良い例の1つです。呼び出しは簡単です。ProbeRegister()、ProbeHit()、ProbeScoped()。彼らの実際のアクセスは少しトリッキーであり、いくつかのもののためにグローバル変数を使用します。


2

グローバルと不十分に実装されたシングルトンの主な問題は、あいまいな構築と解体のバグです。

したがって、これらの問題のないプリミティブを使用する場合、またはポインターの問題を非常に認識している場合。その後、安全に使用できます。

グローバルは、gotoと同じようにその場所を持ち、すぐに解雇されるべきではなく、注意して使用されます。

Google C ++スタイルガイドでそれについての良い説明があります


それが主な問題だとは思いません。「グローバルを使用しない」は、存在コンストラクタよりも前に遡ります。それらには2つの大きな問題があると思います。まず、プログラムに関する推論が複雑になります。グローバルにアクセスする関数がある場合、その関数の動作は自身の引数だけでなく、グローバルにアクセスする他のすべての関数にも依存します。それはもっと考える必要があります。第2に、すべてを頭の中でつかむことができても(できません)、それでも並行処理の問題はさらに複雑になります。

@Joeのキーワードは「依存関係」です。グローバル(またはシングルトンやその他の共有状態)にアクセスする関数は、それらすべてに暗黙的な依存関係を持っています。依存関係のすべてが明示的である場合、少しのコードについて推論する方がはるかに簡単です。これは、依存関係の完全なリストが引数リストに記載されているときに得られるものです。
ダッシュトムバン

1

グローバルは、関数呼び出しの間に何らかの状態を必要とするシステムのプロトタイプをすばやく作成するときに役立ちます。システムが機能することを確認したら、状態をクラスに移動し、関数をそのクラスのメソッドにします。

シングルトンは、自分自身や他の人に問題を引き起こすのに役立ちます。導入するグローバルな状態が多ければ多いほど、コードの正確性、保守、拡張性、同時実行性などに関して、より多くの問題が発生します。そうしないでください。


1

私は、シングルトンの代わりにカスタムライフタイム管理のある種類のDI / IoCコンテナーを使用することをお勧めします(「シングルインスタンス」ライフタイムマネージャーを使用している場合でも)。少なくとも、テストを容易にするために実装を交換するのは簡単です。


ご存じない方のために説明すると、DIは「依存性注入」、IoCは「制御の反転」です。リンク:martinfowler.com/articles/injection.html (これらについては以前読んだことがありましたが、頭字語を見たことがないため、少し検索する必要がありました。)ただし、この優れた変換について言及した場合は+1。
2010

0

シングルトンのメモリ節約機能が必要な場合は、フライウェイトデザインパターンを試すことができますか?

http://en.wikipedia.org/wiki/Flyweight_pattern

上記のマルチスレッドの問題に関する限り、スレッド間で共有される可能性のあるリソースのロックメカニズムを実装することは、ある程度簡単に見通せるはずです。 http://en.wikipedia.org/wiki/Read/write_lock_pattern


0

シングルトンは、初期状態のプロトタイプに共有状態を格納するための優れた方法です。

これらは特効薬ではなく、いくつかの問題が発生しますが、特定のUI /ロジック状態に非常に役立つパターンです。

たとえば、iOSでは、[UIApplication sharedApplication]を取得するためにシングルトンを使用します。cocos2dでは、それを使用して[CCNotifications sharedManager]などの特定のオブジェクトへの参照を取得できます。個人的には、通常、[Game sharedGame]シングルトンから開始できます。多くの異なるコンポーネント間で共有される状態を保存します。


0

うわー、これは私にとって興味深いものです。個人的にシングルトンパターンに問題を抱えたことは一度もないので。私の現在のプロジェクトはニンテンドーDS用のC ++ゲームエンジンであり、それらはグローバルC基礎となるライブラリの関数。


私の質問は、なぜシングルトンを使用するのでしょうか?静的関数のみで構成されるシングルトンまたはクラスでAPIをラップするのが好きな人がいるようですが、なぜクラスに悩むのでしょうか。関数呼び出し(希望どおりにラップ)するだけでも簡単に思えますが、それらの内部では「グローバル」状態にアクセスします。たとえば、Log :: GetInstance()-> LogError(...)は、クライアントコードが知る必要なくグローバルな状態に内部的にアクセスするLogError(...)の場合もあります。
ダッシュトムバン

0

コントローラーが1つしかなく、複数のモジュールで処理されるアイテムがある場合のみ。

たとえば、マウスインターフェースなど。またはジョイスティックのインターフェース。または音楽プレーヤー。またはサウンドプレーヤー。または画面。または、ファイル保管マネージャー。


-1

グローバルははるかに高速です!したがって、ゲームのようなパフォーマンス重視のアプリケーションに完全に適合します。

シングルトンは、より優れたグローバルなIMOであり、適切なツールです。

控えめに使用してください!


よりもずっと速い?グローバルは、ポインタの場合はランダムなメモリページに配置され、値の場合は固定されているがおそらく離れたメモリページに配置されます。コンパイラーは、有用なのぞき穴の最適化を使用できません。私が考えることができるあらゆる手段で、グローバルは遅いです。

ゲッターとセッターよりも速く、参照を渡すよりも高速です。しかし、それほどではありません。サイズを抑えるのに役立つので、一部のシステムでは役立ちます。私が「たくさん」と言ったとき、私はおそらく大げさに言ったかもしれませんが、私はあなたが何かを使うべきではないと言っている人にとても懐疑的です。あなただけの常識を使用する必要があります。結局のところ、シングルトンは静的クラスメンバーにすぎず、それらはグローバルです。
jacmoe 2010

ただし、グローバルとの同期の問題を解決するには、それらのゲッター/セッターが必要であるため、実際には関係ありません。グローバル状態に伴う問題(およびまれな利点)は、それがグローバル状態であることであり、速度や(事実上)インターフェースの構文の側面に関する懸念はありません。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.