added breezefield as a dep, needed to edit some stuff to make it work but it works

This commit is contained in:
2021-05-08 11:42:13 +02:00
parent 93efcd6829
commit f96a1e19ec
4 changed files with 362 additions and 0 deletions

68
deps/breezefield/collider.lua vendored Normal file
View File

@@ -0,0 +1,68 @@
-- a Collider object, wrapping shape, body, and fixtue
local set_funcs, lp, lg, COLLIDER_TYPES = unpack(require("deps.breezefield.utils")) -- it me who put deps.breezefield
local Collider = {}
Collider.__index = Collider
function Collider.new(world, collider_type, ...)
-- deprecated
return world:newCollider(collider_type, {...})
end
function Collider:draw_type()
if self.collider_type == 'Edge' or self.collider_type == 'Chain' then
return 'line'
end
return self.collider_type:lower()
end
function Collider:__draw__()
self._draw_type = self._draw_type or self:draw_type()
local args
if self._draw_type == 'line' then
args = {self:getSpatialIdentity()}
else
args = {'line', self:getSpatialIdentity()}
end
love.graphics[self:draw_type()](unpack(args))
end
function Collider:draw()
self:__draw__()
end
function Collider:destroy()
self._world.colliders[self] = nil
self.fixture:setUserData(nil)
self.fixture:destroy()
self.body:destroy()
end
function Collider:getSpatialIdentity()
if self.collider_type == 'Circle' then
return self:getX(), self:getY(), self:getRadius()
else
return self:getWorldPoints(self:getPoints())
end
end
function Collider:collider_contacts()
local contacts = self:getContacts()
local colliders = {}
for i, contact in ipairs(contacts) do
if contact:isTouching() then
local f1, f2 = contact:getFixtures()
if f1 == self.fixture then
colliders[#colliders+1] = f2:getUserData()
else
colliders[#colliders+1] = f1:getUserData()
end
end
end
return colliders
end
return Collider

24
deps/breezefield/init.lua vendored Normal file
View File

@@ -0,0 +1,24 @@
-- breezefield: init.lua
--[[
implements Collider and World objects
Collider wraps the basic functionality of shape, fixture, and body
World wraps world, and provides automatic drawing simplified collisions
]]--
local bf = {}
local BASE = (...) .. "."
local Collider = require(BASE .. 'collider')
local World = require(BASE .. 'world')
function bf.newWorld(...)
return bf.World:new(...)
end
bf.Collider = Collider
bf.World = World
return bf

32
deps/breezefield/utils.lua vendored Normal file
View File

@@ -0,0 +1,32 @@
-- function used for both
local function set_funcs(mainobject, subobject)
-- this function assigns functions of a subobject to a primary object
--[[
mainobject: the table to which to assign the functions
subobject: the table whose functions to assign
no output
--]]
for k, v in pairs(subobject.__index) do
if k ~= '__gc' and k ~= '__eq' and k ~= '__index'
and k ~= '__tostring' and k ~= 'destroy' and k ~= 'type'
and k ~= 'typeOf'and k ~= 'getUserData' and k ~= 'setUserData' then
mainobject[k] = function(mainobject, ...)
return v(subobject, ...)
end
end
end
end
local COLLIDER_TYPES = {
CIRCLE = "Circle",
CIRC = "Circle",
RECTANGLE = "Rectangle",
RECT = "Rectangle",
POLYGON = "Polygon",
POLY = "Polygon",
EDGE = 'Edge',
CHAIN = 'Chain'
}
return {set_funcs, love.physics, love.graphics, COLLIDER_TYPES}

238
deps/breezefield/world.lua vendored Normal file
View File

@@ -0,0 +1,238 @@
-- breezefield: World.lua
--[[
World: has access to all the functions of love.physics.world
additionally stores all Collider objects assigned to it in
self.colliders (as key-value pairs)
can draw all its Colliders
by default, calls :collide on any colliders in it for postSolve
or for beginContact if the colliders are sensors
--]]
-- TODO make updating work from here too
-- TODO: update test and tutorial
local Collider = require('deps.breezefield.collider') -- it me who put deps.breezefield
local set_funcs, lp, lg, COLLIDER_TYPES = unpack(require("deps.breezefield.utils")) -- it me who put deps.breezefield
local World = {}
World.__index = World
function World:new(...)
-- create a new physics world
--[[
inputs: (same as love.physics.newWorld)
xg: float, gravity in x direction
yg: float, gravity in y direction
sleep: boolean, whether bodies can sleep
outputs:
w: bf.World, the created world
]]--
local w = {}
setmetatable(w, self)
w._world = lp.newWorld(...)
set_funcs(w, w._world)
w.update = nil -- to use our custom update
w.colliders = {}
-- some functions defined here to use w without being passed it
function w.collide(obja, objb, coll_type, ...)
-- collision event for two Colliders
local function run_coll(obj1, obj2, ...)
if obj1[coll_type] ~= nil then
local e = obj1[coll_type](obj1, obj2, ...)
if type(e) == 'function' then
w.collide_events[#w.collide_events+1] = e
end
end
end
if obja ~= nil and objb ~= nil then
run_coll(obja, objb, ...)
run_coll(objb, obja, ...)
end
end
function w.enter(a, b, ...)
return w.collision(a, b, 'enter', ...)
end
function w.exit(a, b, ...)
return w.collision(a, b, 'exit', ...)
end
function w.preSolve(a, b, ...)
return w.collision(a, b, 'preSolve', ...)
end
function w.postSolve(a, b, ...)
return w.collision(a, b, 'postSolve', ...)
end
function w.collision(a, b, ...)
-- objects that hit one another can have collide methods
-- by default used as postSolve callback
local obja = a:getUserData(a)
local objb = b:getUserData(b)
w.collide(obja, objb, ...)
end
w:setCallbacks(w.enter, w.exit, w.preSolve, w.postSolve)
w.collide_events = {}
return w
end
function World:draw(alpha, draw_over)
-- draw the world
--[[
alpha: sets the alpha of the drawing, defaults to 1
draw_over: draws the collision objects shapes even if their
.draw method is overwritten
--]]
local color = {love.graphics.getColor()}
for _, c in pairs(self.colliders) do
love.graphics.setColor(1, 1, 1, alpha or 1)
c:draw(alpha)
if draw_over then
love.graphics.setColor(1, 1, 1, alpha or 1)
c:__draw__()
end
end
love.graphics.setColor(color)
end
function World:queryRectangleArea(x1, y1, x2, y2)
-- query a bounding-box aligned area for colliders
--[[
inputs:
x1, y1, x2, y2: floats, the x and y coordinates of two points
outputs:
colls: table, all colliders in bounding box
--]]
local colls = {}
local callback = function(fixture)
table.insert(colls, fixture:getUserData())
return true
end
self:queryBoundingBox(x1, y1, x2, y2, callback)
return colls
end
local function check_vertices(vertices)
if #vertices % 2 ~= 0 then
error('vertices must be a multiple of 2')
elseif #vertices < 4 then
error('must have at least 2 vertices with x and y each')
end
end
local function query_region(world, coll_type, args)
local collider = world:newCollider(coll_type, args)
collider:setSensor(true)
world:update(0)
local colls = collider:collider_contacts(collider)
collider:destroy()
return colls
end
function World:queryPolygonArea(...)
-- query an area enclosed by the lines connecting a series of points
--[[
inputs:
x1, y1, x2, y2, ... floats, the x and y positions defining polygon
outputs:
colls: table, all Colliders intersecting the area
--]]
local vertices = {...}
if type(vertices[1]) == 'table' then
vertices = vertices[1]
end
check_vertices(vertices)
return query_region(self, 'Polygon', vertices)
end
function World:queryCircleArea(x, y, r)
-- get all colliders in a circle are
--[[
inputs:
x, y, r: floats, x, y and radius of circle
outputs:
colls: table: colliders in area
]]--
return query_region(self, 'Circle', {x, y, r})
end
function World:queryEdgeArea(...)
-- get all colliders along a (series of) line(s)
--[[
inputs:
x1, y1, x2, y2, ... floats, the x and y positions defining lines
outpts:
colls: table: colliders intersecting these lines
--]]
local vertices = {...}
if type(vertices[1]) == 'table' then
vertices = vertices[1]
end
check_vertices(vertices)
return query_region(self, 'Edge', vertices)
end
function World:update(dt)
-- update physics world
self._world:update(dt)
for i, v in pairs(self.collide_events) do
v()
self.collide_events[i] = nil
end
end
--[[
create a new collider in this world
args:
collider_type (string): the type of the collider (not case seinsitive). any of:
circle, rectangle, polygon, edge, chain.
shape_arguments (table): arguments required to instantiate shape.
circle: {x, y, radius}
rectangle: {x, y, width height}
polygon/edge/chain: {x1, y1, x2, y2, ...}
table_to_use (optional, table): table to generate as the collider
]]--
function World:newCollider(collider_type, shape_arguments, table_to_use)
local o = table_to_use or {}
setmetatable(o, Collider)
-- note that you will need to set static vs dynamic later
local _collider_type = COLLIDER_TYPES[collider_type:upper()]
assert(_collider_type ~= nil, "unknown collider type: "..collider_type)
collider_type = _collider_type
if collider_type == 'Circle' then
local x, y, r = unpack(shape_arguments)
o.body = lp.newBody(self._world, x, y, "dynamic")
o.shape = lp.newCircleShape(r)
elseif collider_type == "Rectangle" then
local x, y, w, h = unpack(shape_arguments)
o.body = lp.newBody(self._world, x, y, "dynamic")
o.shape = lp.newRectangleShape(w, h)
collider_type = "Polygon"
else
o.body = lp.newBody(self._world, 0, 0, "dynamic")
o.shape = lp['new'..collider_type..'Shape'](unpack(shape_arguments))
end
o.collider_type = collider_type
o.fixture = lp.newFixture(o.body, o.shape, 1)
o.fixture:setUserData(o)
set_funcs(o, o.body)
set_funcs(o, o.shape)
set_funcs(o, o.fixture)
-- index by self for now
o._world = self
self.colliders[o] = o
return o
end
return World