Move tiny-tools into tiny.lua directly.
Less implicit global var work. The global is still there, but it's explicitly captured in world.lua, removing any weird require-ordering rules.
This commit is contained in:
parent
59e7095470
commit
aca14fbfb0
48
lib/tiny.lua
48
lib/tiny.lua
|
@ -905,6 +905,53 @@ function tiny.setSystemIndex(world, system, index)
|
||||||
return oldIndex
|
return oldIndex
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@author Sage Vaillancourt
|
||||||
|
---
|
||||||
|
--- A convenience function for quickly building a well-typed system matching the given shape.
|
||||||
|
--- Typing does not work when a literal tiny filter is passed as the `shape`, but this function can still be convenient.
|
||||||
|
---@generic T
|
||||||
|
---@param name string The `.name` of the system. Shouldn't be used for identifying it in code, but can be helpful when debugging.
|
||||||
|
---@param shape T | fun()
|
||||||
|
---@param process fun(entity: T, dt: number, system: System) | nil
|
||||||
|
---@param compare nil | fun(system: System, entityA: T, entityB: T): boolean If provided, will instead initialize a sortedProcessingSystem.
|
||||||
|
---@return System | { entities: T[] }
|
||||||
|
function tiny.filteredSystem(world, name, shape, process, compare)
|
||||||
|
assert(type(name) == "string")
|
||||||
|
assert(process == nil or type(process) == "function")
|
||||||
|
|
||||||
|
local system = compare and tiny.sortedProcessingSystem() or tiny.processingSystem()
|
||||||
|
system.compare = compare
|
||||||
|
system.name = name
|
||||||
|
|
||||||
|
if type(shape) == "table" then
|
||||||
|
local keys = {}
|
||||||
|
for key, value in pairs(shape) do
|
||||||
|
local isTable = type(value) == "table"
|
||||||
|
local isMaybe = isTable and value.maybe ~= nil
|
||||||
|
|
||||||
|
if not isMaybe then
|
||||||
|
-- ^ Don't require any Maybe types
|
||||||
|
keys[#keys + 1] = key
|
||||||
|
end
|
||||||
|
end
|
||||||
|
system.filter = tiny.requireAll(unpack(keys))
|
||||||
|
elseif type(shape) == "function" then
|
||||||
|
system.filter = shape
|
||||||
|
else
|
||||||
|
error("Unknown type for `shape` argument: " .. type(shape))
|
||||||
|
end
|
||||||
|
|
||||||
|
if not process then
|
||||||
|
return world:addSystem(system)
|
||||||
|
end
|
||||||
|
|
||||||
|
function system:process(e, dt)
|
||||||
|
process(e, dt, self)
|
||||||
|
end
|
||||||
|
|
||||||
|
return world:addSystem(system)
|
||||||
|
end
|
||||||
|
|
||||||
-- Construct world metatable.
|
-- Construct world metatable.
|
||||||
worldMetaTable = {
|
worldMetaTable = {
|
||||||
__index = {
|
__index = {
|
||||||
|
@ -921,6 +968,7 @@ worldMetaTable = {
|
||||||
getEntityCount = tiny.getEntityCount,
|
getEntityCount = tiny.getEntityCount,
|
||||||
getSystemCount = tiny.getSystemCount,
|
getSystemCount = tiny.getSystemCount,
|
||||||
setSystemIndex = tiny.setSystemIndex,
|
setSystemIndex = tiny.setSystemIndex,
|
||||||
|
filteredSystem = tiny.filteredSystem,
|
||||||
},
|
},
|
||||||
__tostring = function()
|
__tostring = function()
|
||||||
return "<tiny-ecs_World>"
|
return "<tiny-ecs_World>"
|
||||||
|
|
17
main.lua
17
main.lua
|
@ -1,20 +1,17 @@
|
||||||
require("tiny-debug")
|
require("tiny-debug")
|
||||||
require("utils")
|
require("utils")
|
||||||
require("tiny-tools")
|
|
||||||
|
|
||||||
local tiny = require("lib/tiny")
|
|
||||||
World = tiny.world()
|
|
||||||
|
|
||||||
require("generated/filter-types")
|
require("generated/filter-types")
|
||||||
require("generated/assets")
|
require("generated/assets")
|
||||||
require("generated/all-systems")
|
require("generated/all-systems")
|
||||||
|
|
||||||
|
local world = require("world")
|
||||||
|
|
||||||
local scenarios = {
|
local scenarios = {
|
||||||
default = function()
|
default = function()
|
||||||
-- TODO: Add default entities
|
-- TODO: Add default entities
|
||||||
end,
|
end,
|
||||||
textTestScenario = function()
|
textTestScenario = function()
|
||||||
World:addEntity({
|
world:addEntity({
|
||||||
position = { x = 0, y = 600 },
|
position = { x = 0, y = 600 },
|
||||||
drawAsText = {
|
drawAsText = {
|
||||||
text = "Hello, world!",
|
text = "Hello, world!",
|
||||||
|
@ -41,7 +38,7 @@ function love.update(dt)
|
||||||
delta = dt
|
delta = dt
|
||||||
|
|
||||||
if love.keyboard.isDown("r") then
|
if love.keyboard.isDown("r") then
|
||||||
World:clearEntities()
|
world:clearEntities()
|
||||||
currentScenario()
|
currentScenario()
|
||||||
freeze = false
|
freeze = false
|
||||||
end
|
end
|
||||||
|
@ -54,7 +51,7 @@ function love.update(dt)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
World:update(delta, function(_, system)
|
world:update(delta, function(_, system)
|
||||||
if system.deferToEnd then
|
if system.deferToEnd then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
@ -63,14 +60,14 @@ function love.update(dt)
|
||||||
end
|
end
|
||||||
|
|
||||||
function love.draw()
|
function love.draw()
|
||||||
World:update(delta, function(_, system)
|
world:update(delta, function(_, system)
|
||||||
if system.deferToEnd then
|
if system.deferToEnd then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
return system.isDrawSystem
|
return system.isDrawSystem
|
||||||
end)
|
end)
|
||||||
|
|
||||||
World:update(delta, function(_, system)
|
world:update(delta, function(_, system)
|
||||||
return system.deferToEnd
|
return system.deferToEnd
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,15 +1,18 @@
|
||||||
Camera = {
|
local world = require("world")
|
||||||
|
|
||||||
|
local camera = {
|
||||||
pan = {
|
pan = {
|
||||||
x = 0,
|
x = 0,
|
||||||
y = 0,
|
y = 0,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
expireBelowScreenSystem = filteredSystem("expireBelowScreen", { position = T.XyPair, expireBelowScreenBy = T.number })
|
local expireBelowScreen =
|
||||||
|
world:filteredSystem("expireBelowScreen", { position = T.XyPair, expireBelowScreenBy = T.number })
|
||||||
|
|
||||||
local focusPriority = {}
|
local focusPriority = {}
|
||||||
|
|
||||||
cameraPanSystem = filteredSystem("cameraPan", { focusPriority = T.number, position = T.XyPair }, function(e, _)
|
cameraPanSystem = world:filteredSystem("cameraPan", { focusPriority = T.number, position = T.XyPair }, function(e, _)
|
||||||
if e.focusPriority >= focusPriority.priority then
|
if e.focusPriority >= focusPriority.priority then
|
||||||
focusPriority.position = e.position
|
focusPriority.position = e.position
|
||||||
end
|
end
|
||||||
|
@ -21,35 +24,13 @@ function cameraPanSystem.preProcess()
|
||||||
end
|
end
|
||||||
|
|
||||||
function cameraPanSystem:postProcess()
|
function cameraPanSystem:postProcess()
|
||||||
Camera.pan.x = math.max(0, focusPriority.position.x - 200)
|
camera.pan.x = math.max(0, focusPriority.position.x - 200)
|
||||||
Camera.pan.y = math.min(0, focusPriority.position.y - 120)
|
camera.pan.y = math.min(0, focusPriority.position.y - 120)
|
||||||
-- TODO: set draw offset
|
love.graphics.translate(0, -camera.pan.y)
|
||||||
|
|
||||||
for _, entity in pairs(expireBelowScreenSystem.entities) do
|
for _, entity in pairs(expireBelowScreen.entities) do
|
||||||
if entity.position.y - (Camera.pan.y + 240) > entity.expireBelowScreenBy then
|
if entity.position.y - (camera.pan.y + 240) > entity.expireBelowScreenBy then
|
||||||
self.world:removeEntity(entity)
|
self.world:removeEntity(entity)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local cameraTopIsh, cameraBottomIsh
|
|
||||||
|
|
||||||
local enableNearCameraY = filteredSystem(
|
|
||||||
"enableNearCameraY",
|
|
||||||
{ enableNearCameraY = Arr(T.Entity) },
|
|
||||||
function(e, _, system)
|
|
||||||
if e.position.y > cameraTopIsh and e.position.y < cameraBottomIsh then
|
|
||||||
for _, enable in ipairs(e.enableNearCameraY) do
|
|
||||||
enable.velocity = e.velocity
|
|
||||||
system.world:addEntity(enable)
|
|
||||||
end
|
|
||||||
system.world:removeEntity(e)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
)
|
|
||||||
|
|
||||||
local within = 1000
|
|
||||||
function enableNearCameraY:preProcess()
|
|
||||||
cameraTopIsh = Camera.pan.y - within
|
|
||||||
cameraBottomIsh = Camera.pan.y + 240 + within
|
|
||||||
end
|
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
collidingEntities = filteredSystem("collidingEntitites", {
|
local world = require("world")
|
||||||
|
|
||||||
|
local collidingEntities = world:filteredSystem("collidingEntitites", {
|
||||||
position = T.XyPair,
|
position = T.XyPair,
|
||||||
size = T.XyPair,
|
size = T.XyPair,
|
||||||
canCollideWith = T.BitMask,
|
canCollideWith = T.BitMask,
|
||||||
|
@ -19,14 +21,14 @@ local function intersects(rect, rectOther)
|
||||||
return leftOther < right and left < rightOther and topOther < bottom and top < bottomOther
|
return leftOther < right and left < rightOther and topOther < bottom and top < bottomOther
|
||||||
end
|
end
|
||||||
|
|
||||||
filteredSystem(
|
world:filteredSystem(
|
||||||
"collisionDetection",
|
"collisionDetection",
|
||||||
{ position = T.XyPair, size = T.XyPair, canBeCollidedBy = T.BitMask, isSolid = Maybe(T.bool) },
|
{ position = T.XyPair, size = T.XyPair, canBeCollidedBy = T.BitMask, isSolid = Maybe(T.bool) },
|
||||||
-- Here, the entity, e, refers to some entity that a moving object may be colliding *into*
|
-- Here, the entity, e, refers to some entity that a moving object may be colliding *into*
|
||||||
function(e, _, system)
|
function(e, _, system)
|
||||||
for _, collider in pairs(collidingEntities.entities) do
|
for _, collider in pairs(collidingEntities.entities) do
|
||||||
if
|
if
|
||||||
(e ~= collider)
|
(e ~= collider)
|
||||||
and collider.canCollideWith
|
and collider.canCollideWith
|
||||||
and e.canBeCollidedBy
|
and e.canBeCollidedBy
|
||||||
and bit.band(collider.canCollideWith, e.canBeCollidedBy) ~= 0
|
and bit.band(collider.canCollideWith, e.canBeCollidedBy) ~= 0
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
filteredSystem("collisionResolution", { collisionBetween = T.Collision }, function(e, _, system)
|
local world = require("world")
|
||||||
|
|
||||||
|
world:filteredSystem("collisionResolution", { collisionBetween = T.Collision }, function(e, _, system)
|
||||||
local collidedInto, collider = e.collisionBetween[1], e.collisionBetween[2]
|
local collidedInto, collider = e.collisionBetween[1], e.collisionBetween[2]
|
||||||
system.world:removeEntity(e)
|
system.world:removeEntity(e)
|
||||||
end)
|
end)
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
filteredSystem("decay", { decayAfterSeconds = T.number }, function(e, dt, system)
|
local world = require("world")
|
||||||
|
|
||||||
|
world:filteredSystem("decay", { decayAfterSeconds = T.number }, function(e, dt, system)
|
||||||
e.decayAfterSeconds = e.decayAfterSeconds - dt
|
e.decayAfterSeconds = e.decayAfterSeconds - dt
|
||||||
if e.decayAfterSeconds <= 0 then
|
if e.decayAfterSeconds <= 0 then
|
||||||
system.world:removeEntity(e)
|
system.world:removeEntity(e)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
LiveForNFrames = filteredSystem("liveForNFrames", { liveForNFrames = T.number }, function(e, _, system)
|
LiveForNFrames = world:filteredSystem("liveForNFrames", { liveForNFrames = T.number }, function(e, _, system)
|
||||||
e.liveForNFrames = e.liveForNFrames - 1
|
e.liveForNFrames = e.liveForNFrames - 1
|
||||||
if e.liveForNFrames <= 0 then
|
if e.liveForNFrames <= 0 then
|
||||||
system.world:removeEntity(e)
|
system.world:removeEntity(e)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
LiveForNFrames.deferToEnd = true
|
LiveForNFrames.deferToEnd = true
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
local world = require("world")
|
||||||
local gfx = love.graphics
|
local gfx = love.graphics
|
||||||
|
|
||||||
---@generic T
|
---@generic T
|
||||||
|
@ -5,7 +6,7 @@ local gfx = love.graphics
|
||||||
---@param process fun(entity: T, dt: number, system: System) | nil
|
---@param process fun(entity: T, dt: number, system: System) | nil
|
||||||
---@return System | { entities: T[] }
|
---@return System | { entities: T[] }
|
||||||
local function drawSystem(name, shape, process)
|
local function drawSystem(name, shape, process)
|
||||||
local system = filteredSystem(name, shape, process, function(_, a, b)
|
local system = world:filteredSystem(name, shape, process, function(_, a, b)
|
||||||
if a.z ~= nil and b.z ~= nil then
|
if a.z ~= nil and b.z ~= nil then
|
||||||
return a.z < b.z
|
return a.z < b.z
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,16 +1,17 @@
|
||||||
|
local world = require("world")
|
||||||
local min = math.min
|
local min = math.min
|
||||||
|
|
||||||
World:addEntity({ gravity = -300 })
|
world:addEntity({ gravity = -300 })
|
||||||
|
|
||||||
local gravities = filteredSystem("gravities", { gravity = T.number })
|
local gravities = world:filteredSystem("gravities", { gravity = T.number })
|
||||||
|
|
||||||
filteredSystem("changeGravity", { changeGravityTo = T.number }, function(e, _, _)
|
world:filteredSystem("changeGravity", { changeGravityTo = T.number }, function(e, _, _)
|
||||||
for _, ge in pairs(gravities.entities) do
|
for _, ge in pairs(gravities.entities) do
|
||||||
ge.gravity = e.changeGravityTo
|
ge.gravity = e.changeGravityTo
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
filteredSystem("fall", { velocity = T.XyPair, mass = T.number }, function(e, dt)
|
world:filteredSystem("fall", { velocity = T.XyPair, mass = T.number }, function(e, dt)
|
||||||
for _, ge in pairs(gravities.entities) do
|
for _, ge in pairs(gravities.entities) do
|
||||||
e.velocity.y = min(400, e.velocity.y - (ge.gravity * dt * e.mass) - (0.5 * dt * dt))
|
e.velocity.y = min(400, e.velocity.y - (ge.gravity * dt * e.mass) - (0.5 * dt * dt))
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
|
local world = require("world")
|
||||||
|
|
||||||
---@type KeyState
|
---@type KeyState
|
||||||
local keyState = {}
|
local keyState = {}
|
||||||
|
|
||||||
local keyInputSystem = filteredSystem("keyInput", { canReceiveKeys = T.marker }, function(e, _, system)
|
local keyInputSystem = world:filteredSystem("keyInput", { canReceiveKeys = T.marker }, function(e, _, system)
|
||||||
e.keyState = keyState
|
e.keyState = keyState
|
||||||
system.world:addEntity(e)
|
system.world:addEntity(e)
|
||||||
end)
|
end)
|
||||||
|
@ -22,7 +24,7 @@ function ClearKeyState()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local mouse = filteredSystem("mouse", { mouseKeyPress = { position = T.XyPair, key = T.number } })
|
local mouse = world:filteredSystem("mouse", { mouseKeyPress = { position = T.XyPair, key = T.number } })
|
||||||
|
|
||||||
function MouseJustPressed(key, clear)
|
function MouseJustPressed(key, clear)
|
||||||
clear = clear ~= nil and clear or true
|
clear = clear ~= nil and clear or true
|
||||||
|
@ -30,7 +32,7 @@ function MouseJustPressed(key, clear)
|
||||||
if event.mouseKeyPress and event.mouseKeyPress.key == key then
|
if event.mouseKeyPress and event.mouseKeyPress.key == key then
|
||||||
if clear then
|
if clear then
|
||||||
event.mouseKeyPress = nil
|
event.mouseKeyPress = nil
|
||||||
World:removeEntity(event)
|
world:removeEntity(event)
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
@ -45,7 +47,7 @@ function love.mousemoved(x, y)
|
||||||
end
|
end
|
||||||
|
|
||||||
function love.mousepressed(x, y, key)
|
function love.mousepressed(x, y, key)
|
||||||
World:addEntity({
|
world:addEntity({
|
||||||
mouseKeyPress = {
|
mouseKeyPress = {
|
||||||
position = {
|
position = {
|
||||||
x = x,
|
x = x,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
|
local world = require("world")
|
||||||
local sqrt = math.sqrt
|
local sqrt = math.sqrt
|
||||||
|
|
||||||
filteredSystem("velocity", { position = T.XyPair, velocity = T.XyPair }, function(e, dt, system)
|
world:filteredSystem("velocity", { position = T.XyPair, velocity = T.XyPair }, function(e, dt, system)
|
||||||
if sqrt((e.velocity.x * e.velocity.x) + (e.velocity.y * e.velocity.y)) < 2 then
|
if sqrt((e.velocity.x * e.velocity.x) + (e.velocity.y * e.velocity.y)) < 2 then
|
||||||
-- velocity = nil
|
-- velocity = nil
|
||||||
else
|
else
|
||||||
|
@ -9,8 +10,8 @@ filteredSystem("velocity", { position = T.XyPair, velocity = T.XyPair }, functio
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
filteredSystem("drag", { velocity = T.XyPair, drag = T.number }, function(e, dt, system)
|
world:filteredSystem("drag", { velocity = T.XyPair, drag = T.number }, function(e, dt, system)
|
||||||
local currentDrag = e.drag * dt
|
local currentDrag = e.drag * dt
|
||||||
e.velocity.x = e.velocity.x - (e.velocity.x * currentDrag * dt)
|
e.velocity.x = e.velocity.x - (e.velocity.x * currentDrag * dt)
|
||||||
e.velocity.y = e.velocity.y - (e.velocity.y * currentDrag * dt)
|
e.velocity.y = e.velocity.y - (e.velocity.y * currentDrag * dt)
|
||||||
end)
|
end)
|
||||||
|
|
|
@ -39,4 +39,4 @@ if tinyWarnWhenNonDataOnEntities then
|
||||||
return valType
|
return valType
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,38 +0,0 @@
|
||||||
local tiny = require("lib/tiny")
|
|
||||||
|
|
||||||
---@generic T
|
|
||||||
---@param shape T | fun()
|
|
||||||
---@param process fun(entity: T, dt: number, system: System) | nil
|
|
||||||
---@param compare nil | fun(system: System, entityA: T, entityB: T): boolean
|
|
||||||
---@return System | { entities: T[] }
|
|
||||||
function filteredSystem(name, shape, process, compare)
|
|
||||||
assert(type(name) == "string")
|
|
||||||
assert(type(shape) == "table" or type(shape) == "function")
|
|
||||||
assert(process == nil or type(process) == "function")
|
|
||||||
|
|
||||||
local system = compare and tiny.sortedProcessingSystem() or tiny.processingSystem()
|
|
||||||
system.compare = compare
|
|
||||||
system.name = name
|
|
||||||
if type(shape) == "table" then
|
|
||||||
local keys = {}
|
|
||||||
for key, value in pairs(shape) do
|
|
||||||
local isTable = type(value) == "table"
|
|
||||||
local isMaybe = isTable and value.maybe ~= nil
|
|
||||||
|
|
||||||
if not isMaybe then
|
|
||||||
-- ^ Don't require any Maybe types
|
|
||||||
keys[#keys + 1] = key
|
|
||||||
end
|
|
||||||
end
|
|
||||||
system.filter = tiny.requireAll(unpack(keys))
|
|
||||||
elseif type(shape) == "function" then
|
|
||||||
system.filter = shape
|
|
||||||
end
|
|
||||||
if not process then
|
|
||||||
return World:addSystem(system)
|
|
||||||
end
|
|
||||||
function system:process(e, dt)
|
|
||||||
process(e, dt, self)
|
|
||||||
end
|
|
||||||
return World:addSystem(system)
|
|
||||||
end
|
|
|
@ -46,3 +46,10 @@ function World:getSystemCount() end
|
||||||
--- the order in which they Systems processed, because lower indexed Systems are
|
--- the order in which they Systems processed, because lower indexed Systems are
|
||||||
--- processed first. Returns the old system.index.
|
--- processed first. Returns the old system.index.
|
||||||
function World:setSystemIndex(world, system, index) end
|
function World:setSystemIndex(world, system, index) end
|
||||||
|
|
||||||
|
---@generic T
|
||||||
|
---@param shape T | fun()
|
||||||
|
---@param process fun(entity: T, dt: number, system: System) | nil
|
||||||
|
---@param compare nil | fun(system: System, entityA: T, entityB: T): boolean
|
||||||
|
---@return System | { entities: T[] }
|
||||||
|
function World:filteredSystem(name, shape, process, compare) end
|
||||||
|
|
Loading…
Reference in New Issue