Luaはゲームのスクリプト言語としてどのように機能しますか?


67

私は、Luaが何であるか、C ++でプログラムされたゲームがそれをどのように使用するかについて少しhaんでいます。私は主にそれがどのようにコンパイルされ実行されるかについて尋ねています。

たとえば、Luaスクリプトを使用するC ++で記述されたプログラムを使用する場合:Luaのコードは、C ++で記述されたメインプログラムの関数を呼び出し、コンパイルされてC ++のメモリヒープに追加されるのを待機している未コンパイルクラスとして機能しますプログラム?

それとも、メインプログラムとは完全に分離したプログラムを実行するだけのLinuxのbashスクリプトのように動作しますか?

回答:


90

スクリプトは、(概念的に)別のプログラム(ホスト)内で実行されているプログラム(スクリプト)を持つプログラミングの抽象化です。ほとんどの場合、スクリプトを記述する言語は、ホストを記述する言語とは異なりますが、プログラム内のプログラムの抽象化はスクリプトと見なすことができます。

概念的には、スクリプトを有効にする一般的な手順は次のとおりです(ホストにpseudo-cを、スクリプトにpseudo-luaを使用します。これらは正確な手順ではなく、スクリプトを有効にする一般的なフローに似ています)

  1. ホストプログラムで仮想マシンを作成します。

    VM m_vm = createVM();
  2. 関数を作成してVMに公開します。

    void scriptPrintMessage(VM vm)
    {
        const char* message = getParameter(vm, 0); // first parameter
        printf(message);
    }
    
    //...
    
    createSymbol(m_vm, "print", scriptPrintMessage);
    

    我々は機能を公開した名前が(ことに注意してくださいprint()関数自体の内部名と一致する必要はありませんscriptPrintMessage

  3. 関数を使用するスクリプトコードを実行します。

    const char* scriptCode = "print(\"Hello world!\")"; // Could also be loaded from a file though
    doText(m_vm, scriptCode);
    

これですべてです。その後、プログラムは次のように流れます。

  1. を呼び出しますdoText()。次に、制御が仮想マシンに転送され、仮想マシンが内のテキストを実行しますscriptCode

  2. スクリプトコードは、以前にエクスポートされたシンボルを見つけますprint。次に、制御を関数に転送しますscriptPrintMessage()

  3. ときにscriptPrintMessage()終了し、制御は、仮想マシンに戻って転送されます。

  4. 内のすべてのテキストscriptCodeが実行されると、doText()終了し、制御はの後の行でプログラムに戻されますdoText()

したがって、一般的に、あなたがしているのは、別のプログラム内でプログラムを実行することだけです。理論的に言えば、スクリプトなしではできないスクリプトでできることは何もありませんが、この抽象化により、興味深いことを本当に簡単に行うことができます。それらのいくつかは次のとおりです。

  • 懸念の分離:ゲームエンジンをC / C ++で記述し、次に実際のゲームをluaなどのスクリプト言語で記述するのが一般的なパターンです。正しく実行すると、ゲームコードはエンジン自体から完全に独立して開発できます。

  • 柔軟性:スクリプト言語は一般的に解釈されるため、スクリプトの変更は必ずしもプロジェクト全体の再構築を必要としません。プログラムを再起動しなくても、スクリプトを変更して結果を確認できます。

  • 安定性とセキュリティ:スクリプトは仮想マシン内で実行されているため、正しく実行されていれば、バグのあるスクリプトがホストプログラムをクラッシュさせることはありません。これは、ユーザーがゲームに独自のスクリプトを記述できるようにする場合に特に重要です。独立した仮想マシンを好きなだけ作成できることを忘れないでください!(かつて、それぞれのマッチが別々のlua仮想マシンで実行されるMMOサーバーを作成しました)

  • 言語機能:ホスト言語とスクリプト言語の選択に基づいてスクリプト言語を使用する場合、各言語が提供する必要のある最高の機能を利用できます。特に、luaのコルーチンは非常に興味深い機能であり、CまたはC ++での実装は非常に困難です。

ただし、スクリプトは完璧ではありません。スクリプトの使用には、いくつかの一般的な欠点があります。

  • デバッグは非常に難しくなります。通常、一般的なIDEに含まれるデバッガーは、スクリプト内のコードをデバッグするようには設計されていません。このため、コンソールトレースデバッグは、私が望むよりはるかに一般的です。

    luaのような一部のスクリプト言語には、Eclipseのような一部のIDEで利用できるデバッグ機能があります。これを行うのは非常に難しく、正直に言って、スクリプトデバッグがネイティブデバッグと同様に機能するのを見たことはありません。

    ちなみに、Unity開発者がこの問題に極端に関心を持っているのは、ゲームエンジンに対する私の主な批判であり、それをもう使用しない主な理由ですが、私は脱線します。

  • IDE統合:IDEがプログラムからエクスポートされている関数を認識している可能性は低いため、IntelliSenseなどの機能がスクリプトで機能することはほとんどありません。

  • パフォーマンス:一般的に解釈されるプログラムであり、アーキテクチャが実際のハードウェアと異なる可能性がある抽象仮想マシンを対象としているため、スクリプトは一般にネイティブコードよりも実行が遅くなります。ただし、luaJITやV8などの一部のVMは、非常に優れた機能を発揮します。これは、スクリプトを非常に頻繁に使用する場合に顕著になります。

    通常、コンテキストの変更(ホストからスクリプトへ、およびスクリプトからホストへ)は非常に高価なので、パフォーマンスの問題がある場合はそれらを最小限に抑えることができます。

スクリプトの使用方法はユーザー次第です。設定の読み込みのような単純なものから、非常に薄いゲームエンジンを使用してゲーム全体をスクリプト言語で作成するような複雑なものに使用されるスクリプトを見てきました。luaとJavaScript(V8経由)のスクリプティングを組み合わせた非常にエキゾチックなゲームエンジンを見たことがあります。

スクリプトは単なるツールです。素晴らしいゲームを作るためにそれをどのように使うかは完全にあなた次第です。


私はこれに本当に新しいですが、私はユニティを使用しています。Unityについて何かお話しましたが、詳しく説明していただけますか?他のものを使用する必要がありますか?
トカモチャ14

1
私はprintfあなたの例では使用しません(または少なくとも使用printf("%s", message);
ラチェットフリーク14

1
@ratchetfreak:私にウインクされた括弧が続くあなたのメッセージの最後にセミコロン...
パンダパジャマ

1
このサイトで長い間見た中で最高の答えの1つです。それは得るすべての賛成に値する。とてもうまくできました。
スティーブンスタドニッキー14

1
ゲームでのLuaのポスターの子はWorld of Warcraftです。UIのほとんどはLuaで記述されており、そのほとんどはプレーヤーで置き換えることができます。あなたがそれについて言及しなかったことに驚いています。
マイケルハンプトン14

7

一般的に、いくつかのネイティブ関数をLuaにバインドまたは公開します(多くの場合、ユーティリティライブラリを使用して、手動で実行できます)。これにより、ゲームでLuaコードを実行するときにLuaコードがネイティブC ++コードを呼び出すことができます。この意味で、Luaコードがネイティブコードを呼び出すだけであるという前提は正しい(Luaには独自の機能の標準ライブラリが用意されているが、すべてのためにネイティブコードを呼び出す必要はない)。

Luaコード自体はLuaランタイムによって解釈されます。これは、独自のプログラムで(通常)ライブラリとしてリンクするCコードです。Luaの仕組みについて詳しくは、Luaのホームページをご覧ください。特に、C ++は実際には動的にコンパイルされることはほとんどないため、特にC ++クラスを考えている場合、Luaは「コンパイルされていないクラス」ではありません。ただし、Luaランタイムと、ゲームが実行するLuaスクリプトによって作成されたオブジェクトは、ゲームのシステムメモリのプールのスペースを消費します。


5

Luaのようなスクリプト言語は、いくつかの方法で使用できます。前述のように、Luaを使用してメインプログラムの関数を呼び出すことができますが、必要に応じてC ++側からLua関数を呼び出すこともできます。一般に、選択したスクリプト言語である程度の柔軟性を持たせるためのインターフェイスを構築し、一連のシナリオでスクリプト言語を使用できるようにします。Luaのようなスクリプト言語の素晴らしいところは、コンパイルされるのではなく解釈されるため、その場でLuaスクリプトを変更できるため、ゲームをコンパイルするのを待つ必要がなく、もしあなたが「やったことはあなたの好みに合わない」

Luaコマンドは、C ++プログラムがコマンドを実行するときにのみ呼び出されます。そのため、コードは呼び出されたときにのみ解釈されます。Luaはメインプログラムとは別に実行されるbashスクリプトと見なすことができると思います。

一般的に、スクリプト言語を使用して、さらに更新したり、さらに反復したりすることができます。多くの企業がGUIにそれを使用しているのを見てきたので、インターフェイスに多くのカスタマイズ性を提供します。彼らがどのようにLuaインターフェースを作成したかを知っていれば、GUIを自分で変更することもできます。ただし、AIロジック、武器に関する情報、キャラクターの会話など、Luaを使用するために利用できる他の多くのパスがあります


3

Luaを含むほとんどのスクリプト言語は、基本的にスクリプト命令を「実際の」CPU命令または関数呼び出しにマッピングするシステムである仮想マシン(VM)上で動作します。通常、Lua VMはメインアプリケーションと同じプロセスで実行されます。これは、それを使用するゲームに特に当てはまります。Lua APIは、スクリプトファイルをロードおよびコンパイルするためにネイティブアプリケーションで呼び出すいくつかの関数を提供します。たとえばluaL_dofile()、指定されたスクリプトをLua バイトコードにコンパイルしてから実行します。このバイトコードは、API内で実行されているVMによってネイティブマシンの命令と関数呼び出しにマッピングされます。

C ++などのネイティブ言語をスクリプト言語に接続するプロセスは、bindingと呼ばれます。Luaの場合、そのAPIは、ネイティブ関数をスクリプトコードに公開するのに役立つ関数を提供します。したがって、たとえば、C ++関数say_hello()を定義し、この関数をLuaスクリプトから呼び出し可能にすることができます。Lua APIには、スクリプトの実行時に表示されるC ++コードを介して変数とテーブルを作成するためのメソッドも用意されています。これらの機能を組み合わせることにより、C ++クラス全体をLuaに公開できます。逆も可能です。LuaAPIにより、ユーザーはLua変数を変更し、ネイティブC ++コードからLua関数を呼び出すことができます。

すべてではありませんが、ほとんどのスクリプト言語には、スクリプトコードとネイティブコードのバインドを容易にするAPIが用意されています。また、ほとんどはバイトコードにコンパイルされ、VMで実行されますが、一部は行ごとに解釈される場合があります。

これがあなたの質問のいくつかを明確にするのに役立つことを願っています。


3

誰もこれに言及していないので、興味がある人のためにここに追加します。ゲームスクリプティングマスタリーと呼ばれる主題に関する本があります。これはかなり前に書かれた素晴らしいテキストですが、今日でも完全に関連しています。

この本では、スクリプト言語がネイティブコードにどのように適合するかを示すだけでなく、独自のスクリプト言語を実装する方法も説明します。これは99%のユーザーにとってはやり過ぎですが、何かを実際に実装するよりも(非常に基本的な形式であっても)何かを理解するより良い方法はありません。

ゲームエンジンを自分で記述したい場合(またはレンダリングエンジンのみで作業する場合)、このテキストは、スクリプト言語をエンジン/ゲームに最適に組み込む方法を理解するのに非常に役立ちます。

また、独自のスクリプト言語を作成する場合は、これが開始するのに最適な場所の1つです(私の知る限り)。


2

まず、スクリプト言語は通常コンパイルされません。これは、一般的にスクリプト言語として定義されているものの大部分です。多くの場合、代わりに「解釈」されます。これが本質的に意味することは、テキストをリアルタイムで読み取り、操作を行ごとに実行している別の言語(ほとんどの場合、コンパイルされている言語)があることです。

この言語と他の言語の違いは、スクリプト言語はより単純になる傾向があることです(一般に「高レベル」と呼ばれます)。ただし、コンパイラは、コーディングの「人間の要素」に伴う多くの問題を最適化する傾向があるため、少し遅くなる傾向があり、結果として得られるバイナリは、マシンに対してより小さく、より速く読み取る傾向があります。さらに、コンパイルされたプログラムで、実行中のコードを読み取るために実行する必要がある別のプログラムからのオーバーヘッドが少なくなります。

今、あなたは、「それは少し簡単だと思いますが、一体なぜ誰かがそのパフォーマンスを少しでも使いやすくするために放棄するのでしょうか?」と考えているかもしれません。

この仮定はあなただけではありませんが、スクリプト言語で行う傾向に応じて、スクリプト言語で何をしているのかによって、パフォーマンスを犠牲にするだけの価値があります。

基本的 に、実行されるプログラムの速度よりも開発の速度が重要な場合、スクリプト言語を使用します。 ゲーム開発には、このような状況がたくさんあります。特に、高レベルのイベント処理のような些細なことを扱う場合。

編集:luaがゲーム開発でかなり人気がありそうな理由は、おそらく世界最速の(最も高速ではないにしても)最も一般的に利用可能なスクリプト言語の1つだからです。ただし、この余分な速度により、利便性の一部が犠牲になりました。とはいえ、CやC ++をそのまま使用するよりも、おそらく間違いなく便利です。

重要な編集: さらなる調査の結果、スクリプト言語の定義に関して、さらに多くの論争があることがわかりました(Ousterhoutの二分法を参照)。言語を「スクリプト言語」として定義することの第一の批判は、それが解釈またはコンパイルされる言語の構文またはセマンティクスにとって重要ではないということです。

通常「スクリプト言語」と見なされる言語は、コンパイルされるのではなく、通常、伝統的に解釈されますが、「スクリプト言語」としての定義の長短は、実際に人々がそれらを見る方法と作成者がそれらを定義する方法の組み合わせ次第です。

一般的に、言語は次の基準を満たしていれば、簡単にスクリプト言語と見なすことができます(Ousterhoutの二分法に同意すると仮定します)(上記のリンクされた記事による)。

  • 動的に入力されます
  • 複雑なデータ構造をほとんど、またはまったく備えていない
  • それらのプログラム(スクリプト)は解釈されます

さらに、別のプログラミング言語(通常はスクリプト言語と見なされない言語)と対話して機能するように設計されている場合、その言語はスクリプト言語であると受け入れられることがよくあります。


1
「スクリプト言語はコンパイルされていません」と書いた最初の行であなたに同意しません。実際、LuaはJITコンパイラーによって実行される前に中間バイトコードにコンパイルされます。もちろん、行ごとに解釈されるものもありますが、すべてではありません。
glampert

「スクリプト言語」の定義に同意しません。スクリプト形式ではなく、コンパイル形式で比較的一般的に使用される(またはC#の場合のように、その逆の)二重の用途を持つ言語(LuaやC#など)があります。言語がスクリプト言語として使用される場合、その言語は解釈され、コンパイルされない言語として厳密に定義されます。
グルガドゥルゲン14

結構、あなたの定義はウィキペディアとよりインラインです:「スクリプト言語またはスクリプト言語は、スクリプトをサポートするプログラミング言語であり、(コンパイルではなく)解釈できる特別なランタイム環境用に作成されたプログラムです...」それでは私のコメント。
glampert

1
通常のLuaでも完全にバイトコードにコンパイルされますが、JITコンパイラーでもネイティブバイナリを生成できます。いいえ、Luaは解釈されません。
オレグV.ボルコフ14

最初の部分は不確かです、私はダウン票を投じたいです。彼は最終的に解釈され、@ OlegV.Volkov JITコンパイルは何かをコンパイルしません。コンパイルはコンパイル時間によって定義されますが、Luaにはありますが、Luaバイトコードにはありません(JITまたはJITなし)。用語を混乱させないでください。
アレックティール14
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.