Add optional Lua scripting module, examples, and validation

This commit is contained in:
emil28092005
2026-09-18 11:28:50 +03:00
parent 06210aac23
commit 5a67735ff4
45 changed files with 3887 additions and 92 deletions
+19
View File
@@ -0,0 +1,19 @@
# Lua playground
A Lua-only project: there is no project `Gameplay.cpp`. The native engine and Player
are built normally; the two gameplay behaviors are loaded from Lua source.
Open this folder as a project in the Editor, refresh Lua schemas, open
`Scenes/main.scene.json`, then Play. **A/D** or arrows move, **Space** jumps from
ground, and **E** resets the player. The gold beacon shows non-physical animation
and a shared `require("util.motion")` module.
Change `Scripts/player.lua` in an external editor and save. Development Play watches
Lua sources; a successful reload restarts the scene and resets script state. Invalid
source leaves the previous generation running and reports the error. Export copies
the captured Lua files into the game; a Lua executable or separately installed Lua
library is not needed.
Run `faset_lua_setup` through the Editor command palette to install the Faset LuaLS
declarations and, if absent, `.luarc.json`. See the
[Lua manual](../../docs/manual/scripting/lua.md) for the API and sandbox boundaries.
+71
View File
@@ -0,0 +1,71 @@
{
"format": "faset.scene",
"version": 1,
"id": "example-lua-scene",
"name": "Lua playground — A/D / Space / E",
"dimension": 2,
"simulation": {
"fixed_delta": 0.016666666666666666,
"max_catch_up_ticks": 4,
"physics_substeps": 4,
"gravity": [0, -9.81, 0]
},
"entities": [
{
"id": "floor", "name": "Ground", "parent": null,
"components": [
{
"id": "floor-transform", "type": "faset.transform", "version": 1,
"fields": {"position": [0, -0.5, 0]}
},
{
"id": "floor-sprite", "type": "faset.sprite", "version": 1,
"fields": {"size": [16, 1], "color": [0.2, 0.3, 0.4, 1]}
},
{
"id": "floor-body", "type": "faset.rigid_body_2d", "version": 1,
"fields": {"body_type": "static", "half_extents": [8, 0.5]}
}
]
},
{
"id": "player", "name": "Player", "parent": null,
"components": [
{
"id": "player-transform", "type": "faset.transform", "version": 1,
"fields": {"position": [-2, 1.5, 0]}
},
{
"id": "player-sprite", "type": "faset.sprite", "version": 1,
"fields": {"size": [0.8, 1], "color": [0.2, 0.75, 0.9, 1]}
},
{
"id": "player-body", "type": "faset.rigid_body_2d", "version": 1,
"fields": {"body_type": "dynamic", "half_extents": [0.4, 0.5], "friction": 0.3}
},
{
"id": "player-controller", "type": "example.lua_player", "version": 1,
"fields": {"move_speed": 5, "jump_speed": 7}
}
]
},
{
"id": "beacon", "name": "Beacon", "parent": null,
"components": [
{
"id": "beacon-transform", "type": "faset.transform", "version": 1,
"fields": {"position": [3, 2, 0]}
},
{
"id": "beacon-sprite", "type": "faset.sprite", "version": 1,
"fields": {"size": [0.5, 0.5], "color": [1, 0.7, 0.2, 1]}
},
{
"id": "beacon-behavior", "type": "example.lua_beacon", "version": 1,
"fields": {"amplitude": 0.3, "frequency": 0.7}
}
]
}
],
"instances": []
}
+27
View File
@@ -0,0 +1,27 @@
local motion = require("util.motion")
local Beacon = faset.behavior {
id = "example.lua_beacon",
version = 1,
name = "Lua Beacon",
fields = {
amplitude = { name = "Height", type = "number", default = 0.3, min = 0, max = 2 },
frequency = { name = "Frequency", type = "number", default = 0.7, min = 0, max = 5 }
}
}
function Beacon:on_start()
self.state.origin_y = self.entity:transform().position.y
self.state.elapsed = 0
end
function Beacon:update(delta)
self.state.elapsed = self.state.elapsed + delta
local pose = self.entity:transform()
pose.position.y = self.state.origin_y
+ motion.bob(self.state.elapsed, self.fields.frequency, self.fields.amplitude)
pose.rotation.z = self.state.elapsed
self.entity:set_transform(pose)
end
return Beacon
+47
View File
@@ -0,0 +1,47 @@
local Player = faset.behavior {
id = "example.lua_player",
version = 1,
name = "Lua Player",
fields = {
move_speed = {
name = "Move speed", type = "number", default = 5,
min = 0, max = 30, units = "m/s"
},
jump_speed = {
name = "Jump speed", type = "number", default = 7,
min = 0, max = 20, units = "m/s"
}
}
}
function Player:on_start()
self.state.origin = self.entity:transform()
self.state.jumps = 0
faset.log("Lua player ready: A/D move, Space jump, E reset")
end
function Player:fixed_update(delta)
local input = faset.input()
if input.interact_pressed then
self.entity:teleport(self.state.origin)
self.entity:set_velocity { x = 0, y = 0, z = 0 }
self.state.jumps = 0
return
end
local velocity = self.entity:velocity()
velocity.x = input.horizontal * self.fields.move_speed
if input.jump_pressed and self.entity:is_grounded() then
velocity.y = self.fields.jump_speed
self.state.jumps = self.state.jumps + 1
faset.log("Jump", self.state.jumps)
end
self.entity:set_velocity(velocity)
if self.entity:transform().position.y < -10 then
self.entity:teleport(self.state.origin)
self.entity:set_velocity { x = 0, y = 0, z = 0 }
end
end
return Player
+7
View File
@@ -0,0 +1,7 @@
local motion = {}
function motion.bob(time, frequency, amplitude)
return math.sin(time * frequency * 2 * math.pi) * amplitude
end
return motion
+13
View File
@@ -0,0 +1,13 @@
{
"format": "faset.project",
"version": 1,
"id": "example-lua-project",
"name": "Lua playground",
"dimension": 2,
"start_scene": "Scenes/main.scene.json",
"scripting": {
"lua": {
"scripts": ["Scripts/player.lua", "Scripts/beacon.lua"]
}
}
}