Improve Lua ECS #30

Merged
SeanOMik merged 15 commits from feat/improve-lua-ecs-29 into main 2024-10-30 03:22:50 +00:00
17 changed files with 226 additions and 33 deletions
Showing only changes of commit 4dbd96832f - Show all commits

View File

@ -77,22 +77,14 @@ end
end ]] end ]]
function on_update() function on_update()
--[[ ---@type number -- Although WorldTransform isn't used, I only want to
local dt = world:resource(DeltaTime) -- modify entities with that component.
local act = world:resource(ActionHandler)
---@type number
local move_objs = act:get_axis("ObjectsMoveUpDown")
world:view(function (t)
if move_objs ~= nil then
t:translate(0, move_objs * 0.35 * dt, 0)
return t
end
end, Transform) ]]
-- although WorldTransform isn't used, I only want to modify entities with that component.
local view = View.new(Transform, WorldTransform, Res(DeltaTime)) local view = View.new(Transform, WorldTransform, Res(DeltaTime))
local res = world:view_query(view) local res = world:view_query(view)
---@param transform Transform
---@param _wt WorldTransform
---@param dt DeltaTime
for entity, transform, _wt, dt in res:iter() do for entity, transform, _wt, dt in res:iter() do
transform:translate(0, 0.15 * dt, 0) transform:translate(0, 0.15 * dt, 0)
entity:update(transform) entity:update(transform)
@ -100,8 +92,9 @@ function on_update()
local changed_view = View.new(Changed(Transform)) local changed_view = View.new(Changed(Transform))
local changed_res = world:view_query(changed_view) local changed_res = world:view_query(changed_view)
---@param transform Transform
for _, transform in changed_res:iter() do for _, transform in changed_res:iter() do
print("Entity transform changed to: " .. tostring(transform)) print("Entity transform changed to: '" .. tostring(transform) .. "' on tick " .. tostring(world:get_tick()))
end end
end end

View File

@ -72,6 +72,12 @@ impl TickTracker {
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Default, PartialOrd, Ord)] #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Default, PartialOrd, Ord)]
pub struct Tick(u64); pub struct Tick(u64);
impl From<u64> for Tick {
fn from(value: u64) -> Self {
Self(value)
}
}
impl std::ops::Deref for Tick { impl std::ops::Deref for Tick {
type Target = u64; type Target = u64;

View File

@ -5,8 +5,8 @@ function Res(resource)
return ResQuery.new(resource) return ResQuery.new(resource)
end end
---Create a Changed Query of a resource or component. ---Create a `ChangedQuery` of a resource or component.
---@param resource table|userdata ---@param val table|userdata
---@return ChangedQuery ---@return ChangedQuery
function Changed(val) function Changed(val)
return ChangedQuery.new(val) return ChangedQuery.new(val)

View File

@ -1,4 +1,29 @@
---@meta ---@meta
---An entity handle.
---@class Entity: userdata ---@class Entity: userdata
Entity = {} Entity = {}
---Get the id of the Entity.
---@return number
function Entity:id() end
---Get the generation number of the Entity.
---
---Entity handles are reused by the ECS World, the generation is used to tell reused Entity
---id's apart from previous generations.
---
---@return number
function Entity:generation() end
---A reference to an entity in the world.
---
---Can be used to insert and update components on the entity.
---
---@class EntityRef: userdata
EntityRef = {}
---Update components that are **already** on an Entity.
---
---@param ... any The components to update on the entity.
function EntityRef:update(...) end

View File

@ -0,0 +1,9 @@
---@meta
---@class EventReader<T>: userdata
EventReader = {}
---Get an iterator for reading the event.
---@generic T
---@return fun(): T? iterator An iterator for reading the events.
function EventReader:read() end

View File

@ -0,0 +1,9 @@
require "action_handler"
require "camera"
require "delta_time"
require "entity"
require "event_reader"
require "free_fly_camera"
require "window"
require "world_transform"
require "world"

View File

@ -0,0 +1,22 @@
---@meta
---An ECS query used for obtaining **changed** resources or components from the world.
---
---Use the utility function `Changed(...)` to create a new query since its faster to
---write than this.
---
---This query will not return if the resource or component has not changed since the last tick.
---
---@class ChangedQuery: userdata
ChangedQuery = {}
---Create a new ChangedQuery.
---
---Use the utility function `Changed(...)` to create a new query since its faster to
---write than this.
---
---@param val table|userdata The component or resource to detect changed of.
function ChangedQuery:new(val) end
---An internal function used by the engine to retrieve the query result.
function ChangedQuery:__lyra_internal_ecs_query_result(world, entity) end

View File

@ -0,0 +1,3 @@
require "view"
require "changed"
require "res"

View File

@ -0,0 +1,16 @@
---@meta
---An ECS query used for obtaining Resources from the `World`.
---@class ResQuery: userdata
ResQuery = {}
---Create a new ResQuery for getting a Resource from the `World`.
---
---Use the utility function `Res(...)` to create a new query since its faster to
---write than this.
---
---@param val table|userdata The resource type to obtain.
function ResQuery:new(val) end
---An internal function used by the engine to retrieve the query result.
function ResQuery:__lyra_internal_ecs_query_result(world, entity) end

View File

@ -0,0 +1,23 @@
---@meta
---@class View: userdata
View = {}
---Create a new view to query for components and world resources.
---
---Each parameter is a query. If you want to query entities with components, you would just use
---the component names.
---There are other queries, like `Changed` for querying for changed resources and components,
---and `Res` for querying for resources.
---
---@return View
function View.new(...) end
---@class ViewResult: userdata
ViewResult = {}
---Returns an interator over the results of the View.
---
---@generic T...
---@return fun(): EntityRef, T... iterator An iterator over the results. In the same order of the created View.
function ViewResult:iter() end

View File

@ -73,3 +73,28 @@ function World:add_resource(resource) end
---@param path string ---@param path string
---@return Handle asset An asset handle to the requested resource type. ---@return Handle asset An asset handle to the requested resource type.
function World:request_asset(path) end function World:request_asset(path) end
---Get the current tick of the world.
---
---The tick is used to drive changed detection of resources and components.
---The world tick is iterated every frame.
---
---@return number
function World:get_tick() end
---Get an event reader of a specific event.
---
---@generic T
---@param event T
---@return EventReader<T>
function World:read_event(event) end
---View the world using the queries contained in a View.
---
---@param view View
---@return ViewResult
function World:view_query(view) end
--World global
---@type World
world = nil

View File

@ -1,10 +1,4 @@
require "math.vec2" require "math.init"
require "math.vec3" require "ecs.init"
require "math.vec4"
require "math.quat"
require "math.transform"
require "ecs.window"
require "ecs.delta_time"
require "asset.handle" require "asset.handle"

View File

@ -0,0 +1,6 @@
require "math.vec2"
require "math.vec3"
require "math.vec4"
require "math.quat"
require "math.transform"
require "math.angle"

View File

@ -53,8 +53,7 @@ impl ScriptApiProvider for LyraEcsApiProvider {
expose_comp_table_wrapper::<LuaWorldTransform>(&ctx, &globals, "WorldTransform")?; expose_comp_table_wrapper::<LuaWorldTransform>(&ctx, &globals, "WorldTransform")?;
expose_table_wrapper::<LuaDeviceEvent>(&ctx, &globals, "DeviceEvent")?; expose_table_wrapper::<LuaDeviceEvent>(&ctx, &globals, "DeviceEvent")?;
let dt_table = create_reflect_table::<lyra_game::DeltaTime>(&ctx)?; expose_resource_table_wrapper::<lyra_game::DeltaTime>(&ctx, &globals, "DeltaTime")?;
globals.set("DeltaTime", dt_table)?;
Ok(()) Ok(())
} }
@ -68,13 +67,14 @@ impl ScriptApiProvider for LyraEcsApiProvider {
} }
} }
fn create_reflect_table<T: Reflect + ResourceObject + Default + 'static>(lua: &mlua::Lua) -> mlua::Result<mlua::Table> { fn expose_resource_table_wrapper<T: Reflect + ResourceObject + Default + 'static>(lua: &mlua::Lua, globals: &mlua::Table, name: &str) -> mlua::Result<()> {
let table = lua.create_table()?; let table = lua.create_table()?;
table.set(FN_NAME_INTERNAL_REFLECT_TYPE, lua.create_function(|_, ()| { table.set(FN_NAME_INTERNAL_REFLECT_TYPE, lua.create_function(|_, ()| {
Ok(ScriptBorrow::from_resource::<T>(None)) Ok(ScriptBorrow::from_resource::<T>(None))
})?)?; })?)?;
Ok(table) globals.set(name, table)?;
Ok(())
} }
fn create_reflect_comp_table<T>(lua: &mlua::Lua, name: &str) -> mlua::Result<mlua::Table> fn create_reflect_comp_table<T>(lua: &mlua::Lua, name: &str) -> mlua::Result<mlua::Table>

View File

@ -10,7 +10,7 @@ use lyra_resource::ResourceManager;
use mlua::{IntoLua, ObjectLike}; use mlua::{IntoLua, ObjectLike};
use super::{ use super::{
reflect_user_data, wrappers::{LuaResHandleToComponent, LuaWrappedEventProxy}, Error, ReflectLuaProxy, ReflectedIterator, TypeLookup, ecs::{View, ViewResult}, FN_NAME_INTERNAL_AS_COMPONENT, FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE ecs::{View, ViewResult}, reflect_user_data, wrappers::{LuaResHandleToComponent, LuaTick, LuaWrappedEventProxy}, Error, ReflectLuaProxy, ReflectedIterator, TypeLookup, FN_NAME_INTERNAL_AS_COMPONENT, FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE
}; };
impl mlua::FromLua for ScriptEntity { impl mlua::FromLua for ScriptEntity {
@ -352,5 +352,9 @@ impl mlua::UserData for ScriptWorldPtr {
methods.add_method("view_query", |_, this, view: mlua::UserDataRef<View>| { methods.add_method("view_query", |_, this, view: mlua::UserDataRef<View>| {
ViewResult::new(this.clone(), &view) ViewResult::new(this.clone(), &view)
}); });
methods.add_method("get_tick", |_, this, ()| {
let w = this.read();
Ok(LuaTick(w.current_tick()))
});
} }
} }

View File

@ -24,3 +24,6 @@ pub use events::*;
mod world_transform; mod world_transform;
pub use world_transform::*; pub use world_transform::*;
mod tick;
pub use tick::*;

View File

@ -0,0 +1,55 @@
use std::any::TypeId;
use lyra_ecs::Tick;
use crate::lua::LuaWrapper;
#[derive(Clone, Default)]
pub struct LuaTick(pub(crate) Tick);
impl std::ops::Deref for LuaTick {
type Target = Tick;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl std::ops::DerefMut for LuaTick {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl mlua::FromLua for LuaTick {
fn from_lua(v: mlua::Value, _: &mlua::Lua) -> mlua::Result<Self> {
let tyname = v.type_name();
let num = v.as_number()
.ok_or(mlua::Error::FromLuaConversionError { from: tyname, to: "Tick".into(), message: None })?;
Ok(Self(Tick::from(num as u64)))
}
}
impl mlua::IntoLua for LuaTick {
fn into_lua(self, _: &mlua::Lua) -> mlua::Result<mlua::Value> {
Ok(mlua::Value::Number(*self.0 as f64))
}
}
impl LuaWrapper for LuaTick {
type Wrap = Tick;
#[inline(always)]
fn wrapped_type_id() -> std::any::TypeId {
TypeId::of::<Tick>()
}
#[inline(always)]
fn into_wrapped(self) -> Self::Wrap {
self.0
}
fn from_wrapped(wrap: Self::Wrap) -> Option<Self> {
Some(Self(wrap))
}
}