なぜLuaはそんなに良いのですか?
ゲームを開発して、自分に尋ねたところ:構成ファイルにはどのデータ形式を使用するのが適切ですか?
オブジェクトを作成するとき、さまざまな初期パラメーターをコード自体ではなく個別のファイルに設定すると便利です。 これにより、再コンパイルせずにオブジェクトの一部のパラメーターを変更でき、実際、プログラミングから遠く離れた人々のためにそれらを変更することが可能になります。
開発者はさまざまな形式を使用します。一部はJSONを使用し、他はXMLまたはその他のデータ形式を使用します。 まあ、いくつかは一般的にデータを.txtファイルに保存するか、パーサーを記述します。 さまざまな形式を確認した後、Luaに決めました。
Luaはゲームだけでなく、一般に他のファイルに保存されたデータを使用するプログラムにも使用できます。
Luaを他の形式と区別する理由は次のとおりです。
- Luaは追加の依存関係なしで簡単に使用できます(1つのLuaライブラリと3つの.hファイルを除く)。
- Luaファイルでは、Luaで記述された数式または関数を使用してデータを初期化できます。 例:
some_variable = math.sqrt(2) * 2 some_variable2 = 64 * 16 - 32
- Luaは非常に高速な言語であり、多くのメモリを消費しません。
- LuaにはMITライセンスがあり、無料および商用プロジェクトの両方でこの言語を使用できます。 サイトに書かれているように:「ただダウンロードして使用してください。」
- Luaはほぼどこでもコンパイルします 追加のライブラリを使用せずに純粋なCで書かれています。
- データは快適な方法で保存およびソートできます。 テキストエディターで簡単に読み、変更できます。
簡単な例から始めましょう。次に、クラスの実装に進みます。
例
Player.luaファイルがあるとしましょう
player = { pos = { X = 20, Y = 30, }, filename = "res/images/player.png", HP = 20, -- }
単純なクラスを使用すると、次のようにデータを取得できます。
LuaScript script("player.lua"); std::string filename = script.get<std::string>("player.filename"); int posX = script.get<std::string>("player.pos.X");
コードが理解しやすいように注意してください。Luaスタックの動作に関する情報を読んで、最も簡単な例を見ることをお勧めします。
ここで読むことができます 。
クラスを作成することから始めましょう:
#ifndef LUASCRIPT_H #define LUASCRIPT_H #include <string> #include <vector> #include <iostream> // Lua C, , C extern "C" { # include "lua.h" # include "lauxlib.h" # include "lualib.h" } class LuaScript { public: LuaScript(const std::string& filename); ~LuaScript(); void printError(const std::string& variableName, const std::string& reason); template<typename T> T get(const std::string& variableName) { // } bool lua_gettostack(const std::string& variableName) { // . - true // } // 0 template<typename T> T lua_get(const std::string& variableName) { return 0; } // , - // template<typename T> T lua_getdefault(const std::string& variableName) { return 0; } private: lua_State* L; int level; // . lug_gettostack }; #endif
コンストラクター:
LuaScript::LuaScript(const std::string& filename) { L = luaL_newstate(); if (luaL_loadfile(L, filename.c_str()) || lua_pcall(L, 0, 0, 0)) { std::cout<<"Error: script not loaded ("<<filename<<")"<<std::endl; L = 0; } }
。
lua_Stateを作成します。ファイルが見つからなかった場合、またはその他のエラーが発生した場合、これに関するメッセージを表示します。
デストラクタ:
LuaScript::~LuaScript() { if(L) lua_close(L); }
printErrorメソッドは、エラーメッセージを表示するように設計されています。
void LuaScript::printError(const std::string& variableName, const std::string& reason) { std::cout<<"Error: can't get ["<<variableName<<"]. "<<reason<<std::endl; }
lua_getdefaultは 、エラーが発生した場合にnull値を返すために使用されます。 そして、数値に対してゼロを返すことができる場合、たとえば文字列に対しては、これは機能しません。そのため、テンプレートを特殊化します(このコードはヘッダーにあります)。
template<> inline std::string LuaScript::lua_getdefault<std::string>() { return "null"; }
そして、テンプレート関数getを書きます 。
まず、変数をスタックの先頭に置くlua_gettostack関数を作成します
例を使用してアルゴリズムを分析しましょう。 Player.luaファイルから変数「player.pos.X」を取得しましょう
読み取り文字を変数「var」に追加しながら、最初のポイントまでループします。
「Player」はグローバルなテーブルなので、 lua_getglobalを使用して取得します。
「Pos」と「X」はすでにグローバルではないデータですが、 lua_getfieldを使用して取得できます。 プレーヤーテーブル自体はスタックの一番上にあります。
次に、テンプレート関数getで 、データ型に固有の関数が実行され、スタックがクリアされて目的の値が返されます。エラーが発生した場合は、 lua_getdefault関数が呼び出されます。
bool lua_gettostack(const std::string& variableName) { level = 0; std::string var = ""; for(unsigned int i = 0; i < variableName.size(); i++) { if(variableName.at(i) == '.') { if(level == 0) { lua_getglobal(L, var.c_str()); } else { lua_getfield(L, -1, var.c_str()); } if(lua_isnil(L, -1)) { printError(variableName, var + " is not defined"); return false; } else { var = ""; level++; } } else { var += variableName.at(i); } } if(level == 0) { lua_getglobal(L, var.c_str()); } else { lua_getfield(L, -1, var.c_str()); } if(lua_isnil(L, -1)) { printError(variableName, var + " is not defined"); return false; } // , true return true; }
getメソッドに戻ります 。
template <typename T> T get(const std::string& variableName) { if(!L) { printError(variableName, "Script is not loaded"); return lua_getdefault<T>(); } T result; if(lua_gettostack(variableName)) { // , result = lua_get<T>(variableName); } else { result = lua_getdefault<T>(); } lua_pop(L, level + 1); // return result; } }
テンプレートの特殊化を追加するためにのみ残ります(一部のデータ型の例):
template <> inline bool LuaScript::lua_get<bool>(const std::string& variableName) { return (bool)lua_toboolean(L, -1); } template <> inline float LuaScript::lua_get<float>(const std::string& variableName) { if(!lua_isnumber(L, -1)) { printError(variableName, "Not a number"); } return (float)lua_tonumber(L, -1); } template <> inline int LuaScript::lua_get<int>(const std::string& variableName) { if(!lua_isnumber(L, -1)) { printError(variableName, "Not a number"); } return (int)lua_tonumber(L, -1); } template <> inline std::string LuaScript::lua_get<std::string>(const std::string& variableName) { std::string s = "null"; if(lua_isstring(L, -1)) { s = std::string(lua_tostring(L, -1)); } else { printError(variableName, "Not a string"); } return s; }
それだけです 記事のすべてのコードがここにあることを思い出してください。 また、クラスを使用する例もあります。
次は?
Luaにはさらに多くの機能があり、近いうちに記事の後半で説明します。 たとえば、無制限の長さのデータの配列を取得し、テーブルキーのリストを取得します(例のPlayerテーブルの場合、["pos"、 "filename"、 "HP"]のようになります)
また、C ++からLua関数を呼び出すことができるように、LuaからC ++関数を呼び出すこともできます。これについては、第3部で説明します。
良いスクリプトを作成してください!