Compare commits

..

No commits in common. "aca14fbfb0af484d76f841bffdbcf1470cddeef2" and "e2ffe2d552c33290cfd5bc46a20744f5920b1cea" have entirely different histories.

14 changed files with 97 additions and 107 deletions

View File

@ -905,53 +905,6 @@ 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 = {
@ -968,7 +921,6 @@ 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>"

View File

@ -1,17 +1,20 @@
require("tiny-debug") require("tiny-debug")
Tiny = require("lib/tiny")
require("utils") require("utils")
require("tiny-tools")
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!",
@ -38,7 +41,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
@ -51,7 +54,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
@ -60,14 +63,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

View File

@ -1,18 +1,15 @@
local world = require("world") Camera = {
local camera = {
pan = { pan = {
x = 0, x = 0,
y = 0, y = 0,
}, },
} }
local expireBelowScreen = expireBelowScreenSystem = filteredSystem("expireBelowScreen", { position = T.XyPair, expireBelowScreenBy = T.number })
world:filteredSystem("expireBelowScreen", { position = T.XyPair, expireBelowScreenBy = T.number })
local focusPriority = {} local focusPriority = {}
cameraPanSystem = world:filteredSystem("cameraPan", { focusPriority = T.number, position = T.XyPair }, function(e, _) cameraPanSystem = 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
@ -24,13 +21,35 @@ 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)
love.graphics.translate(0, -camera.pan.y) -- TODO: set draw offset
for _, entity in pairs(expireBelowScreen.entities) do for _, entity in pairs(expireBelowScreenSystem.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

View File

@ -1,6 +1,4 @@
local world = require("world") collidingEntities = filteredSystem("collidingEntitites", {
local collidingEntities = world:filteredSystem("collidingEntitites", {
position = T.XyPair, position = T.XyPair,
size = T.XyPair, size = T.XyPair,
canCollideWith = T.BitMask, canCollideWith = T.BitMask,
@ -21,7 +19,7 @@ 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
world:filteredSystem( 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*

View File

@ -1,6 +1,4 @@
local world = require("world") filteredSystem("collisionResolution", { collisionBetween = T.Collision }, function(e, _, system)
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)

View File

@ -1,13 +1,11 @@
local world = require("world") filteredSystem("decay", { decayAfterSeconds = T.number }, function(e, dt, system)
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 = world:filteredSystem("liveForNFrames", { liveForNFrames = T.number }, function(e, _, system) LiveForNFrames = 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)

View File

@ -1,4 +1,3 @@
local world = require("world")
local gfx = love.graphics local gfx = love.graphics
---@generic T ---@generic T
@ -6,7 +5,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 = world:filteredSystem(name, shape, process, function(_, a, b) local system = 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

View File

@ -1,17 +1,16 @@
local world = require("world")
local min = math.min local min = math.min
world:addEntity({ gravity = -300 }) World:addEntity({ gravity = -300 })
local gravities = world:filteredSystem("gravities", { gravity = T.number }) local gravities = filteredSystem("gravities", { gravity = T.number })
world:filteredSystem("changeGravity", { changeGravityTo = T.number }, function(e, _, _) 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)
world:filteredSystem("fall", { velocity = T.XyPair, mass = T.number }, function(e, dt) 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

View File

@ -1,9 +1,7 @@
local world = require("world")
---@type KeyState ---@type KeyState
local keyState = {} local keyState = {}
local keyInputSystem = world:filteredSystem("keyInput", { canReceiveKeys = T.marker }, function(e, _, system) local keyInputSystem = filteredSystem("keyInput", { canReceiveKeys = T.marker }, function(e, _, system)
e.keyState = keyState e.keyState = keyState
system.world:addEntity(e) system.world:addEntity(e)
end) end)
@ -24,7 +22,7 @@ function ClearKeyState()
end end
end end
local mouse = world:filteredSystem("mouse", { mouseKeyPress = { position = T.XyPair, key = T.number } }) local mouse = 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
@ -32,7 +30,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
@ -47,7 +45,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,

View File

@ -1,7 +1,6 @@
local world = require("world")
local sqrt = math.sqrt local sqrt = math.sqrt
world:filteredSystem("velocity", { position = T.XyPair, velocity = T.XyPair }, function(e, dt, system) 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
@ -10,7 +9,7 @@ world:filteredSystem("velocity", { position = T.XyPair, velocity = T.XyPair }, f
end end
end) end)
world:filteredSystem("drag", { velocity = T.XyPair, drag = T.number }, function(e, dt, system) 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)

36
tiny-tools.lua Normal file
View File

@ -0,0 +1,36 @@
---@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

View File

@ -46,10 +46,3 @@ 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

View File

@ -1,2 +0,0 @@
local tiny = require("lib/tiny")
return tiny.world()