From 160e087f7b5fa424876ec2ba67bd66335711565a Mon Sep 17 00:00:00 2001 From: Brandonn Date: Wed, 23 Jul 2025 01:52:47 +0400 Subject: [PATCH 1/4] Update README.md with basic signal example --- README.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9431f3b..e223264 100644 --- a/README.md +++ b/README.md @@ -201,8 +201,20 @@ lua.do_string(""" print(v:pcall('length')) -- true 2.2360680103302 print(v:pcall('invalid method')) -- false "Invalid method" ``` +## Signal +- Use signals with `Callable`. + ```lua + function myClass:_on_timeout() + print("timeout end") + end + + function myClass:_ready() + local timer = self:get_tree():create_timer(2.0) + --another way is : timer:connect("timeout", Callable(self, "_on_timeout")) + timer.timeout:connect(Callable(self, "_on_timeout")) + end - + ``` ## TODO - [X] Bind Variant types to Lua - [X] Bind utility functions to Lua From d0334e596622a334e6570eafd981a8c61bc40001 Mon Sep 17 00:00:00 2001 From: Brandonn Date: Wed, 23 Jul 2025 02:02:46 +0400 Subject: [PATCH 2/4] Create EN_GUIDE.md --- documentation/EN_GUIDE.md | 424 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 424 insertions(+) create mode 100644 documentation/EN_GUIDE.md diff --git a/documentation/EN_GUIDE.md b/documentation/EN_GUIDE.md new file mode 100644 index 0000000..69afd76 --- /dev/null +++ b/documentation/EN_GUIDE.md @@ -0,0 +1,424 @@ +# Complete Guide: Using Lua with Godot via **lua-gdextension** + +> A hands-on doc, based on the extension’s source code, covering the common Godot areas you’ll touch with Lua: installation, syntax, nodes/scenes, signals, properties, resources, coroutines, threads, UI, physics, audio, networking, editor tooling, etc. Every section includes Lua examples. + +--- + +## Table of Contents +1. [Installation & Setup](#installation--setup) +2. [First Steps: Lua Script Structure](#first-steps-lua-script-structure) +3. [Node Lifecycle](#node-lifecycle) +4. [Properties, Methods & Variant Mapping](#properties-methods--variant-mapping) +5. [Signals](#signals) +6. [Scenes & Instantiation](#scenes--instantiation) +7. [Resources (Textures, AudioStream, Scenes, Scripts…)](#resources-textures-audiostream-scenes-scripts) +8. [User Input (Input, Actions, Events)](#user-input-input-actions-events) +9. [2D / 3D Physics](#2d--3d-physics) +10. [UI (Control, Themes, UI signals)](#ui-control-themes-ui-signals) +11. [Audio](#audio) +12. [Animation (AnimationPlayer, Tween)](#animation-animationplayer-tween) +13. [Lua Coroutines & Timers](#lua-coroutines--timers) +14. [Lua Threads & Godot Async Tasks](#lua-threads--godot-async-tasks) +15. [Networking (High-Level Multiplayer API)](#networking-high-level-multiplayer-api) +16. [Debugging & Lua Errors](#debugging--lua-errors) +17. [Advanced Interop (Callable, RefCounted, Userdata)](#advanced-interop-callable-refcounted-userdata) +18. [Editor: tools, plugins, REPL](#editor-tools-plugins-repl) +19. [Best Practices & Common Pitfalls](#best-practices--common-pitfalls) +20. [Quick FAQ](#quick-faq) + +--- + +## Installation & Setup + +1. **Install the extension** + - Copy `addons/lua-gdextension` into your Godot project. + - Ensure `lua_gdextension.gdextension` sits in `addons/lua-gdextension/`. + - Enable the plugin in **Project > Project Settings > Plugins**. + +2. **Lua scripts** + - Put your Lua files anywhere (e.g., `res://scripts/my_script.lua`). + - Typical template: + ```lua + local MyNode = { + extends = Node2D, + } + + function MyNode:_ready() + print("Hello from Lua!") + end + + return MyNode + ``` + +3. **Lua REPL** + - Use `addons/lua-gdextension/lua_repl.tscn` to run Lua code live. + +--- + +## First Steps: Lua Script Structure + +```lua +local Foo = { + extends = Node, -- Godot class you extend + class_name = "Foo", -- (optional) +} + +Foo.my_value = 42 + +function Foo:_ready() + print("_ready called", self.my_value) +end + +function Foo:_process(delta) + -- per-frame logic +end + +function Foo:bar(x) + return x * 2 +end + +return Foo +``` + +**Notes:** +- `extends` must point to a Godot class. +- `self` is the actual Godot instance. +- Special callbacks (`_ready`, `_process`, `_physics_process`, etc.) are auto-wired. + +--- + +## Node Lifecycle + +Common callbacks: `_enter_tree`, `_ready`, `_process(delta)`, `_physics_process(delta)`, `_exit_tree`, `_notification(what)`. + +```lua +local Player = { extends = CharacterBody2D } + +function Player:_enter_tree() + print("In tree") +end + +function Player:_ready() + self.speed = 200 +end + +function Player:_physics_process(delta) + local dir = Vector2.ZERO + if Input.is_action_pressed("ui_right") then dir.x = 1 end + if Input.is_action_pressed("ui_left") then dir.x = -1 end + self.velocity = dir * self.speed + self:move_and_slide() +end + +return Player +``` + +--- + +## Properties, Methods & Variant Mapping + +| Godot Variant | Lua | Notes | +|--------------------------|----------------------------------|------------------------------------| +| `bool` | boolean | | +| `int`, `float` | number | | +| `String` | string | | +| `Vector2/3/4`, `Color`… | userdata (objects) | Properties/methods accessible | +| `Array` | sequential Lua table | 1-based indexing in Lua | +| `Dictionary` | key/value Lua table | | +| `Callable` | Lua function or `Callable(...)` | See Signals | +| `Object`/`Node` | userdata | | + +```lua +self.position = Vector2(100, 50) +print(self.position.x) + +local node = self:get_node("Camera2D") +node:make_current() + +local col = Color(1, 0.5, 0.2, 1) +local arr = {1, 2, 3} +local dict = { hp = 100, name = "Bob" } +``` + +--- + +## Signals + +### Connect (Godot 4 style) +```lua +local timer = Timer.new() +self:add_child(timer) + +function self:_on_timeout() + print("Timer finished") +end + +timer.timeout:connect(Callable(self, "_on_timeout")) +-- or: timer:connect("timeout", Callable(self, "_on_timeout")) +``` + +### Anonymous functions +Depending on version, prefer explicit `Callable`: +```lua +local callable = Callable(function() + print("timeout!") +end) +timer.timeout:connect(callable) +``` + +### Emitting a custom signal +```lua +local Obj = { extends = Node } + +function Obj:trigger() + self:emit_signal("my_signal", 1, 2) +end + +return Obj +``` + +--- + +## Scenes & Instantiation + +```lua +local PackedScene = load("res://scenes/Enemy.tscn") +local enemy = PackedScene:instantiate() +self:add_child(enemy) + +local sprite = Sprite2D.new() +sprite.texture = load("res://assets/player.png") +self:add_child(sprite) + +get_tree():change_scene_to_file("res://scenes/menu.tscn") +``` + +--- + +## Resources (Textures, AudioStream, Scenes, Scripts…) + +```lua +local tex = load("res://icon.svg") +local snd = load("res://sfx/hit.ogg") +``` + +Async loading: +```lua +local rl = ResourceLoader +rl.load_threaded_request("res://big_scene.tscn", "PackedScene") + +function self:_process(_dt) + local status = rl.load_threaded_get_status("res://big_scene.tscn") + if status == ResourceLoader.THREAD_LOAD_LOADED then + local scene = rl.load_threaded_get("res://big_scene.tscn") + self:add_child(scene:instantiate()) + end +end +``` + +--- + +## User Input (Input, Actions, Events) + +```lua +if Input.is_action_just_pressed("jump") then + self:jump() +end + +function self:_input(event) + if event:is_action_pressed("shoot") then + self:shoot() + end +end +``` + +--- + +## 2D / 3D Physics + +```lua +local Player = { extends = CharacterBody2D } + +function Player:_physics_process(delta) + local dir = Vector2.ZERO + if Input.is_action_pressed("ui_right") then dir.x = 1 end + if Input.is_action_pressed("ui_left") then dir.x = -1 end + self.velocity.x = dir.x * 200 + self:move_and_slide() +end + +return Player +``` + +Collision detection: +```lua +local Area = { extends = Area2D } + +function Area:_ready() + self.body_entered:connect(Callable(self, "_on_body_entered")) +end + +function Area:_on_body_entered(body) + print("Touched by", body) +end + +return Area +``` + +--- + +## UI (Control, Themes, UI signals) + +```lua +local MyUI = { extends = Control } + +function MyUI:_ready() + local btn = Button.new() + btn.text = "Click me" + btn.pressed:connect(Callable(self, "_on_btn_pressed")) + self:add_child(btn) +end + +function MyUI:_on_btn_pressed() + print("Button pressed!") +end + +return MyUI +``` + +--- + +## Audio + +```lua +local snd = load("res://sfx/laser.ogg") +local player = AudioStreamPlayer.new() +player.stream = snd +self:add_child(player) +player:play() +``` + +--- + +## Animation (AnimationPlayer, Tween) + +```lua +local anim = self:get_node("AnimationPlayer") +anim:play("run") + +local tween = self:create_tween() +tween:tween_property(self, "modulate", Color(1,1,1,0), 1.0) + :set_trans(Tween.TRANS_SINE) + :set_ease(Tween.EASE_IN_OUT) +``` + +--- + +## Lua Coroutines & Timers + +```lua +function self:_ready() + local timer = self:get_tree():create_timer(2.0) + timer.timeout:connect(Callable(self, "_on_timeout")) +end + +function self:_on_timeout() + print("2 seconds passed") +end +``` + +Coroutines: +```lua +local co = coroutine.create(function() + print("start") + coroutine.yield("wait") + print("resume") +end) + +local ok, val = coroutine.resume(co) +-- later: coroutine.resume(co) +``` + +--- + +## Lua Threads & Godot Async Tasks + +```lua +local thread = Thread.new() +local function heavy() + -- heavy work + return 123 +end +thread:start(heavy) +thread:wait_to_finish() +``` + +--- + +## Networking (High-Level Multiplayer API) + +```lua +local peer = ENetMultiplayerPeer.new() +peer:create_server(12345) +get_tree().multiplayer.peer = peer +``` + +RPC: +```lua +function self:some_rpc() + print("RPC called") +end +-- self:rpc("some_rpc") +``` + +--- + +## Debugging & Lua Errors + +- Use `print`, `assert`. +- Check the Godot console for `LuaError` / Variant conversion issues. + +--- + +## Advanced Interop (Callable, RefCounted, Userdata) + +```lua +local cb = Callable(self, "method_name") +local cb2 = Callable(function(...) print("hello", ...) end) +``` + +`RefCounted` objects live as long as a Lua reference exists. + +--- + +## Editor: tools, plugins, REPL + +- Plugin registered via `plugin.gd` (GDScript). +- REPL: `lua_repl.tscn`. +- For complex editor tooling: mix GDScript + Lua. + +--- + +## Best Practices & Common Pitfalls + +1. One `Callable` argument for `connect`. +2. Tables → Array/Dictionary automatically; beware of mixed tables. +3. Don’t block the main thread. +4. Disconnect signals when needed. +5. Avoid globals. +6. Profiling: Lua is interpreted; move hot paths to C++ if needed. +7. `to_variant` errors = unexpected `nil` or wrong type. +8. Match your Godot version with the extension build. + +--- + +## Quick FAQ + +**Yield?** → Use Timer + callback or a Lua coroutine. +**Export variables to the Inspector?** → Not natively; use GDScript if you need editor-exposed properties. +**Import another Lua script?** → `require("res://scripts/module.lua")`. +**Custom signals?** → `emit_signal`; declare them via C++/GDScript when needed. + +--- + +Need more examples? Just ask! From 6421ce0996eb4b208dca8faa0748988e1db3a374 Mon Sep 17 00:00:00 2001 From: Brandonn Date: Wed, 23 Jul 2025 02:03:42 +0400 Subject: [PATCH 3/4] Create FR_GUIDE.md --- documentation/FR_GUIDE.md | 424 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 424 insertions(+) create mode 100644 documentation/FR_GUIDE.md diff --git a/documentation/FR_GUIDE.md b/documentation/FR_GUIDE.md new file mode 100644 index 0000000..57a596c --- /dev/null +++ b/documentation/FR_GUIDE.md @@ -0,0 +1,424 @@ +# Guide complet : Utiliser Lua avec Godot via **lua-gdextension** + +> Une documentation pratique, basée sur la lecture du code source de l’extension, pour couvrir « tous les aspects » courants de Godot avec Lua : installation, syntaxe, nodes/scènes, signaux, propriétés, ressources, coroutines, threads, UI, physique, audio, réseau, éditeur, etc. Chaque section inclut des exemples Lua. + +--- + +## Table des matières +1. [Installation & configuration](#installation--configuration) +2. [Premiers pas : structure d’un script Lua](#premiers-pas--structure-dun-script-lua) +3. [Cycle de vie des Nodes](#cycle-de-vie-des-nodes) +4. [Propriétés, méthodes & Variant mapping](#propriétés-méthodes--variant-mapping) +5. [Signaux](#signaux) +6. [Scènes & instantiation](#scènes--instantiation) +7. [Ressources (Textures, AudioStream, Scenes, Scripts…)](#ressources-textures-audiostream-scenes-scripts) +8. [Entrées utilisateur (Input, Actions, événements)](#entrées-utilisateur-input-actions-événements) +9. [Physique 2D / 3D](#physique-2d--3d) +10. [UI (Control, thèmes, signals UI)](#ui-control-thèmes-signals-ui) +11. [Audio](#audio) +12. [Animation (AnimationPlayer, Tween)](#animation-animationplayer-tween) +13. [Coroutines Lua & Timers](#coroutines-lua--timers) +14. [Threads Lua & tâches asynchrones Godot](#threads-lua--tâches-asynchrones-godot) +15. [Réseau (High-Level Multiplayer API)](#réseau-high-level-multiplayer-api) +16. [Debugging & erreurs Lua](#debugging--erreurs-lua) +17. [Interopérabilité avancée (Callable, RefCounted, Userdata)](#interopérabilité-avancée-callable-refcounted-userdata) +18. [Éditeur : outils, plugins, REPL](#éditeur--outils-plugins-repl) +19. [Bonnes pratiques & pièges courants](#bonnes-pratiques--pièges-courants) +20. [FAQ rapide](#faq-rapide) + +--- + +## Installation & configuration + +1. **Installer l’extension** + - Copiez le dossier `addons/lua-gdextension` dans votre projet Godot. + - Assurez-vous que `lua_gdextension.gdextension` est dans `addons/lua-gdextension/`. + - Activez le plugin dans **Project > Project Settings > Plugins**. + +2. **Scripts Lua** + - Placez vos scripts Lua où vous voulez (ex: `res://scripts/mon_script.lua`). + - Forme type : + ```lua + local MyNode = { + extends = Node2D, + } + + function MyNode:_ready() + print("Hello from Lua!") + end + + return MyNode + ``` + +3. **REPL Lua** + - Utilisez `addons/lua-gdextension/lua_repl.tscn` pour exécuter du code à la volée. + +--- + +## Premiers pas : structure d’un script Lua + +```lua +local Foo = { + extends = Node, -- Classe Godot étendue + class_name = "Foo", -- (optionnel) +} + +Foo.my_value = 42 + +function Foo:_ready() + print("_ready called", self.my_value) +end + +function Foo:_process(delta) + -- logique frame +end + +function Foo:bar(x) + return x * 2 +end + +return Foo +``` + +**Notes :** +- `extends` doit être une classe Godot. +- `self` est l’instance Godot. +- Les callbacks spéciaux `_ready`, `_process`, `_physics_process`, etc. sont détectés. + +--- + +## Cycle de vie des Nodes + +Méthodes usuelles : `_enter_tree`, `_ready`, `_process(delta)`, `_physics_process(delta)`, `_exit_tree`, `_notification(what)`. + +```lua +local Player = { extends = CharacterBody2D } + +function Player:_enter_tree() + print("Dans l'arbre") +end + +function Player:_ready() + self.speed = 200 +end + +function Player:_physics_process(delta) + local dir = Vector2.ZERO + if Input.is_action_pressed("ui_right") then dir.x = 1 end + if Input.is_action_pressed("ui_left") then dir.x = -1 end + self.velocity = dir * self.speed + self:move_and_slide() +end + +return Player +``` + +--- + +## Propriétés, méthodes & Variant mapping + +| Godot Variant | Lua | Remarques | +|--------------------------|----------------------------------|------------------------------------| +| `bool` | boolean | | +| `int`, `float` | number | | +| `String` | string | | +| `Vector2/3/4`, `Color`… | userdata (objets) | Champs/méthodes accessibles | +| `Array` | table séquentielle | 1-based en Lua | +| `Dictionary` | table clé/valeur | | +| `Callable` | fonction Lua ou `Callable(...)` | Voir Signaux | +| `Object`/`Node` | userdata | | + +```lua +self.position = Vector2(100, 50) +print(self.position.x) + +local node = self:get_node("Camera2D") +node:make_current() + +local col = Color(1, 0.5, 0.2, 1) +local arr = {1, 2, 3} +local dict = { hp = 100, name = "Bob" } +``` + +--- + +## Signaux + +### Connexion (Godot 4) +```lua +local timer = Timer.new() +self:add_child(timer) + +function self:_on_timeout() + print("Timer finished") +end + +timer.timeout:connect(Callable(self, "_on_timeout")) +-- ou : timer:connect("timeout", Callable(self, "_on_timeout")) +``` + +### Fonctions anonymes +Selon la version, préférez explicitement `Callable` : +```lua +local callable = Callable(function() + print("timeout!") +end) +timer.timeout:connect(callable) +``` + +### Émettre un signal custom +```lua +local Obj = { extends = Node } + +function Obj:trigger() + self:emit_signal("my_signal", 1, 2) +end + +return Obj +``` + +--- + +## Scènes & instantiation + +```lua +local PackedScene = load("res://scenes/Enemy.tscn") +local enemy = PackedScene:instantiate() +self:add_child(enemy) + +local sprite = Sprite2D.new() +sprite.texture = load("res://assets/player.png") +self:add_child(sprite) + +get_tree():change_scene_to_file("res://scenes/menu.tscn") +``` + +--- + +## Ressources (Textures, AudioStream, Scenes, Scripts…) + +```lua +local tex = load("res://icon.svg") +local snd = load("res://sfx/hit.ogg") +``` + +Chargement asynchrone : +```lua +local rl = ResourceLoader +rl.load_threaded_request("res://big_scene.tscn", "PackedScene") + +function self:_process(_dt) + local status = rl.load_threaded_get_status("res://big_scene.tscn") + if status == ResourceLoader.THREAD_LOAD_LOADED then + local scene = rl.load_threaded_get("res://big_scene.tscn") + self:add_child(scene:instantiate()) + end +end +``` + +--- + +## Entrées utilisateur (Input, Actions, événements) + +```lua +if Input.is_action_just_pressed("jump") then + self:jump() +end + +function self:_input(event) + if event:is_action_pressed("shoot") then + self:shoot() + end +end +``` + +--- + +## Physique 2D / 3D + +```lua +local Player = { extends = CharacterBody2D } + +function Player:_physics_process(delta) + local dir = Vector2.ZERO + if Input.is_action_pressed("ui_right") then dir.x = 1 end + if Input.is_action_pressed("ui_left") then dir.x = -1 end + self.velocity.x = dir.x * 200 + self:move_and_slide() +end + +return Player +``` + +Détection collision : +```lua +local Area = { extends = Area2D } + +function Area:_ready() + self.body_entered:connect(Callable(self, "_on_body_entered")) +end + +function Area:_on_body_entered(body) + print("Touched by", body) +end + +return Area +``` + +--- + +## UI (Control, thèmes, signals UI) + +```lua +local MyUI = { extends = Control } + +function MyUI:_ready() + local btn = Button.new() + btn.text = "Click me" + btn.pressed:connect(Callable(self, "_on_btn_pressed")) + self:add_child(btn) +end + +function MyUI:_on_btn_pressed() + print("Button pressed!") +end + +return MyUI +``` + +--- + +## Audio + +```lua +local snd = load("res://sfx/laser.ogg") +local player = AudioStreamPlayer.new() +player.stream = snd +self:add_child(player) +player:play() +``` + +--- + +## Animation (AnimationPlayer, Tween) + +```lua +local anim = self:get_node("AnimationPlayer") +anim:play("run") + +local tween = self:create_tween() +tween:tween_property(self, "modulate", Color(1,1,1,0), 1.0) + :set_trans(Tween.TRANS_SINE) + :set_ease(Tween.EASE_IN_OUT) +``` + +--- + +## Coroutines Lua & Timers + +```lua +function self:_ready() + local timer = self:get_tree():create_timer(2.0) + timer.timeout:connect(Callable(self, "_on_timeout")) +end + +function self:_on_timeout() + print("2 secondes passées") +end +``` + +Coroutines : +```lua +local co = coroutine.create(function() + print("start") + coroutine.yield("wait") + print("resume") +end) + +local ok, val = coroutine.resume(co) +-- plus tard: coroutine.resume(co) +``` + +--- + +## Threads Lua & tâches asynchrones Godot + +```lua +local thread = Thread.new() +local function heavy() + -- travail lourd + return 123 +end +thread:start(heavy) +thread:wait_to_finish() +``` + +--- + +## Réseau (High-Level Multiplayer API) + +```lua +local peer = ENetMultiplayerPeer.new() +peer:create_server(12345) +get_tree().multiplayer.peer = peer +``` + +RPC : +```lua +function self:some_rpc() + print("RPC called") +end +-- self:rpc("some_rpc") +``` + +--- + +## Debugging & erreurs Lua + +- Utilisez `print`, `assert`. +- Lisez la console pour les erreurs `LuaError` / conversions Variant. + +--- + +## Interopérabilité avancée (Callable, RefCounted, Userdata) + +```lua +local cb = Callable(self, "method_name") +local cb2 = Callable(function(...) print("hello", ...) end) +``` + +Les objets `RefCounted` vivent tant qu’une référence existe en Lua. + +--- + +## Éditeur : outils, plugins, REPL + +- Plugin enregistré via `plugin.gd` (GDScript). +- REPL : `lua_repl.tscn`. +- Pour des outils éditeur complexes : combinez GDScript + Lua. + +--- + +## Bonnes pratiques & pièges courants + +1. Un seul argument `Callable` pour `connect`. +2. Tables → Array/Dictionary automatiques, mais attention aux tables mixtes. +3. Ne bloquez pas le thread principal. +4. Déconnectez vos signaux si nécessaire. +5. Évitez les globals. +6. Profiling : Lua reste interprété. +7. Les erreurs `to_variant` sont souvent des `nil` inattendus. +8. Vérifiez la compatibilité de version Godot / extension. + +--- + +## FAQ rapide + +**Yield ?** → Timer + callback ou coroutine. +**Exporter des variables à l’inspector ?** → Pas natif, utilisez GDScript si besoin. +**Importer un autre script Lua ?** → `require("res://scripts/module.lua")`. +**Signaux custom ?** → `emit_signal`, déclaration côté C++/GDScript au besoin. + +--- + +Besoin d’exemples supplémentaires ? Dis-moi ! From 4248b241b5f3617f53e28765527354064d167a18 Mon Sep 17 00:00:00 2001 From: Brandonn Date: Wed, 23 Jul 2025 02:12:33 +0400 Subject: [PATCH 4/4] Update README.md with User guide link --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index e223264..2bad5e9 100644 --- a/README.md +++ b/README.md @@ -215,6 +215,15 @@ lua.do_string(""" end ``` +--- + +## 📘 User Guide +- We also offer a user guide covering the use of Lua through many aspects of Godot: +* 🇫🇷 [Guide d'utilisation (Français)](documentation/FR_GUIDE.md) +* 🇬🇧 [User Guide (English)](documentation/EN_GUIDE.md) + +--- + ## TODO - [X] Bind Variant types to Lua - [X] Bind utility functions to Lua