Browse Source

Update: README

AnnulusGames 1 year ago
parent
commit
fce59a9675
2 changed files with 292 additions and 2 deletions
  1. 146 1
      README.md
  2. 146 1
      README_JA.md

+ 146 - 1
README.md

@@ -233,7 +233,12 @@ Console.WriteLine(results[i]);
 return add(1, 2)
 return add(1, 2)
 ```
 ```
 
 
-Additionally, `LuaFunction` operates asynchronously. Therefore, you can define a function that waits for an operation in Lua, such as the example below:
+> [!TIP]  
+> Defining functions with `LuaFunction` can be somewhat verbose. When adding multiple functions, it is recommended to use the Source Generator with the `[LuaObject]` attribute. For more details, see the [LuaObject](#luaobject) section.
+
+## Integration with async/await
+
+`LuaFunction` operates asynchronously. Therefore, you can define a function that waits for an operation in Lua, such as the example below:
 
 
 ```cs
 ```cs
 // Define a function that waits for the given number of seconds using Task.Delay
 // Define a function that waits for the given number of seconds using Task.Delay
@@ -298,6 +303,146 @@ for (int i = 0; i < 10; i++)
 }
 }
 ```
 ```
 
 
+## LuaObject
+
+By applying the `[LuaObject]` attribute, you can create custom classes that run within Lua. Adding this attribute to a class that you wish to use in Lua allows the Source Generator to automatically generate the code required for interaction from Lua.
+
+The following is an example implementation of a wrapper class for `System.Numerics.Vector3` that can be used in Lua:
+
+```cs
+using System.Numerics;
+using Lua;
+
+var state = LuaState.Create();
+
+// Add an instance of the defined LuaObject as a global variable
+// (Implicit conversion to LuaValue is automatically defined for classes with the LuaObject attribute)
+state.Environment["Vector3"] = new LuaVector3();
+
+await state.DoFileAsync("vector3_sample.lua");
+
+// Add LuaObject attribute and partial keyword
+[LuaObject]
+public partial class LuaVector3
+{
+    Vector3 vector;
+
+    // Add LuaMember attribute to members that will be used in Lua
+    // The argument specifies the name used in Lua (if omitted, the member name is used)
+    [LuaMember("x")]
+    public float X
+    {
+        get => vector.X;
+        set => vector = vector with { X = value };
+    }
+
+    [LuaMember("y")]
+    public float Y
+    {
+        get => vector.Y;
+        set => vector = vector with { Y = value };
+    }
+
+    [LuaMember("z")]
+    public float Z
+    {
+        get => vector.Z;
+        set => vector = vector with { Z = value };
+    }
+
+    // Static methods are treated as regular Lua functions
+    [LuaMember("create")]
+    public static LuaVector3 Create(float x, float y, float z)
+    {
+        return new LuaVector3()
+        {
+            vector = new Vector3(x, y, z)
+        };
+    }
+
+    // Instance methods implicitly receive the instance (this) as the first argument
+    // In Lua, this is accessed with instance:method() syntax
+    [LuaMember("normalized")]
+    public LuaVector3 Normalized()
+    {
+        return new LuaVector3()
+        {
+            vector = Vector3.Normalize(vector)
+        };
+    }
+}
+```
+
+```lua
+-- vector3_sample.lua
+
+local v1 = Vector3.create(1, 2, 3)
+-- 1  2  3
+print(v1.x, v1.y, v1.z)
+
+local v2 = v1:normalized()
+-- 0.26726123690605164  0.5345224738121033  0.8017836809158325
+print(v2.x, v2.y, v2.z)
+```
+
+The types of fields/properties with the `[LuaMember]` attribute, as well as the argument and return types of methods, must be either `LuaValue` or convertible to/from `LuaValue`.
+
+Return types such as `void`, `Task/Task<T>`, `ValueTask/ValueTask<T>`, `UniTask/UniTask<T>`, and `Awaitable/Awaitable<T>` are also supported.
+
+If the type is not supported, the Source Generator will output a compile-time error.
+
+### LuaMetamethod
+
+By adding the `[LuaMetamethod]` attribute, you can designate a C# method to be used as a Lua metamethod.
+
+Here is an example that adds the `__add`, `__sub`, and `__tostring` metamethods to the `LuaVector3` class:
+
+```cs
+[LuaObject]
+public partial class LuaVector3
+{
+    // The previous implementation is omitted
+
+    [LuaMetamethod(LuaObjectMetamethod.Add)]
+    public static LuaVector3 Add(LuaVector3 a, LuaVector3 b)
+    {
+        return new LuaVector3()
+        {
+            vector = a.vector + b.vector
+        };
+    }
+    
+    [LuaMetamethod(LuaObjectMetamethod.Sub)]
+    public static LuaVector3 Sub(LuaVector3 a, LuaVector3 b)
+    {
+        return new LuaVector3()
+        {
+            vector = a.vector - b.vector
+        };
+    }
+
+    [LuaMetamethod(LuaObjectMetamethod.ToString)]
+    public override string ToString()
+    {
+        return vector.ToString();
+    }
+}
+```
+
+```lua
+local v1 = Vector3.create(1, 1, 1)
+local v2 = Vector3.create(2, 2, 2)
+
+print(v1) -- <1, 1, 1>
+print(v2) -- <2, 2, 2>
+
+print(v1 + v2) -- <3, 3, 3>
+print(v1 - v2) -- <-1, -1, -1>
+```
+
+> [!NOTE]  
+> `__index` and `__newindex` cannot be set as they are used internally by the code generated by `[LuaObject]`.
+
 ## Module Loading
 ## Module Loading
 
 
 In Lua, you can load modules using the `require` function. In regular Lua, modules are managed by searchers within the `package.searchers` function list. In Lua-CSharp, this is replaced by the `ILuaModuleLoader` interface.
 In Lua, you can load modules using the `require` function. In regular Lua, modules are managed by searchers within the `package.searchers` function list. In Lua-CSharp, this is replaced by the `ILuaModuleLoader` interface.

+ 146 - 1
README_JA.md

@@ -233,7 +233,12 @@ Console.WriteLine(results[i]);
 return add(1, 2)
 return add(1, 2)
 ```
 ```
 
 
-また、`LuaFunction`は非同期メソッドとして動作します。そのため、以下のような関数を定義することでLua側から処理の待機を行うことも可能です。
+> [!TIP]
+> `LuaFunction`による関数の定義はやや記述量が多いため、関数をまとめて追加する際には`[LuaObject]`属性によるSource Generatorの使用を推奨します。詳細は[LuaObject](#luaobject)の項目を参照してください。
+
+## async/awaitとの統合
+
+`LuaFunction`は非同期メソッドとして動作します。そのため、以下のような関数を定義することでLua側から処理の待機を行うことも可能です。
 
 
 ```cs
 ```cs
 // 与えられた秒数だけTask.Delayで待機する関数を定義
 // 与えられた秒数だけTask.Delayで待機する関数を定義
@@ -298,6 +303,146 @@ for (int i = 0; i < 10; i++)
 }
 }
 ```
 ```
 
 
+## LuaObject
+
+`[LuaObject]`属性を用いることで、Lua上で動作する独自のクラスを作成することが可能になります。Luaで使いたいクラスにこの属性を付加することで、Source GeneratorがLua側から利用されるコードを自動生成します。
+
+以下のサンプルコードはLuaで動作する`System.Numerics.Vector3`のラッパークラスの実装例です。
+
+```cs
+using System.Numerics;
+using Lua;
+
+var state = LuaState.Create();
+
+// 定義したLuaObjectのインスタンスをグローバル変数として追加する
+// (LuaObjectを追加したクラスにはLuaValueへの暗黙的変換が自動で定義される)
+state.Environment["Vector3"] = new LuaVector3();
+
+await state.DoFileAsync("vector3_sample.lua");
+
+// LuaObject属性とpartialキーワードを追加
+[LuaObject]
+public partial class LuaVector3
+{
+    Vector3 vector;
+
+    // Lua側で使用されるメンバーにLuaMember属性を付加
+    // 引数にはLuaでの名前を指定 (省略した場合はメンバーの名前が使用される)
+    [LuaMember("x")]
+    public float X
+    {
+        get => vector.X;
+        set => vector = vector with { X = value };
+    }
+
+    [LuaMember("y")]
+    public float Y
+    {
+        get => vector.Y;
+        set => vector = vector with { Y = value };
+    }
+
+    [LuaMember("z")]
+    public float Z
+    {
+        get => vector.Z;
+        set => vector = vector with { Z = value };
+    }
+
+    // staticメソッドの場合は通常のLua関数として解釈される
+    [LuaMember("create")]
+    public static LuaVector3 Create(float x, float y, float z)
+    {
+        return new LuaVector3()
+        {
+            vector = new Vector3(x, y, z)
+        };
+    }
+
+    // インスタンスメソッドの場合は暗黙的に自身のインスタンス(this)が1番目の引数として追加される
+    // これはLuaではinstance:method()のような表記でアクセスできる
+    [LuaMember("normalized")]
+    public LuaVector3 Normalized()
+    {
+        return new LuaVector3()
+        {
+            vector = Vector3.Normalize(vector)
+        };
+    }
+}
+```
+
+```lua
+-- vector3_sample.lua
+
+local v1 = Vector3.create(1, 2, 3)
+-- 1  2  3
+print(v1.x, v1.y, v1.z)
+
+local v2 = v1:normalized()
+-- 0.26726123690605164  0.5345224738121033  0.8017836809158325
+print(v2.x, v2.y, v2.z)
+```
+
+`[LuaMember]`を付加するフィールド/プロパティの型、またはメソッドの引数や戻り値の型は`LuaValue`またはそれに変換が可能である必要があります。
+
+ただし、戻り値には`void`, `Task/Task<T>`, `ValueTask/ValueTask<T>`, `UniTask/UniTask<T>`, `Awaitable/Awaitable<T>`を利用することも可能です。
+
+利用可能でない型に対してはSource Generatorがコンパイルエラーを出力します。
+
+### LuaMetamethod
+
+`[LuaMetamethod]`属性を追加することで、C#のメソッドをLua側で使用されるメタメソッドとして設定することが可能です。
+
+例として、先ほどの`LuaVector3`クラスに`__add`, `__sub`, `__tostring`のメタメソッドを追加したコードを示します。
+
+```cs
+[LuaObject]
+public partial class LuaVector3
+{
+    // 上のコードで書かれた実装は省略
+
+    [LuaMetamethod(LuaObjectMetamethod.Add)]
+    public static LuaVector3 Add(LuaVector3 a, LuaVector3 b)
+    {
+        return new LuaVector3()
+        {
+            vector = a.vector + b.vector
+        };
+    }
+    
+    [LuaMetamethod(LuaObjectMetamethod.Sub)]
+    public static LuaVector3 Sub(LuaVector3 a, LuaVector3 b)
+    {
+        return new LuaVector3()
+        {
+            vector = a.vector - b.vector
+        };
+    }
+
+    [LuaMetamethod(LuaObjectMetamethod.ToString)]
+    public override string ToString()
+    {
+        return vector.ToString();
+    }
+}
+```
+
+```lua
+local v1 = Vector3.create(1, 1, 1)
+local v2 = Vector3.create(2, 2, 2)
+
+print(v1) -- <1, 1, 1>
+print(v2) -- <2, 2, 2>
+
+print(v1 + v2) -- <3, 3, 3>
+print(v1 - v2) -- <-1, -1, -1>
+```
+
+> [!NOTE]
+> `__index`, `__newindex`は`[LuaObject]`の生成コードに利用されるため、設定することはできません。
+
 ## モジュールの読み込み
 ## モジュールの読み込み
 
 
 Luaでは`require`関数を用いてモジュールを読み込むことができます。通常のLuaでは`package.searchers`の検索関数を用いてモジュールの管理を行いますが、Lua-CSharpでは代わりに`ILuaModuleLoader`がモジュール読み込みの機構として提供されています。
 Luaでは`require`関数を用いてモジュールを読み込むことができます。通常のLuaでは`package.searchers`の検索関数を用いてモジュールの管理を行いますが、Lua-CSharpでは代わりに`ILuaModuleLoader`がモジュール読み込みの機構として提供されています。