/entt-meets-sol2

Use EnTT in Lua with Sol2

Primary LanguageC++Creative Commons Zero v1.0 UniversalCC0-1.0

EnTT meets Sol2

CodeFactor Grade GitHub

Requirements

  • EnTT v.3.13.0 (latest)
  • sol2 v3.2.2 (latest)

Building

> git clone https://github.com/skaarj1989/entt-meets-sol2.git
> cd entt-meets-sol2
> cmake -S . -B build

vcpkg quickstart

https://github.com/microsoft/vcpkg#getting-started

> git clone https://github.com/microsoft/vcpkg
> ./vcpkg/bootstrap-vcpkg.bat

Add the following environment variables

VCPKG_ROOT=path_to_vcpkg
VCPKG_DEFAULT_TRIPLET=x64-windows

Install required dependencies

> ./vcpkg install entt sol2 lua

Registry

entt/wiki/registry

Goal

  • Expose registry to Lua scripts:
    • Manage entity lifetime
    • Set components
    • Get component by reference
    • runtime_view
    • Some of MonoBehaviour functionality

c++ setup

examples/registry

struct Transform { int x, y; };

register_meta_component<Transform>();

sol::state lua{};
lua.require("dispatcher" ...);

lua.new_usertype<Transform>("Transform",
  "type_id", &entt::type_hash<Transform>::value,
  sol::call_constructor,
  sol::factories([](int x, int y) {
    return Transform{ x, y };
  }),
  "x", &Transform::x,
  "y", &Transform::y
);
entt::registry registry{};
lua["registry"] = std::ref(registry); // Make the registry available to Lua

Lua script

-- Create a registry inside a script
registry = entt.registry.new()
mario = registry:create()
registry:emplace(mario, Transform(1, 2))
material = registry:get(mario, Material)

if (registry:has(mario, Transform)) then
  registry:emplace(mario, DeletionFlag())
end
-- Utilizes variadic args - pass as many types as you want
registry:runtime_view(Transform, DeletionFlag):each(
  function(entity)
    registry:remove(entity, DeletionFlag)
  end
)

Want something like MonoBehaviour in Unity? examples/system

Lua script

local node = {}
function node:init()
  self.owner:emplace(self.id(), Transform(5, 9))
end
function node:update(dt)
  local transform = self.owner:get(self.id(), Transform)
  transform.x = transform.x + 1
end
function node:destroy()
  -- ...
end

return node

c++ setup

struct ScriptComponent {
  sol::table self;
  struct {
    sol::function update;
  } hooks;
};

registry.emplace<ScriptComponent>(entity, lua.script_file("behavior.lua"));

while (true) {
  auto view = registry.view<ScriptComponent>();
  for (auto entity : view) {
    auto &script = view.get<ScriptComponent>(entity);
    script.hooks.update(script.self, delta_time);
  }
}

Event dispatcher

entt/wiki/dispatcher

Goal

  • Either create new (inside script) or connect existing (native c++) dispatcher
  • Trigger/enqueue events from Lua side
  • Listen for events in Lua scripts
  • Support disconnection of listeners

c++ setup

examples/dispatcher

struct an_event { int value; };

register_meta_event<an_event>();

sol::state lua{}
lua.require("dispatcher", ...)

lua.new_usertype<an_event>("an_event",
  "type_id", &entt::type_hash<an_event>::value,
  sol::call_constructor,
  sol::factories([](int value) {
    return an_event{ value };
  }),
  "value", &an_event::value
);
entt::dispatcher dispatcher{};
lua["dispatcher"] = std::ref(dispatcher); // Make the dispatcher available to Lua

// load a script ...

dispatcher.update(); // inside loop

Lua script

-- Create a dispatcher in a script
dispatcher = entt.dispatcher.new()
listener = {}
function listener.receive(evt)
    -- ...
end
function listener.method(evt)
    -- ...
end

-- You have to store result of 'connect()', otherwise listener will be
-- disconnected in lua garbage collection step.
conn = dispatcher:connect(an_event, listener.receive)
dispatcher:connect(another_event, listener.method)

-- Event type is deduced from bound 'type_id()' method
dispatcher:trigger(an_event(42))
dispatcher:enqueue(another_event())

conn = nil -- to disconnect listener

Cooperative scheduler

entt/wiki/cooperative-scheduler

Goal

  • Define process class in Lua and attach it to scheduler either native c++ or dedicated to script
  • Allow process chaining

c++ setup

examples/scheduler

sol::state lua{};
lua.require("scheduler", ...);
entt::scheduler scheduler{};
lua["scheduler"] = std::ref(scheduler); // Make the scheduler available to Lua

scheduler.update(dt); // inside loop

Lua script

-- Create a scheduler in a script
scheduler = entt.scheduler.new()
local process_a = {}
function process:update(dt)
    -- ...
end
-- define init, succeeded and other methods ...

-- This one differs from entt attach().then().then ... but works the same
scheduler:attach(
    process_a,
    process_b,
    process_n
    -- etc ...
)

License

Code released under CC0 1.0 Universal