Skip to content

Commit fce59a9

Browse files
committed
Update: README
1 parent edf2ff8 commit fce59a9

File tree

2 files changed

+292
-2
lines changed

2 files changed

+292
-2
lines changed

README.md

Lines changed: 146 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,12 @@ Console.WriteLine(results[i]);
233233
return add(1, 2)
234234
```
235235

236-
Additionally, `LuaFunction` operates asynchronously. Therefore, you can define a function that waits for an operation in Lua, such as the example below:
236+
> [!TIP]
237+
> 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.
238+
239+
## Integration with async/await
240+
241+
`LuaFunction` operates asynchronously. Therefore, you can define a function that waits for an operation in Lua, such as the example below:
237242

238243
```cs
239244
// 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++)
298303
}
299304
```
300305

306+
## LuaObject
307+
308+
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.
309+
310+
The following is an example implementation of a wrapper class for `System.Numerics.Vector3` that can be used in Lua:
311+
312+
```cs
313+
using System.Numerics;
314+
using Lua;
315+
316+
var state = LuaState.Create();
317+
318+
// Add an instance of the defined LuaObject as a global variable
319+
// (Implicit conversion to LuaValue is automatically defined for classes with the LuaObject attribute)
320+
state.Environment["Vector3"] = new LuaVector3();
321+
322+
await state.DoFileAsync("vector3_sample.lua");
323+
324+
// Add LuaObject attribute and partial keyword
325+
[LuaObject]
326+
public partial class LuaVector3
327+
{
328+
Vector3 vector;
329+
330+
// Add LuaMember attribute to members that will be used in Lua
331+
// The argument specifies the name used in Lua (if omitted, the member name is used)
332+
[LuaMember("x")]
333+
public float X
334+
{
335+
get => vector.X;
336+
set => vector = vector with { X = value };
337+
}
338+
339+
[LuaMember("y")]
340+
public float Y
341+
{
342+
get => vector.Y;
343+
set => vector = vector with { Y = value };
344+
}
345+
346+
[LuaMember("z")]
347+
public float Z
348+
{
349+
get => vector.Z;
350+
set => vector = vector with { Z = value };
351+
}
352+
353+
// Static methods are treated as regular Lua functions
354+
[LuaMember("create")]
355+
public static LuaVector3 Create(float x, float y, float z)
356+
{
357+
return new LuaVector3()
358+
{
359+
vector = new Vector3(x, y, z)
360+
};
361+
}
362+
363+
// Instance methods implicitly receive the instance (this) as the first argument
364+
// In Lua, this is accessed with instance:method() syntax
365+
[LuaMember("normalized")]
366+
public LuaVector3 Normalized()
367+
{
368+
return new LuaVector3()
369+
{
370+
vector = Vector3.Normalize(vector)
371+
};
372+
}
373+
}
374+
```
375+
376+
```lua
377+
-- vector3_sample.lua
378+
379+
local v1 = Vector3.create(1, 2, 3)
380+
-- 1 2 3
381+
print(v1.x, v1.y, v1.z)
382+
383+
local v2 = v1:normalized()
384+
-- 0.26726123690605164 0.5345224738121033 0.8017836809158325
385+
print(v2.x, v2.y, v2.z)
386+
```
387+
388+
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`.
389+
390+
Return types such as `void`, `Task/Task<T>`, `ValueTask/ValueTask<T>`, `UniTask/UniTask<T>`, and `Awaitable/Awaitable<T>` are also supported.
391+
392+
If the type is not supported, the Source Generator will output a compile-time error.
393+
394+
### LuaMetamethod
395+
396+
By adding the `[LuaMetamethod]` attribute, you can designate a C# method to be used as a Lua metamethod.
397+
398+
Here is an example that adds the `__add`, `__sub`, and `__tostring` metamethods to the `LuaVector3` class:
399+
400+
```cs
401+
[LuaObject]
402+
public partial class LuaVector3
403+
{
404+
// The previous implementation is omitted
405+
406+
[LuaMetamethod(LuaObjectMetamethod.Add)]
407+
public static LuaVector3 Add(LuaVector3 a, LuaVector3 b)
408+
{
409+
return new LuaVector3()
410+
{
411+
vector = a.vector + b.vector
412+
};
413+
}
414+
415+
[LuaMetamethod(LuaObjectMetamethod.Sub)]
416+
public static LuaVector3 Sub(LuaVector3 a, LuaVector3 b)
417+
{
418+
return new LuaVector3()
419+
{
420+
vector = a.vector - b.vector
421+
};
422+
}
423+
424+
[LuaMetamethod(LuaObjectMetamethod.ToString)]
425+
public override string ToString()
426+
{
427+
return vector.ToString();
428+
}
429+
}
430+
```
431+
432+
```lua
433+
local v1 = Vector3.create(1, 1, 1)
434+
local v2 = Vector3.create(2, 2, 2)
435+
436+
print(v1) -- <1, 1, 1>
437+
print(v2) -- <2, 2, 2>
438+
439+
print(v1 + v2) -- <3, 3, 3>
440+
print(v1 - v2) -- <-1, -1, -1>
441+
```
442+
443+
> [!NOTE]
444+
> `__index` and `__newindex` cannot be set as they are used internally by the code generated by `[LuaObject]`.
445+
301446
## Module Loading
302447

303448
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.

README_JA.md

Lines changed: 146 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,12 @@ Console.WriteLine(results[i]);
233233
return add(1, 2)
234234
```
235235

236-
また、`LuaFunction`は非同期メソッドとして動作します。そのため、以下のような関数を定義することでLua側から処理の待機を行うことも可能です。
236+
> [!TIP]
237+
> `LuaFunction`による関数の定義はやや記述量が多いため、関数をまとめて追加する際には`[LuaObject]`属性によるSource Generatorの使用を推奨します。詳細は[LuaObject](#luaobject)の項目を参照してください。
238+
239+
## async/awaitとの統合
240+
241+
`LuaFunction`は非同期メソッドとして動作します。そのため、以下のような関数を定義することでLua側から処理の待機を行うことも可能です。
237242

238243
```cs
239244
// 与えられた秒数だけTask.Delayで待機する関数を定義
@@ -298,6 +303,146 @@ for (int i = 0; i < 10; i++)
298303
}
299304
```
300305

306+
## LuaObject
307+
308+
`[LuaObject]`属性を用いることで、Lua上で動作する独自のクラスを作成することが可能になります。Luaで使いたいクラスにこの属性を付加することで、Source GeneratorがLua側から利用されるコードを自動生成します。
309+
310+
以下のサンプルコードはLuaで動作する`System.Numerics.Vector3`のラッパークラスの実装例です。
311+
312+
```cs
313+
using System.Numerics;
314+
using Lua;
315+
316+
var state = LuaState.Create();
317+
318+
// 定義したLuaObjectのインスタンスをグローバル変数として追加する
319+
// (LuaObjectを追加したクラスにはLuaValueへの暗黙的変換が自動で定義される)
320+
state.Environment["Vector3"] = new LuaVector3();
321+
322+
await state.DoFileAsync("vector3_sample.lua");
323+
324+
// LuaObject属性とpartialキーワードを追加
325+
[LuaObject]
326+
public partial class LuaVector3
327+
{
328+
Vector3 vector;
329+
330+
// Lua側で使用されるメンバーにLuaMember属性を付加
331+
// 引数にはLuaでの名前を指定 (省略した場合はメンバーの名前が使用される)
332+
[LuaMember("x")]
333+
public float X
334+
{
335+
get => vector.X;
336+
set => vector = vector with { X = value };
337+
}
338+
339+
[LuaMember("y")]
340+
public float Y
341+
{
342+
get => vector.Y;
343+
set => vector = vector with { Y = value };
344+
}
345+
346+
[LuaMember("z")]
347+
public float Z
348+
{
349+
get => vector.Z;
350+
set => vector = vector with { Z = value };
351+
}
352+
353+
// staticメソッドの場合は通常のLua関数として解釈される
354+
[LuaMember("create")]
355+
public static LuaVector3 Create(float x, float y, float z)
356+
{
357+
return new LuaVector3()
358+
{
359+
vector = new Vector3(x, y, z)
360+
};
361+
}
362+
363+
// インスタンスメソッドの場合は暗黙的に自身のインスタンス(this)が1番目の引数として追加される
364+
// これはLuaではinstance:method()のような表記でアクセスできる
365+
[LuaMember("normalized")]
366+
public LuaVector3 Normalized()
367+
{
368+
return new LuaVector3()
369+
{
370+
vector = Vector3.Normalize(vector)
371+
};
372+
}
373+
}
374+
```
375+
376+
```lua
377+
-- vector3_sample.lua
378+
379+
local v1 = Vector3.create(1, 2, 3)
380+
-- 1 2 3
381+
print(v1.x, v1.y, v1.z)
382+
383+
local v2 = v1:normalized()
384+
-- 0.26726123690605164 0.5345224738121033 0.8017836809158325
385+
print(v2.x, v2.y, v2.z)
386+
```
387+
388+
`[LuaMember]`を付加するフィールド/プロパティの型、またはメソッドの引数や戻り値の型は`LuaValue`またはそれに変換が可能である必要があります。
389+
390+
ただし、戻り値には`void`, `Task/Task<T>`, `ValueTask/ValueTask<T>`, `UniTask/UniTask<T>`, `Awaitable/Awaitable<T>`を利用することも可能です。
391+
392+
利用可能でない型に対してはSource Generatorがコンパイルエラーを出力します。
393+
394+
### LuaMetamethod
395+
396+
`[LuaMetamethod]`属性を追加することで、C#のメソッドをLua側で使用されるメタメソッドとして設定することが可能です。
397+
398+
例として、先ほどの`LuaVector3`クラスに`__add`, `__sub`, `__tostring`のメタメソッドを追加したコードを示します。
399+
400+
```cs
401+
[LuaObject]
402+
public partial class LuaVector3
403+
{
404+
// 上のコードで書かれた実装は省略
405+
406+
[LuaMetamethod(LuaObjectMetamethod.Add)]
407+
public static LuaVector3 Add(LuaVector3 a, LuaVector3 b)
408+
{
409+
return new LuaVector3()
410+
{
411+
vector = a.vector + b.vector
412+
};
413+
}
414+
415+
[LuaMetamethod(LuaObjectMetamethod.Sub)]
416+
public static LuaVector3 Sub(LuaVector3 a, LuaVector3 b)
417+
{
418+
return new LuaVector3()
419+
{
420+
vector = a.vector - b.vector
421+
};
422+
}
423+
424+
[LuaMetamethod(LuaObjectMetamethod.ToString)]
425+
public override string ToString()
426+
{
427+
return vector.ToString();
428+
}
429+
}
430+
```
431+
432+
```lua
433+
local v1 = Vector3.create(1, 1, 1)
434+
local v2 = Vector3.create(2, 2, 2)
435+
436+
print(v1) -- <1, 1, 1>
437+
print(v2) -- <2, 2, 2>
438+
439+
print(v1 + v2) -- <3, 3, 3>
440+
print(v1 - v2) -- <-1, -1, -1>
441+
```
442+
443+
> [!NOTE]
444+
> `__index`, `__newindex``[LuaObject]`の生成コードに利用されるため、設定することはできません。
445+
301446
## モジュールの読み込み
302447

303448
Luaでは`require`関数を用いてモジュールを読み込むことができます。通常のLuaでは`package.searchers`の検索関数を用いてモジュールの管理を行いますが、Lua-CSharpでは代わりに`ILuaModuleLoader`がモジュール読み込みの機構として提供されています。

0 commit comments

Comments
 (0)