-
-
Notifications
You must be signed in to change notification settings - Fork 29
Lua
A Lua wrapper is available to make use of the Lua language as a scripting language for the engine. This wrapper uses Sol2, a library allowing to bind C++ features in Lua (and conversely) extremely easily.
There is nothing to do to use the engine with Lua: just build it or get the artifacts, and you will be good to go. If you do not need the wrapper though, you can avoid compiling it by giving -DRAZ_USE_LUA=OFF
when generating the project with CMake.
All important functionalities are normally available using Lua. If some are not, please file an issue or create a pull request to add them! To see some examples, check the repo's scripts.
The engine can be used entirely with Lua; see the script demo and the related script to get you started. You can run the demo giving it any Lua script; simply drag & drop a .lua
file onto the executable, or run it with ./scriptDemo path/to/script.lua
.
Any entity can also have a script component, which needs a specific format to be valid:
-- Constants and/or global variables can be defined here
function setup()
-- Code here will be executed once whenever the script is (re)loaded; this function is entirely optional
-- You can use the special 'this' variable, which represents the entity containing the script
-- For example: this:getTransform().position = Vec3f.new(0, 0, 0)
end
function update(timeInfo)
-- Code here will be executed every frame; this function is required to exist
-- The 'timeInfo' parameter contains the same fields that are given to each system & world (deltaTime, globalTime, ...)
-- For example: this:getTransform():translate(0, math.sin(timeInfo.globalTime) * timeInfo.deltaTime, 0)
end
To add a script to an entity, simply do
Raz::LuaScript& luaScript = entity.addComponent<Raz::LuaScript>(/* Lua code */);
You can also reload the script by using luaScript.loadCode(...)
given some code, or luaScript.loadCodeFromFile(...)
given a file path.
Finally, you can register any other entity besides the default this
to be used from within the script:
Raz::Entity& mesh = ...;
// The given string will be the name the entity is available from within the script
luaScript.registerEntity(mesh, "myMesh");
-- No setup() function is needed in our case
function update(timeInfo)
-- Always stay one unit above the 'myMesh' entity
this:getTransform().position = myMesh:getTransform().position + Vec3f.new(0, 1, 0)
end
Functionalities that differ from their C++ counterpart are listed below.
- As Lua does not support templates, rvalues and explicit move, each
System
can be added in aWorld
through its specific function, likeworld:addRenderSystem(...)
,world:addPhysicsSystem(...)
, etc. These are meant to be strictly equivalent to theworld.addSystem<YourSystem>(...)
you would do in C++, meaning they take the same arguments in both languages. Likewise for addingRenderProcess
es in aRenderGraph
: userenderGraph:addBloomRenderProcess(...)
,renderGraph:addConvolutionRenderProcess(...)
, etc. - Unlike the above, components can be added using overloads of
Entity::addComponent()
: simply useentity:addComponent(Transform.new(...))
,entity:addComponent(Mesh.new(...))
, etc. Components being meant to be (almost exclusively) just data, they can technically be moved or copied around without issue. - Object attributes that have both getters & setters, like
Transform::get/setPosition()
, are accessible on both read & write usages as properties in Lua astransform.position
, instead oftransform:get/setPosition()
as it would normally have been. This may be useful when making heavy use of those, but can be unintuitive and thus might disappear in the future.
- The Microphone's
recoverData()
overload taking a vector is not available, as although it would be possible to make it usable, it would not result in any gain concerning memory allocations. Use the overload returning the vector instead.
- The templated functions
Image::recoverPixel()
andImage::setPixel()
are bound torecoverByte/FloatPixel()
/setByte/FloatPixel()
for single channel images &recoverVecNX()
/setVecNX()
for multi-channel images (N
being2
,3
or4
, andX
beingb
orf
; for example,recoverVec2b()
for a 2 channels byte image, orsetVec4fPixel()
for a 4 channels float image).
-
Transform::get/setScale()
is accessible withtransform.scaling
, notscale
. This is to avoid a name conflict with the functionscale()
. -
Vector::lerp()
is templated on the C++ side to allow calculations being done in the given type, avoiding truncation if needed. To that end,lerp<float>()
is available in Lua aslerpf()
.
- The
ShaderProgram::setAttribute()
&UniformBuffer::sendData()
overloads taking an int, an unsigned int or a float must be used respectively withsetIntAttribute()
/setUintAttribute()
/setFloatAttribute()
&sendIntData()
/sendUintData()
/sendFloatData()
. Lua has no unsigned integers support, and although overloading int and float works fine, it would not be possible to have the unsigned int one; three different functions for each have thus been made. The mathematical vectors & matrices overloads are still usable with the genericsetAttribute()
/sendData()
functions.- Specifically for
ShaderProgram::setAttribute()
, thestd::vector<int/unsigned int/float>
overloads are not available at all, at least for now, since I do not know ifstd::vector
can be bound easily through Sol.
- Specifically for
- The
Texture3D
constructors taking image slices are not available; use theload()
member function afterward instead.-
Texture3D::create()
does however work with those.
-
Due to certain mechanisms of Lua, which forces to drop some safety guarantees, care must be taken in order to use the engine properly.
As move semantics are not available in Lua, everything that should be (and is) moved on the C++ side, as are most components, are NOT moved explicitly in Lua. For example, to add a Mesh component, you would do:
// In C++
// Constructing in-place...
entity.addComponent<Mesh>(...);
// ... or moving a variable
Raz::Mesh mesh(...);
entity.addComponent<Mesh>(std::move(mesh)); // Moving it explicitly
-- In Lua
-- There is no in-place construction, an object must be given
-- This example works fine, as the component is moved internally and is inaccessible after the call
entity:addComponent(Mesh.new(...))
-- When giving a variable however...
local mesh = Mesh.new(...)
entity:addComponent(mesh) -- No explicit move, but the variable has been!
Due to this, all objects not given directly to the function must NOT be used anymore; adding a component to an entity ALWAYS moves the given object, if applicable. This is obviously the same that is done on the C++ side, but in a hidden way. There is currently not much that can be done to make this error-prone behavior clearer. In the future, a kind of handle mechanism could be used in the Lua wrapper to track lifetimes specifically for this purpose.
- Home
- How to build RaZ
- Getting started
- General usage knowledge
- Some examples...
- Playground
- Tutorials
- File formats
- Modules
- Debug