added breezefield as a dep, needed to edit some stuff to make it work but it works
This commit is contained in:
68
deps/breezefield/collider.lua
vendored
Normal file
68
deps/breezefield/collider.lua
vendored
Normal 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
24
deps/breezefield/init.lua
vendored
Normal 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
32
deps/breezefield/utils.lua
vendored
Normal 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
238
deps/breezefield/world.lua
vendored
Normal 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
|
||||||
Reference in New Issue
Block a user