各ゲーム ループの繰り返しで main.lua スクリプトを呼び出す - 良い設計か悪い設計か? パフォーマンスに(相対的に)どのように影響しますか?
からゲームの状態を維持します。C++ ホスト プログラムまたはb。Lua スクリプトまたはcから。両方から同期しますか?
(トピックに関する前の質問: Lua と C++: 職務の分離)
(すべての回答に投票します。最良の回答が受け入れられます。)
各ゲーム ループの繰り返しで main.lua スクリプトを呼び出す - 良い設計か悪い設計か? パフォーマンスに(相対的に)どのように影響しますか?
からゲームの状態を維持します。C++ ホスト プログラムまたはb。Lua スクリプトまたはcから。両方から同期しますか?
(トピックに関する前の質問: Lua と C++: 職務の分離)
(すべての回答に投票します。最良の回答が受け入れられます。)
My basic rule for lua is - or any script language in a game -
Basically, any code thats called at >33-100Hz (depending on frame rate) is C++ I try to invoke the script engine <10Hz.
Based on any kind of actual metric? not really. but it does put a dividing line in the design, with c++ and lua tasks clearly delineated - without the up front delineation the per frame lua tasks will grow until they are bogging processing per frame - and then theres no clear guideline on what to prune.
lua の最も良い点は、軽量の VM を備えていることです。チャンクがプリコンパイルされた後、VM でそれらを実行すると、実際には非常に高速ですが、それでも C++ コードほど高速ではなく、毎回 lua を呼び出す必要はないと思います。レンダリングされたフレームは良い考えです。
ゲームの状態を C++ に置き、到達できる関数を lua に追加して、状態を変更します。イベントベースのアプローチはほぼより優れており、イベントの登録は lua で行う必要があります (できればゲームの開始時または特定のゲームイベントでのみ、ただし 1 分間に数回のみ)。ただし、実際のイベントはC++ コード。ユーザー入力もイベントであり、通常はすべてのフレームで発生するわけではありません (おそらく MouseMove を除きますが、このため慎重に使用する必要があります)。ユーザー入力イベントを処理する方法 (すべて (どのキーが押されたかなど) を lua で処理するか、またはたとえばキーボードのキーごとに個別のイベントがあるか (極端な場合) かどうか) は、ゲームによって異なります。しようとしています (ターン制のゲームでは、すべてのイベントに対してイベント ハンドラーが 1 つしかない場合がありますが、RTS ではより多くのイベントが必要です。FPS は注意して扱う必要があります (主に、マウスの移動はフレームごとに行われるため)。一般に、イベントの種類が分かれば、lua でコーディングする必要が少なくなります (パフォーマンスが向上します)。イベント" (lua コードをより複雑にする必要があるため、実際にはパフォーマンスが低下する可能性があります)。
あるいは、パフォーマンスが本当に重要な場合は、実際に新しいオペコードを追加することで lua VM を改善できます (私はこれを行う会社を見てきましたが、主にコンパイルされた lua チャンクの逆コンパイルをより困難にするためです)。これは実際にはそうではありません。するのは難しいことです。getglobal
lua コードで何度も実行する必要があるもの (イベントの登録、イベントの実行、ゲームの状態の変更など) がある場合は、それらを lua VM に実装することをお勧めします。setglobal
オペコードは 1 つまたは 2 つしか取りません (たとえば、0 から 255 および 0 から 65535 のパラメーターを使用して SETSTATE オペコードを作成できます。ここで、最初のパラメーターは変更する状態を記述し、2 番目のパラメーターは状態の新しい値を記述します。もちろん、これは最大 255 のイベントがあり、最大 2^16 の値がある場合にのみ機能しますが、場合によっては十分かもしれません.また、これが 1 つのオペコードしか必要としないという事実は、コードがより高速に実行されることを意味します)。また、lua コードをわかりにくくする場合は、逆コンパイルがより困難になります (ただし、lua の内部動作を知っている人にとってはそれほど重要ではありません)。フレームごとにいくつかのオペコード (約 30 ~ 40 のトップ) を実行しても、パフォーマンスにそれほど影響はありません。しかし、lua VM の 30 ~ 40 のオペコードは役に立ちません。
私はC++が好きではありません。しかし、私はゲームが好きです。
私のアプローチは少し異例かもしれません。Lua でできることはすべて行い、C++ では絶対最小限のことだけを行います。ゲーム ループ、エンティティなどはすべて Lua で行われます。Lua で QuadTree を実装したこともあります。C++ は、外部ライブラリとのインターフェースだけでなく、グラフィカルおよびファイルシステムの処理も行います。
これはマシンベースの決定ではなく、プログラマーベースの決定です。C++ よりも Lua の方がはるかに高速にコードを出力できます。そのため、私はプログラマー サイクルを、コンピューター サイクルの節約ではなく、新しい機能に費やしています。私のターゲット マシン (過去 3 年間のすべてのラップトップ) は、この量の Lua に非常に簡単に対処できます。
Lua は驚くほどフットプリントが少ないです (知らない場合はluaJITを調べてください)。
これは、ボトルネックを見つけた場合 (まだ見つけていません)、遅い部分を見つけるためにゲームのプロファイルを作成し、その部分を C++ に変換します... Luaを使用して回避します。
私が取り組んでいるゲームで初めて Lua を使用しています。私のアプリケーションの C++ 側は、実際には各ゲーム ステートのインスタンスへのポインタを保持しています。一部のゲーム ステートは C++ で実装され、一部は Lua で実装されます (「ゲーム プレイ」ステートなど)。
更新とメインのアプリケーション ループは、C++ 側で実行されます。Lua VM が実行時に新しいゲーム状態をアプリケーションに追加できるようにする関数を公開しました。
リソースが限られているハードウェア (ビデオが統合された Atom プロセッサ) で実行しても、速度の問題はまだ発生していません。Lua 関数はフレームごとに呼び出されます。私のアプリケーションで (時間の点で) 最もコストのかかる操作は、レンダリングです。
新しいステートを完全に Lua で作成できる機能は、このプロジェクトで行った最良の決定の 1 つです。これにより、ゲーム全体を再コンパイルすることなく、ゲームの一部を自由に追加できるからです。
編集:私はLuabindを使用しています。これは、他のバインディングフレームワークやもちろんLua C APIよりも一般的にパフォーマンスが遅いと読んでいます。
IMHO Lua スクリプトは特定の動作のためのものです。Lua スクリプトを毎秒 60 回呼び出すと、パフォーマンスが確実に低下します。
Lua スクリプトは、多くの場合、振る舞いや特定のイベントをゲーム エンジン ロジック (GUI、アイテム、ダイアログ、ゲーム エンジン イベントなど) から分離します。たとえば、Lua の適切な使用法は、爆発 (パーティクル FX) をトリガーする場合です。ゲーム キャラクターがどこかを歩く場合、エンジンでそのイベントの出力をハードコーディングするのは非常に見苦しい選択です。ただし、エンジンが正しいスクリプトをトリガーするようにして、その特定の動作をエンジンから分離することをお勧めします。
2 つの場所 (Lua とエンジン) で状態の同期を維持する複雑さのレベルを上げるのではなく、ゲームの状態を 1 つの部分に保持しようとすることをお勧めします。これにスレッドを追加すると、非常に見苦しい混乱が生じることになります。 . 複雑にしないでおく。(私のデザインでは、ほとんどのゲーム ステートを C++ で保持しています)
あなたのゲームで頑張ってください!
十分に複雑なゲームには、独自の動作を持つ複数のゲーム オブジェクトがあるため、すべてのフレーム イテレーションで Lua スクリプト全体を実行したくないでしょう。つまり、大規模なゲームの動作の特定の部分を処理する小さなスクリプトを複数用意しない限り、Lua の利点は失われます。lua_call 関数を使用して、ファイル全体だけでなく、スクリプト内の適切な lua ルーチンを呼び出すことができます。
ここに理想的な答えはありませんが、ゲーム ステートの大部分は、伝統的にゲーム エンジン (つまり C++) に保存されます。Lua に割り当てた意思決定を Lua が実行できるように、Lua に明らかにします。
どの言語がどの行動に適しているかを検討する必要があります。Lua は高レベルの制御と決定に役立ち、C++ はパフォーマンス指向のコードに役立ちます。Lua は、再コンパイルせずに調整する必要があるゲームの部分に特に役立ちます。たとえば、すべての魔法の定数と変数を Lua に入れることができます。グラフィックスやオーディオのレンダリングなど、Lua が属さない場所に Lua を押し付けようとしないでください。
1の性能について:main.lua
変わらない場合はlua_loadfile
orで一度読み込んでloadfile
、返ってきた関数への参照を保存しておき、必要なときに呼び出す。
Lua と C++ の間のバインディングによって、パフォーマンスのほとんどが失われます。関数呼び出しは、実際にはラップして再ラップする必要があり、通常はそのようなことが数回行われます。通常、純粋な Lua または純粋な C++ コードは、混合コードよりも高速です(小規模な操作の場合)。
そうは言っても、私は個人的に、フレームごとに Lua スクリプトを実行する際に大きなパフォーマンス ヒットが見られませんでした。
通常、スクリプティングは高レベルで優れています。Lua は、 Bots ( Quake 3 ) やユーザー インターフェイス( World of Warcraft )の有名なゲームで使用されています。高レベルでの使用 Lua マイクロスレッドは便利です。コルーチンは (実際のスレッドと比較して) 大幅に節約できます。たとえば、Lua コードをたまにしか実行しない場合などです。