feat(world): add initial fake3D world implementation
This commit is contained in:
parent
a60058522b
commit
2004efa558
4 changed files with 484 additions and 0 deletions
|
@ -27,6 +27,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
- **camera:** Add two new camera types: "middle" and "zoom".
|
||||
|
||||
- **world:** Add a fake 3D world, à la Zelda or BeatThemAll
|
||||
|
||||
### Changed
|
||||
|
||||
- **world2D:** Use a list for bodies (hitboxes, etc) and one other for actors
|
||||
|
|
209
gamecore/modules/world/actors/actor3D.lua
Normal file
209
gamecore/modules/world/actors/actor3D.lua
Normal file
|
@ -0,0 +1,209 @@
|
|||
-- actor3D.lua :: the implementation of a 2D actor. It contain every element
|
||||
-- needed to create your own 2D actors.
|
||||
|
||||
--[[
|
||||
Copyright © 2019 Kazhnuz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
]]
|
||||
|
||||
local cwd = (...):gsub('%.actor3D$', '') .. "."
|
||||
local BaseActor = require(cwd .. "baseactor")
|
||||
local Actor3D = BaseActor:extend()
|
||||
|
||||
local Hitbox = require(cwd .. "utils.hitbox3D")
|
||||
|
||||
-- INIT FUNCTIONS
|
||||
-- Initialise the actor and its base functions
|
||||
|
||||
function Actor3D:new(world, type, x, y, z, w, h, d, isSolid)
|
||||
Actor3D.super.new(self, world, type, x, y, z, w, h, d, isSolid)
|
||||
self:initHitboxes()
|
||||
end
|
||||
|
||||
function Actor3D:destroy()
|
||||
self.world:removeActor(self)
|
||||
self.mainHitbox:destroy()
|
||||
self.isDestroyed = true
|
||||
end
|
||||
|
||||
-- PHYSICS FUNCTIONS
|
||||
-- Handle movement and collisions.
|
||||
|
||||
function Actor3D:autoMove(dt)
|
||||
self:updateHitboxes()
|
||||
self.onGround = false
|
||||
self:applyGravity(dt)
|
||||
|
||||
local dx, dy, dz = self:getFuturePosition(dt)
|
||||
local newx, newy, newz, cols, colNumber = self:move(dx, dy, dz)
|
||||
|
||||
-- apply after the movement the friction, until the player stop
|
||||
-- note: the friction is applied according to the delta time,
|
||||
-- thus the friction should be how much speed is substracted in 1 second
|
||||
|
||||
self:solveAllCollisions(cols)
|
||||
|
||||
self:applyFriction(dt)
|
||||
end
|
||||
|
||||
function Actor3D:changeSpeedToCollisionNormal(normal)
|
||||
local xsp, ysp, zsp = self.xsp, self.ysp, self.zsp
|
||||
local nx, ny, nz = normal.x, normal.y, normal.z
|
||||
|
||||
if (nx < 0 and xsp > 0) or (nx > 0 and xsp < 0) then
|
||||
xsp = -xsp * self.bounceFactor
|
||||
end
|
||||
|
||||
if (ny < 0 and ysp > 0) or (ny > 0 and ysp < 0) then
|
||||
ysp = -ysp * self.bounceFactor
|
||||
end
|
||||
|
||||
if (nz < 0 and zsp > 0) or (nz > 0 and zsp < 0) then
|
||||
zsp = -zsp * self.bounceFactor
|
||||
end
|
||||
|
||||
self.xsp, self.ysp, self.zsp = xsp, ysp, zsp
|
||||
end
|
||||
|
||||
function Actor3D:move(dx, dy, dz)
|
||||
local cols, colNumber = {}, 0
|
||||
if (self.isDestroyed == false) then
|
||||
self.x, self.y, self.z, cols, colNumber = self.mainHitbox:checkCollision(dx, dy, dz, self.filter)
|
||||
self.mainHitbox:updatePosition()
|
||||
end
|
||||
return self.x, self.y, self.z, cols, colNumber
|
||||
end
|
||||
|
||||
function Actor3D:checkCollision(dx, dy, dz)
|
||||
local x, y, z, cols, colNumber = dx, dy, dz, {}, 0
|
||||
if (self.isDestroyed == false) then
|
||||
x, y, z, cols, colNumber = self.mainHitbox:checkCollision(dx, dy, dz, self.filter)
|
||||
end
|
||||
return self.x, self.y, self.z, cols, colNumber
|
||||
end
|
||||
|
||||
-- GRAVITY SYSTEM FUNCTIONS
|
||||
-- All functions related to gravity
|
||||
|
||||
function Actor3D:applyGravity(dt)
|
||||
self.zsp = self.zsp - (self.grav * dt)
|
||||
|
||||
if utils.math.sign(self.zsp) == utils.math.sign(self.grav) then
|
||||
self:checkGround( )
|
||||
end
|
||||
end
|
||||
|
||||
function Actor3D:checkGround()
|
||||
local dx, dy, dz = self.x, self.y, self.z + utils.math.sign(self.grav)
|
||||
local newx, newy, newz, cols, colNumber = self:checkCollision(dx, dy, dz)
|
||||
|
||||
for i, col in ipairs(cols) do
|
||||
if (col.type == "touch") or (col.type == "bounce") or (col.type == "slide") then
|
||||
if not (self.grav == 0) then
|
||||
if col.normal.z ~= utils.math.sign(self.grav) then self.onGround = true end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- COORDINATE/MOVEMENT FUNCTIONS
|
||||
-- Handle coordinate
|
||||
|
||||
function Actor3D:getViewCenter()
|
||||
local x, y, z = self:getCenter()
|
||||
return x, y - (self.d/2)
|
||||
end
|
||||
|
||||
-- HITBOXES FUNCTIONS
|
||||
-- Functions related to actor hitboxes
|
||||
|
||||
function Actor3D:addHitboxFromFrameData(framedata, animationID, frameID, hitboxID)
|
||||
local sx, sy = self:getSpriteScalling()
|
||||
local type = framedata[1]
|
||||
local ox = framedata[2]
|
||||
local oy = framedata[3]
|
||||
local oz = framedata[4]
|
||||
local w = framedata[5]
|
||||
local h = framedata[6]
|
||||
local d = framedata[7]
|
||||
local isSolid = framedata[8] or false
|
||||
local anim = animationID or "null"
|
||||
local frame = frameID or 0
|
||||
local id = hitboxID or 0
|
||||
if (sx < 0) then
|
||||
ox = self.w - ox - w
|
||||
end
|
||||
if (sy < 0) then
|
||||
oz = self.d - oz - d
|
||||
end
|
||||
|
||||
if (type == "main") then
|
||||
self.mainHitbox:modify(ox, oy, oz, w, h, d)
|
||||
else
|
||||
local hitboxName = anim .. frame .. type .. id
|
||||
self:addHitbox(hitboxName, type, ox, oy, oz, w, h, d, isSolid)
|
||||
return hitboxName
|
||||
end
|
||||
end
|
||||
|
||||
function Actor3D:initMainHitbox()
|
||||
self.mainHitbox = Hitbox(self, self.type, 0, 0, 0, self.w, self.h, self.d, self.isSolid)
|
||||
self.mainHitbox:advertiseAsMainHitbox()
|
||||
end
|
||||
|
||||
function Actor3D:addHitbox(name, type, ox, oy, oz, w, h, d, isSolid)
|
||||
if (self.hitboxes[name] ~= nil) then
|
||||
print("ERROR:", "The hitbox " .. name .. " already exists")
|
||||
else
|
||||
local hitbox = Hitbox(self, type, ox, oy, oz, w, h, d, isSolid)
|
||||
self.hitboxes[name] = hitbox
|
||||
return hitbox
|
||||
end
|
||||
end
|
||||
|
||||
function Actor3D:checkHitboxCollisions(name, filter)
|
||||
self:checkHitboxCollisionsAtPoint(name, self.x, self.y, self.z, filter)
|
||||
end
|
||||
|
||||
function Actor3D:checkHitboxCollisionsAtPoint(name, dx, dy, dz, filter)
|
||||
local x, y, z, cols, colNumber = dx, dy, dz, {}, 0
|
||||
local filter = filter or self.filter
|
||||
if (self.isDestroyed == false) and (self.hitboxes[name] ~= nil) then
|
||||
x, y, z, cols, colNumber = self.hitboxes[name]:checkCollision(dx, dy, dz, filter)
|
||||
local type = self.hitboxes[name].type
|
||||
|
||||
for i, col in ipairs(cols) do
|
||||
self:hitboxResponse(name, type, col)
|
||||
end
|
||||
end
|
||||
|
||||
return x, y, z, cols, colNumber
|
||||
end
|
||||
|
||||
-- DRAW FUNCTIONS
|
||||
-- Draw the actors.
|
||||
|
||||
function Actor3D:draw()
|
||||
self:drawStart()
|
||||
local x, y = math.floor(self.x), math.floor(self.y - self.z)
|
||||
self:drawSprite(x, y)
|
||||
self:drawEnd()
|
||||
end
|
||||
|
||||
return Actor3D
|
129
gamecore/modules/world/actors/utils/hitbox3D.lua
Normal file
129
gamecore/modules/world/actors/utils/hitbox3D.lua
Normal file
|
@ -0,0 +1,129 @@
|
|||
-- hitbox3D.lua :: a basic 3D hitbox object. It's used by the actors to check
|
||||
-- collisions and to handle different type of responses.
|
||||
|
||||
--[[
|
||||
Copyright © 2019 Kazhnuz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
]]
|
||||
|
||||
local Hitbox3D = Object:extend()
|
||||
|
||||
-- INIT FUNCTIONS
|
||||
-- Initialise the actor and its base functions
|
||||
|
||||
function Hitbox3D:new(owner, type, ox, oy, oz, w, h, d, isSolid)
|
||||
self.owner = owner
|
||||
self.world = owner.world
|
||||
|
||||
self.type = type
|
||||
self.ox = ox
|
||||
self.oy = oy
|
||||
self.oz = oz
|
||||
self.x, self.y, self.z = self:getPosition()
|
||||
self.w = w
|
||||
self.h = h
|
||||
self.d = d
|
||||
self.isSolid = isSolid
|
||||
|
||||
self.isMainHitBox = false
|
||||
|
||||
self:setDebugColor(0,0,0)
|
||||
self:register()
|
||||
end
|
||||
|
||||
function Hitbox3D:advertiseAsMainHitbox()
|
||||
self.isMainHitBox = true
|
||||
end
|
||||
|
||||
function Hitbox3D:modify(ox, oy, oz, w, h, d)
|
||||
self.ox = ox
|
||||
self.oy = oy
|
||||
self.oz = oz
|
||||
self.x, self.y, self.z = self:getPosition()
|
||||
self.w = w
|
||||
self.h = h
|
||||
self.d = d
|
||||
end
|
||||
|
||||
function Hitbox3D:setDebugColor(r,g,b)
|
||||
self.debug = {}
|
||||
self.debug.r = r
|
||||
self.debug.g = g
|
||||
self.debug.b = b
|
||||
end
|
||||
|
||||
function Hitbox3D:register()
|
||||
self.world:registerBody(self)
|
||||
end
|
||||
|
||||
function Hitbox3D:destroy()
|
||||
self.world:removeBody(self)
|
||||
end
|
||||
|
||||
-- COORDINATE FUNCTIONS
|
||||
-- Handle Hitbox position
|
||||
|
||||
function Hitbox3D:updatePosition()
|
||||
self.x, self.y, self.z = self:getPosition()
|
||||
self.world:updateBody(self)
|
||||
return self.x, self.y, self.z
|
||||
end
|
||||
|
||||
function Hitbox3D:getPosition()
|
||||
return self.ox + self.owner.x, self.oy + self.owner.y, self.oz + self.owner.z
|
||||
end
|
||||
|
||||
function Hitbox3D:getOwnerPosition()
|
||||
return self.x - self.ox, self.y - self.oy, self.z - self.oz
|
||||
end
|
||||
|
||||
function Hitbox3D:getNewOwnerPosition(x, y, z)
|
||||
return x - self.ox, y - self.oy, z - self.oz
|
||||
end
|
||||
|
||||
function Hitbox3D:getCenter()
|
||||
return self.x + (self.w/2), self.y + (self.h/2), self.z + (self.d/2)
|
||||
end
|
||||
|
||||
-- COLLISION FUNCTIONS
|
||||
-- Handle Hitbox position
|
||||
|
||||
function Hitbox3D:checkCollision(dx, dy, dz, filter)
|
||||
self:updatePosition()
|
||||
|
||||
local dx, dy = self.ox + dx, self.oy + dy, self.oz + dz
|
||||
local x, y, z, cols, colNumber = self.world:checkCollision(self, dx, dy, dz, filter)
|
||||
local newx, newy, newz = self:getNewOwnerPosition(x, y, z)
|
||||
|
||||
return newx, newy, newz, cols, colNumber
|
||||
end
|
||||
|
||||
-- DRAW FUNCTIONS
|
||||
-- Just some debug function to draw hitbox
|
||||
|
||||
function Hitbox3D:draw()
|
||||
local x, y, z = self:getPosition()
|
||||
love.graphics.setColor(self.debug.r, self.debug.g, self.debug.b, 1)
|
||||
utils.graphics.box(x, (y-z) - (self.d), self.w, self.h)
|
||||
love.graphics.setColor(self.debug.r/2, self.debug.g/2, self.debug.b/2, 1)
|
||||
utils.graphics.box(x, (y-z) - (self.d) + (self.h), self.w, self.d)
|
||||
utils.graphics.resetColor()
|
||||
end
|
||||
|
||||
return Hitbox3D
|
144
gamecore/modules/world/world3D.lua
Normal file
144
gamecore/modules/world/world3D.lua
Normal file
|
@ -0,0 +1,144 @@
|
|||
-- world3D.lua :: a basic fake3D world based on bump-2dpd.
|
||||
|
||||
--[[
|
||||
Copyright © 2019 Kazhnuz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
]]
|
||||
|
||||
local cwd = (...):gsub('%.world3D$', '') .. "."
|
||||
|
||||
local BaseWorld = require(cwd .. "baseworld")
|
||||
local World3D = BaseWorld:extend()
|
||||
|
||||
local Sti = require(cwd .. "libs.sti")
|
||||
local Bump = require(cwd .. "libs.bump")
|
||||
local Bump3D = require(cwd .. "libs.bump-3dpd")
|
||||
local CameraSystem = require(cwd .. "camera")
|
||||
|
||||
function World3D:new(scene, actorlist, mapfile)
|
||||
World3D.super.new(self, scene, actorlist, mapfile)
|
||||
end
|
||||
|
||||
-- ACTORS FUNCTIONS
|
||||
-- Add support for bodies in Actor functions
|
||||
|
||||
function World3D:initActors()
|
||||
self.currentCreationID = 0
|
||||
self.actors = {}
|
||||
self.bodies = Bump3D.newWorld(50)
|
||||
end
|
||||
|
||||
function World3D:newActor(name, x, y, z)
|
||||
self.obj.index[name](self, x, y, z)
|
||||
end
|
||||
|
||||
function World3D:newCollision(name, x, y, z, w, h, d)
|
||||
self.obj.collisions[name](self, x, y, z, w, h, d)
|
||||
end
|
||||
|
||||
function World3D:moveActor(actor, x, y, z, filter)
|
||||
return self.bodies:move(actor.mainHitbox, x, y, z, filter)
|
||||
end
|
||||
|
||||
function World3D:getActorsInRect(x, y, w, h)
|
||||
-- Just a placeholder before adding a better algorythm
|
||||
World3D.super.getActorsInRect(x, y, w, h)
|
||||
end
|
||||
|
||||
function World3D:getVisibleActors(id)
|
||||
|
||||
return self.actors
|
||||
end
|
||||
|
||||
-- PLAYER FUNCTIONS
|
||||
-- Load player stuff
|
||||
|
||||
function World3D:addPlayer(actor, sourceid, haveCam)
|
||||
local player = {}
|
||||
player.actor = actor
|
||||
player.sourceid = sourceid or 1
|
||||
|
||||
table.insert(self.players, player)
|
||||
|
||||
self.cameras:addTarget(player.actor)
|
||||
end
|
||||
|
||||
-- MAP LOADING FUNCTIONS
|
||||
-- Handle loading of actors from map
|
||||
|
||||
function World3D:batchActor(objectlayer, object)
|
||||
local name = objectlayer.name
|
||||
local gwidth = object.properties.gwidth or self.map.tilewidth
|
||||
local gheight = object.properties.gheight or self.map.tileheight
|
||||
local x = object.x
|
||||
local y = object.y
|
||||
local z = object.properties.z or 0
|
||||
local w = object.width
|
||||
local h = object.height
|
||||
|
||||
local cellHor = math.ceil(w / gwidth)
|
||||
local cellVert = math.ceil(h / gheight)
|
||||
|
||||
for i=1, cellHor do
|
||||
for j=1, cellVert do
|
||||
self:newActor(name, x + (i-1)*gwidth, y + (j-1)*gheight, z)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function World3D:newActorFromMap(objectlayer, object)
|
||||
local z = object.properties.z or 0
|
||||
self:newActor(objectlayer.name, object.x, object.y, z)
|
||||
end
|
||||
|
||||
function World3D:newCollisionFromMap(objectlayer, object)
|
||||
local z = object.properties.z or 0
|
||||
local d = object.properties.d or 16
|
||||
self:newCollision(objectlayer.name, object.x, object.y, z, object.width, object.height, d)
|
||||
end
|
||||
|
||||
function World3D:addPlayerFromMap(object, i)
|
||||
local z = object.properties.z or 0
|
||||
self:addPlayer(self.obj.Player(self, object.x, object.y, z), i)
|
||||
end
|
||||
|
||||
-- BODIES MANAGEMENT FUNCTIONS
|
||||
-- Basic function to handle bodies. Wrappers around Bump2D functions
|
||||
|
||||
function World3D:registerBody(body)
|
||||
return self.bodies:add(body, body.x, body.y, body.z, body.w, body.h, body.d)
|
||||
end
|
||||
|
||||
function World3D:updateBody(body)
|
||||
return self.bodies:update(body, body.x, body.y, body.z, body.w, body.h, body.d)
|
||||
end
|
||||
|
||||
function World3D:removeBody(body)
|
||||
return self.bodies:remove(body)
|
||||
end
|
||||
|
||||
function World3D:checkCollision(body, x, y, z, filter)
|
||||
return self.bodies:check(body, x, y, z, filter)
|
||||
end
|
||||
|
||||
function World3D:getBodiesInRect(x, y, w, h)
|
||||
return {} --self.bodies:queryRect(x, y, w, h)
|
||||
end
|
||||
|
||||
return World3D
|
Loading…
Reference in a new issue