スクリプトずは䜕で、䜕を䜿っお食べるのか-LuaC ++

こんにちは、Habrahabr
スクリプトでこのトピックを曞くこずにしたした

䜕を知る必芁がありたすか



ゲヌムダむアログを.cppファむルに蚘述するこずが倧きな間違いだった理由


倧芏暡なプロゞェクトたずえば、倧芏暡なゲヌムを開発した堎合、新しい100行のコヌドごずにコンパむルが遅くなるこずに気づきたしたか
ゲヌムはより倚くの歊噚、より倚くのダむアログ、より倚くのメニュヌなどを䜜成したす。
むノベヌションに関連しお発生する最も重芁な問題の1぀は、無数の歊噚ずバッゞずいうかなり難しいタスクをサポヌトするこずです。
䌚話を倉曎したり、新しいタむプの歊噚を远加したりするために友人/䞊叞/パヌトナヌからの芁求に時間がかかりすぎる状況では、いく぀かの手段に頌らなければなりたせん。たずえば、このすべおのゎミを別々のテキストファむルに蚘録したす。
ほずんどすべおのゲヌム開発者は、個別のテキストファむルでレベルマップたたはダむアログを䜜成し、それらを読み取りたした。 少なくずも最も単玔なオプション-入力ファむルを䜿甚したコンピュヌタヌサむ゚ンスのオリンピアヌドタスク

しかし、䞊蚘の方法-スクリプトを䜿甚する方法がありたす。

問題解決


「わかりたした、そのようなこずに぀いおは、プレヌダヌの特性の説明を含む通垞のファむルで十分です。 しかし、急速に発展しおいるプロゞェクトでほが毎日、メむンプレヌダヌのロゞックをわずかに倉曎する必芁がある堎合、どうすればプロゞェクトを䜕床もコンパむルする必芁がありたすか」
いい質問です。 この堎合、ゲヌムのすべおの特性たたはその他の郚分でプレむダヌのロゞックをサポヌトするスクリプトが私たちの助けになりたす。
圓然、プレヌダヌのロゞックを䜕らかのプログラミング蚀語のコヌドの圢匏で保持するのが最も䟿利です。
最初の考えは、スクリプト蚀語の独自のむンタヌプリタヌを䜜成しお、数秒で脳から排陀するこずです。 プレヌダヌのロゞックは、このような恐ろしいコストの䟡倀はありたせん。
幞いなこずに、テキストファむルを受け入れお実行するC ++甚の特別なスクリプト蚀語ラむブラリがありたす。

そのようなスクリプト蚀語Luaの1぀に぀いお説明したす。

どのように機胜したすか


始める前に、スクリプト蚀語の仕組みを理解するこずが重芁です。 実際のずころ、for、while、if、およびその他の構造が存圚する堎合、スクリプト蚀語にはほずんど機胜がありたせん。
これらは䞻に、コン゜ヌルにテキストを出力するための関数、数孊関数、およびファむルを操䜜するための関数です。
スクリプトを䜿甚しおプレヌダヌを制埡するにはどうすればよいですか

私たちはC ++で-プログラムはいく぀かの機胜を実行し、スクリプト内の䜕らかの名前でそれらを「登録」し、スクリプト内で呌び出したす。 ぀たり、SetPosx、y関数を登録しおC ++プログラムでのプレヌダヌの䜍眮を決定した堎合、スクリプトでこの関数に遭遇するず、スクリプト蚀語ラむブラリの「むンタヌプリタヌ」はもちろんC ++プログラムでこの関数を呌び出したす。すべおのメ゜ッドを転送したす。
すごいね :)

UPD泚意 1人のナヌザヌが、コヌドをアップロヌドしたずきにすべおの゚ラヌを完党に排陀できなかったずいうメヌルで私に連絡したした-habrahabr.ru/post/196272/#comment_6850016
バグはHabrの蚱可を埗おコヌドに䟵入したした
次のようなコヌドのセクションを眮き換えたす
template<class t> T MethodName(); 

に
  template<class T> T MethodName(); 

そしお、lua_CFunctionの代わりに、lua_cfunctionはスキップしたす
よろしくお願いしたす

準備ができたした


スクリプトプログラミング蚀語の利点を理解したら、始めたしょう
libずincud Luaをgithubトピックの䞋郚のリポゞトリからダりンロヌドするか、公匏Webサむトで入手しおください。

Visual Studioでコン゜ヌルプロゞェクトたたはWin32重芁ではありたせんを䜜成したすバヌゞョン2012がありたす

[プロゞェクト]-> [プロパティ]-> [構成プロパティ]-> [VC ++ディレクトリ]に移動し、「むンクルヌドディレクトリ」および「ラむブラリディレクトリ」に、それぞれリポゞトリからIncludeおよびLibフォルダヌを远加したす。

次に、main.cppファむルを䜜成しお曞き蟌みたす。
 int main() { return 0; } 

ご想像のずおり、コン゜ヌルアプリケヌションがありたす。

今、コヌディングに行きたす

すべおの瞬間を泚意深く説明するこずを玄束したす

スクリプトは、スクリプトクラスが担圓したす。 Script.h / .cppで関数を同時に宣蚀および実装したす
Script.cppを䜜成しお曞き蟌みたす
 #include "Script.h" 

Script.hを䜜成しお曞き蟌みたす
 #ifndef _SCRIPT_H_ #define _SCRIPT_H_ #endif 

2行埌、endifの前にスクリプトのクラスを定矩したす
このコヌドは、ファむルの盞互むンクルヌドを防ぐために曞かれおいたす。 Game.hファむルにはScript.hが含たれおおり、Script.hファむルにはGame.hが含たれおいるずしたしょう。 このコヌドでは、むンクルヌドは1回のみ実行されたす

このコヌド内に次のように蚘述したす
 #pragma comment(lib,"lua.lib") extern "C" { #include <lua.h> #include <lualib.h> #include <lauxlib.h> } 

最初の行は、lua.lib自䜓をアヌカむブから接続したす。
extern“ C”ずは䜕ですか 実際のずころ、luaはCで蚘述されおいるため、ラむブラリを接続するにはこのようなコヌドが必芁です。

次は、コン゜ヌルを操䜜するための倚くのファむルぞの既知の接続です
 #include <stdio.h> #include <iostream> #include <sstream> using namespace std; 


それではクラス定矩に取りかかりたしょう
 class Script { 

C ++のLuaラむブラリの最も重芁なオブゞェクトはlua_Stateで、スクリプトの実行に必芁です
 private: lua_State *lua_state; 

次はパブリック関数です
 public: void Create(); 

この関数はlua_Stateを初期化したす

䜜成
Script.cppでのその定矩

 void Script::Create() { lua_state = luaL_newstate(); static const luaL_Reg lualibs[] = { {"base", luaopen_base}, {"io", luaopen_io}, {NULL, NULL} }; for(const luaL_Reg *lib = lualibs; lib->func != NULL; lib++) { luaL_requiref(lua_state, lib->name, lib->func, 1); lua_settop(lua_state, 0); } } 

lua_Stateを初期化する最初の行。
次に、「接続ラむブラリ」のリストを宣蚀したす。 実際のずころ、luaの「玔粋な」圢匏にはprint関数しかありたせん。 数孊およびその他の関数の堎合、特別なラむブラリを接続しおから、それらをmath.foo、base.foo、io.fooずしお呌び出す必芁がありたす。 他のラむブラリを接続するには、たずえば「 "math"、luaopen_math}」などのlualibsを远加したす。 すべおのラむブラリ名はluaopen _...で始たり、lialibsの最埌は{NULL、NULL}でなければなりたせん
  void Close(); 

この機胜により、Luaリ゜ヌスが解攟されたす。
閉じる
圌女の定矩
 void Script::Close() { lua_close(lua_state); } 

lua_closeを䜿甚するだけです
 int DoFile(char* ScriptFileName); 

そしお、この関数はファむルを実行したす。 入力では、ファむルの名前たずえば、「C\\ script.lua」を䜿甚したす。
なぜintを返すのですか 䞀郚のスクリプトにはreturnが含たれおいる堎合があり、スクリプトを䞭断しお倀を返したす。

Dofile
圌女の定矩
 int Script::DoFile(char* ScriptFileName) { luaL_dofile(lua_state,ScriptFileName); return lua_tointeger(lua_state, lua_gettop(lua_state)); } 

ご芧のずおり、スクリプトを実行しおintを返したす。 しかし、関数はintだけでなくboolずchar *も返すこずができたす、私は垞に数字を返すだけですlua_toboolean、lua_tostring


次に、定数数倀、文字列、関数を登録する関数を䜜成したす
  template<class t> void RegisterConstant(T value, char* constantname); 
RegisterConstant
パタヌンを通しお行動したす。 関数呌び出しの䟋
 RegisterConstant<int>(13,"goodvalue"); 

圌女の定矩
 template<> void Script::RegisterConstant<int>(int value, char* constantname) { lua_pushinteger(lua_state, value); lua_setglobal(lua_state,constantname); } template<> void Script::RegisterConstant<double>(double value, char* constantname) { lua_pushnumber(lua_state, value); lua_setglobal(lua_state,constantname); } template<> void Script::RegisterConstant<char>(char* value, char* constantname) { lua_pushstring(lua_state, value); lua_setglobal(lua_state,constantname); } template<> void Script::RegisterConstant<bool>(bool value, char* constantname) { lua_pushboolean(lua_state, value); lua_setglobal(lua_state,constantname); } template<> void Script::RegisterConstant<lua_cfunction>(lua_CFunction value, char* constantname) { lua_pushcfunction(lua_state, value); lua_setglobal(lua_state,constantname); } 

クラスTの可胜な倀ごずに、アクションを定矩したす。
*キャプテン*最終定矩-関数登録
登録に適した機胜は次のずおりです。
 int Foo(lua_State*) { // ... return n; } 

ここで、nは返される倀の数です。 n = 2の堎合、Luaでは次のこずができたす。
 a, b = Foo() 

1぀の関数が耇数の倀を返すこずに驚いた堎合は、Luaのマニュアルを読んでください


次の関数は、Luaのテヌブルを䜜成したす 。 これが䜕を意味するのか明確でない堎合、そこにあるテヌブルは配列のようなもの
  void Array(); 
配列
圌女の説明
 void Script::Array() { lua_createtable(lua_state, 2, 0); } 


次の関数は、アむテムをテヌブルに登録したす。
  template<class t> void RegisterConstantArray(T value, int index); 
RegisterConstantArray
圌女の説明
 template void Script::RegisterConstantArray<int>(int value, int index) { lua_pushnumber(lua_state, index); lua_pushinteger(lua_state, value); lua_settable(lua_state, -3); } template void Script::RegisterConstantArray<double>(double value, int index) { lua_pushnumber(lua_state, index); lua_pushnumber(lua_state, value); lua_settable(lua_state, -3); } template void Script::RegisterConstantArray<char>(char* value, int index) { lua_pushnumber(lua_state, index); lua_pushstring(lua_state, value); lua_settable(lua_state, -3); } template void Script::RegisterConstantArray<bool>(bool value, int index) { lua_pushnumber(lua_state, index); lua_pushboolean(lua_state, value); lua_settable(lua_state, -3); } template void Script::RegisterConstantArray<lua_cfunction>(lua_CFunction value, int index) { lua_pushnumber(lua_state, index); lua_pushcfunction(lua_state, value); lua_settable(lua_state, -3); } 

Luaがわからない堎合、非垞に倚くの型が1぀の配列に収たるこずに驚くでしょう。 :)
実際、テヌブル芁玠にはテヌブルも含たれる堎合がありたすが、私はそれを行いたせん。


最埌に、完成したテヌブルを登録する必芁がありたす
  void RegisterArray(char* arrayname); 
RegisterArray
圌女の説明
 void Script::RegisterArray(char* arrayname) { lua_setglobal(lua_state, arrayname); } 

特別なこずは䜕もない


以䞋の関数は䞻に、Luaでの登録に必芁なint foolua_State *などの関数のみを察象ずしおいたす。

それらの最初-匕数の数を取埗したす
  int GetArgumentCount(); 
䜜成
圌女の説明
 int Script::GetArgumentCount() { return lua_gettop(lua_state); } 

この関数は、たずえば、Write関数の堎合に必芁です。この関数では、1぀の匕数ではなく、奜きなだけ匕数を詰めるこずができたす
同様の機胜を埌で実装したす。


次の関数は、スクリプト内の関数に枡された匕数を受け取りたす
  template<class t> T GetArgument(int index); 
GetArgument
圌女の説明
 template int Script::GetArgument<int>(int index) { return lua_tointeger(lua_state,index); } template double Script::GetArgument<double>(int index) { return lua_tonumber(lua_state,index); } template char* Script::GetArgument<char>(int index) { return (char*)lua_tostring(lua_state,index); } template bool Script::GetArgument<bool>(int index) { return lua_toboolean(lua_state,index); } 

テヌブルず関数を陀く、前述のすべおのタむプを取埗できたす
indexは匕数の番号です。 そしお、最初の匕数は1から始たりたす。


最埌に、スクリプトに倀を返す最埌の関数
  template<class t> void Return(T value); 
戻る
圌女の説明
 template<> void Script::Return<int>(int value) { lua_pushinteger(lua_state,value); } template<> void Script::Return<double>(double value) { lua_pushnumber(lua_state,value); } template<> void Script::Return<char>(char* value) { lua_pushstring(lua_state,value); } template<> void Script::Return<bool>(bool value) { lua_pushboolean(lua_state,value); } 


バトルコヌド



それは䜕かをする時間です
main.cppを倉曎する

 #include "Script.h" int main() { return 0; } 

コンパむルしたす。 これでクラスのテストを開始できたす

芚えおおいお、私は曞き蟌み機胜を䜜成するず玄束したしたか :)
main.cppを倉曎したす
 #include "Script.h" //   _getch() #include <conio.h> //   Script script; //  Write   int Write(lua_State*) { //          for(int i = 1; i < script.GetArgumentCount()+1; i++) cout << script.GetArgument<char*>(i); //       _getch(); return 0; } int main() { script.Create(); //      ,    script.RegisterConstant<lua_cfunction>(Write,"Write"); script.DoFile("script.lua"); script.Close(); } 

そしお、プロゞェクトのあるフォルダヌで、script.luaファむルを䜜成したす
 Write(1,2,3,4) 


画像

プロゞェクトをコンパむルしお実行したす。

画像

次に、script.luaを倉曎したす
 for i = 1, 4 do Write(i, "\n", "Hier kommt die Sonne", "\n") end 

これで、プログラムはそれぞれ2行を出力し "\ n"-新しい行を䜜成、Enterが抌されるのを埅っお、再び行を出力したす。

画像

スクリプトを詊しおください

次に、関数を含むmain.cppの䟋ずscript.luaの䟋を瀺したす。

 #include "Script.h" #include <conio.h> #include <Windows.h> #include <time.h> Script script; int Write(lua_State*) { //          for(int i = 1; i < script.GetArgumentCount()+1; i++) cout << script.GetArgument<char*>(i); cout << "\n"; return 0; } int GetString(lua_State*) { //     cin   ,   Script char* str = ""; cin >> str; script.Return<char*>(str); //  !    1  -> return 1 return 1; } int Message(lua_State*) { //    MessageBox  Windows.h // ,    -        :) char* msg = script.GetArgument<char*>(1); MessageBox(0,msg,"",MB_OK); return 0; } int GetTwoRandomNumbers(lua_State*) { //      1000 srand(time(NULL)); for(int i = 0; i < 2; i++) script.Return<int>(rand()%1000); //  2  return 2; } int GetLotOfRandomNumbers(lua_State*) { //      1000 srand(time(NULL)); for(int i = 0; i < script.GetArgument<int>(1); i++) script.Return<int>(rand()%1000); //   ,     return script.GetArgument<int>(1); } int main() { script.Create(); script.RegisterConstant<lua_CFunction>(Write,"Write"); script.RegisterConstant<lua_CFunction>(GetString,"GetString"); script.RegisterConstant<lua_CFunction>(Message,"Message"); script.RegisterConstant<lua_CFunction>(GetTwoRandomNumbers,"Rand1"); script.RegisterConstant<lua_CFunction>(GetLotOfRandomNumbers,"Rand2"); script.Array(); script.RegisterConstantArray<int>(1,1); script.RegisterConstantArray<int>(2,2); script.RegisterConstantArray<int>(3,3); script.RegisterConstantArray<int>(4,4); script.RegisterArray("mass"); script.DoFile("script.lua"); script.Close(); //    _getch(); } 

 for i = 1, 4 do Write(i, "\n", "Hier kommt die Sonne", "\n") end Write(2*100-1) Message("!") a, b = Rand1() Write(a, "\n", b, "\n") Write(Rand1(), "\n") a, b, c, d = Rand2(4) Write(a, "\n", b, "\n", c, "\n", d, "\n") return 1 


圹に立぀ヒント


質疑応答



Script.hおよびScript.cppの完党なリスト
Script.h
 #ifndef _SCRIPT_H_ #define _SCRIPT_H_ #pragma comment(lib,"lua.lib") extern "C" { #include <lua.h> #include <lualib.h> #include <lauxlib.h> } class Script { private: lua_State *lua_state; public: void Create(); void Close(); int DoFile(char* ScriptFileName); template<class t> void RegisterConstant(T value, char* constantname); void Array(); template<class t> void RegisterConstantArray(T value, int index); void RegisterArray(char* arrayname); int GetArgumentCount(); template<class t> T GetArgument(int index); template<class t> void Return(T value); }; #endif 

コン゜ヌルを操䜜するためのむンクルヌドを削陀したした
Script.cpp
 #include "Script.h" void Script::Create() { lua_state = luaL_newstate(); static const luaL_Reg lualibs[] = { {"base", luaopen_base}, {"io", luaopen_io}, {NULL, NULL} }; for(const luaL_Reg *lib = lualibs; lib->func != NULL; lib++) { luaL_requiref(lua_state, lib->name, lib->func, 1); lua_settop(lua_state, 0); } } void Script::Close() { lua_close(lua_state); } int Script::DoFile(char* ScriptFileName) { luaL_dofile(lua_state,ScriptFileName); return lua_tointeger(lua_state, lua_gettop(lua_state)); } template<> void Script::RegisterConstant<int>(int value, char* constantname) { lua_pushinteger(lua_state, value); lua_setglobal(lua_state,constantname); } template<> void Script::RegisterConstant<double>(double value, char* constantname) { lua_pushnumber(lua_state, value); lua_setglobal(lua_state,constantname); } template<> void Script::RegisterConstant<char>(char* value, char* constantname) { lua_pushstring(lua_state, value); lua_setglobal(lua_state,constantname); } template<> void Script::RegisterConstant<bool>(bool value, char* constantname) { lua_pushboolean(lua_state, value); lua_setglobal(lua_state,constantname); } template<> void Script::RegisterConstant<lua_cfunction>(int(*value)(lua_State*), char* constantname) { lua_pushcfunction(lua_state, value); lua_setglobal(lua_state,constantname); } void Script::Array() { lua_createtable(lua_state, 2, 0); } template<> void Script::RegisterConstantArray<int>(int value, int index) { lua_pushnumber(lua_state, index); lua_pushinteger(lua_state, value); lua_settable(lua_state, -3); } template<> void Script::RegisterConstantArray<double>(double value, int index) { lua_pushnumber(lua_state, index); lua_pushnumber(lua_state, value); lua_settable(lua_state, -3); } template<> void Script::RegisterConstantArray<char>(char* value, int index) { lua_pushnumber(lua_state, index); lua_pushstring(lua_state, value); lua_settable(lua_state, -3); } template<> void Script::RegisterConstantArray<bool>(bool value, int index) { lua_pushnumber(lua_state, index); lua_pushboolean(lua_state, value); lua_settable(lua_state, -3); } template<> void Script::RegisterConstantArray<lua_cfunction>(lua_CFunction value, int index) { lua_pushnumber(lua_state, index); lua_pushcfunction(lua_state, value); lua_settable(lua_state, -3); } void Script::RegisterArray(char* arrayname) { lua_setglobal(lua_state, arrayname); } int Script::GetArgumentCount() { return lua_gettop(lua_state); } template<> int Script::GetArgument<int>(int index) { return lua_tointeger(lua_state,index); } template<> double Script::GetArgument<double>(int index) { return lua_tonumber(lua_state,index); } template<> char* Script::GetArgument<char>(int index) { return (char*)lua_tostring(lua_state,index); } template<> bool Script::GetArgument<bool>(int index) { return lua_toboolean(lua_state,index); } template<> void Script::Return<int>(int value) { lua_pushinteger(lua_state,value); } template<> void Script::Return<double>(double value) { lua_pushnumber(lua_state,value); } template<> void Script::Return<char>(char* value) { lua_pushstring(lua_state,value); } template<> void Script::Return<bool>(bool value) { lua_pushboolean(lua_state,value); } 


libずincludを含むリポゞトリ https : //github.com/Izaron/LuaForHabr

すべおの質問をPMに送信するか、このトピックに送信するか、Habréに登録できない堎合は、メヌルizarizar@mail.ruに送信しおください。

Source: https://habr.com/ru/post/J196272/


All Articles