1
0
Fork 0
mirror of https://github.com/Reuh/ubiquitousse.git synced 2025-10-27 17:19:31 +00:00

Added uqt.input for ctrulua and other stuff

This commit is contained in:
Reuh 2016-12-25 21:05:10 +01:00
parent 42738cc7c9
commit c0db856b82
7 changed files with 430 additions and 81 deletions

View file

@ -3,7 +3,7 @@
--- Audio functions. --- Audio functions.
return { return {
--- Loads an audio file and returns the corresponding audio object. --- Loads an audio file and returns the corresponding audio object.
-- TODO: audio object doc -- TODO: audio object doc & API
-- @impl backend -- @impl backend
load = function(filepath) end load = function(filepath) end
} }

View file

@ -10,6 +10,7 @@ local version = "0.0.1"
local uqt = require((...):match("^(.-ubiquitousse)%.")) local uqt = require((...):match("^(.-ubiquitousse)%."))
local ctr = require("ctr") local ctr = require("ctr")
local gfx = require("ctr.gfx") local gfx = require("ctr.gfx")
local hid = require("ctr.hid")
-- Version compatibility warning -- Version compatibility warning
do do
@ -31,15 +32,20 @@ do
end end
-- Redefine all functions in tbl which also are in toAdd, so when used they call the old function (in tbl) and then the new (in toAdd). -- Redefine all functions in tbl which also are in toAdd, so when used they call the old function (in tbl) and then the new (in toAdd).
-- Functions with names prefixed by a exclamation mark will overwrite the old function.
local function add(tbl, toAdd) local function add(tbl, toAdd)
for k,v in pairs(toAdd) do for k,v in pairs(toAdd) do
local old = tbl[k] local old = tbl[k]
if k:sub(1,1) == "!" then
tbl[k] = v
else
tbl[k] = function(...) tbl[k] = function(...)
old(...) old(...)
return v(...) return v(...)
end end
end end
end end
end
-- uqt -- uqt
uqt.backend = "ctrulua" uqt.backend = "ctrulua"
@ -57,4 +63,263 @@ add(uqt.time, {
}) })
end end
-- uqt.input: TODO -- uqt.input
if uqt.input then
local keys = {}
local touchX, touchY, dTouchX, dTouchY
add(uqt.input, {
update = function()
hid.read()
keys = hid.keys()
local nTouchX, nTouchY = hid.touch()
dTouchX, dTouchY = nTouchX - touchX, nTouchY - touchY
touchX, touchY = nTouchX, nTouchY
end,
buttonDetector = function(...)
local ret = {}
for _,id in ipairs({...}) do
-- Keys
if id:match("^key%.") then
local key = id:match("^key%.(.+)$")
table.insert(ret, function()
return keys.held[key]
end)
else
error("Unknown button identifier: "..id)
end
end
return unpack(ret)
end,
axisDetector = function(...)
local ret = {}
for _,id in ipairs({...}) do
-- Binary axis
if id:match(".+%,.+") then
local d1, d2 = uqt.input.buttonDetector(id:match("^(.+)%,(.+)$"))
table.insert(ret, function()
local b1, b2 = d1(), d2()
if b1 and b2 then return 0
elseif b1 then return -1
elseif b2 then return 1
else return 0 end
end)
-- Touch movement
elseif id:match("^touch%.move%.") then
local axis, threshold = id:match("^touch%.move%.(.+)%%(.+)$")
if not axis then axis = id:match("^touch%.move%.(.+)$") end -- no threshold (=0)
threshold = tonumber(threshold) or 0
table.insert(ret, function()
local val, raw, max
if axis == "x" then
raw, max = dTouchX, gfx.BOTTOM_WIDTH
elseif axis == "y" then
raw, max = dTouchY, gfx.BOTTOM_HEIGHT
end
val = raw / max
return math.abs(val) > math.abs(threshold) and val or 0, raw, max
end)
-- Touch position
elseif id:match("^touch%.position%.") then
local axis, threshold = id:match("^touch%.position%.(.+)%%(.+)$")
if not axis then axis = id:match("^touch%.position%.(.+)$") end -- no threshold (=0)
threshold = tonumber(threshold) or 0
table.insert(ret, function()
local val, raw, max
if axis == "x" then
max = gfx.BOTTOM_WIDTH / 2 -- /2 because x=0,y=0 is the center of the screen (an axis value is in [-1,1])
raw = touchX - max
elseif axis == "y" then
max = gfx.BOTTOM_HEIGHT / 2
raw = touchY - max
end
val = raw / max
return math.abs(val) > math.abs(threshold) and val or 0, raw, max
end)
-- Circle pad axis
elseif id:match("^circle%.") then
local axis, threshold = id:match("^circle%.(.+)%%(.+)$")
if not axis then axis = id:match("^circle%.(.+)$") end -- no threshold (=0)
threshold = tonumber(threshold) or 0
table.insert(ret, function()
local x, y = hid.circle()
local val, raw, max = 0, 0, 156
if axis == "x" then raw = x
elseif axis == "y" then raw = y end
val = raw / max
return math.abs(val) > math.abs(threshold) and val or 0, raw, max
end)
-- C-Stick axis
elseif id:match("^cstick%.") then
local axis, threshold = id:match("^cstick%.(.+)%%(.+)$")
if not axis then axis = id:match("^cstick%.(.+)$") end -- no threshold (=0)
threshold = tonumber(threshold) or 0
table.insert(ret, function()
local x, y = hid.cstick()
local val, raw, max = 0, 0, 146
if axis == "x" then raw = x
elseif axis == "y" then raw = y end
val = raw / max
return math.abs(val) > math.abs(threshold) and val or 0, raw, max
end)
-- Accelerometer axis
elseif id:match("^accel%.") then
local axis, threshold = id:match("^accel%.(.+)%%(.+)$")
if not axis then axis = id:match("^accel%.(.+)$") end -- no threshold (=0)
threshold = tonumber(threshold) or 0
table.insert(ret, function()
local x, y, z = hid.accel()
local val, raw, max = 0, 0, 32768 -- no idea actually, but it's a s16
if axis == "x" then raw = x
elseif axis == "y" then raw = y
elseif axis == "z" then raw = z end
val = raw / max
return math.abs(val) > math.abs(threshold) and val or 0, raw, max
end)
-- Gyroscope axis
elseif id:match("^gyro%.") then
local axis, threshold = id:match("^gyro%.(.+)%%(.+)$")
if not axis then axis = id:match("^gyro%.(.+)$") end -- no threshold (=0)
threshold = tonumber(threshold) or 0
table.insert(ret, function()
local roll, pitch, yaw = hid.gyro()
local val, raw, max = 0, 0, 32768 -- no idea actually, but it's a s16
if axis == "roll" then raw = roll
elseif axis == "pitch" then raw = pitch
elseif axis == "yaw" then raw = yaw end
val = raw / max
return math.abs(val) > math.abs(threshold) and val or 0, raw, max
end)
else
error("Unknown axis identifier: "..id)
end
end
return unpack(ret)
end,
buttonsInUse = function(threshold)
local r = {}
for key, held in pairs(keys.held) do
if held then table.insert(r, "key."..key) end
end
return r
end,
axesInUse = function(threshold)
local r = {}
threshold = threshold or 0.5
if math.abs(touchX) / gfx.BOTTOM_WIDTH > threshold then table.insert(r, "touch.position.x%"..threshold) end
if math.abs(touchY) / gfx.BOTTOM_HEIGHT > threshold then table.insert(r, "touch.position.y%"..threshold) end
if math.abs(dTouchX) / gfx.BOTTOM_WIDTH > threshold then table.insert(r, "touch.move.x%"..threshold) end
if math.abs(dTouchY) / gfx.BOTTOM_HEIGHT > threshold then table.insert(r, "touch.move.y%"..threshold) end
local circleX, circleY = hid.circle()
if math.abs(circleX) / 156 > threshold then table.insert(r, "circle.x%"..threshold) end
if math.abs(circleY) / 156 > threshold then table.insert(r, "circle.y%"..threshold) end
if ctr.apt.isNew3DS() then
local cstickX, cstickY = hid.cstick()
if math.abs(cstickY) / 146 > threshold then table.insert(r, "cstick.y%"..threshold) end
if math.abs(cstickX) / 146 > threshold then table.insert(r, "cstick.x%"..threshold) end
end
local accelX, accelY, accelZ = hid.accel()
if math.abs(accelX) / 32768 > threshold then table.insert(r, "accel.x%"..threshold) end
if math.abs(accelY) / 32768 > threshold then table.insert(r, "accel.y%"..threshold) end
if math.abs(accelZ) / 32768 > threshold then table.insert(r, "accel.z%"..threshold) end
-- no gyro, because it is always in use
return r
end,
buttonName = function(...)
local ret = {}
for _,id in ipairs({...}) do
-- Key
if id:match("^key%.") then
local key = id:match("^key%.(.+)$")
table.insert(ret, key:sub(1,1):upper()..key:sub(2).." key")
else
table.insert(ret, id)
end
end
return unpack(ret)
end,
axisName = function(...)
local ret = {}
for _,id in ipairs({...}) do
-- Binary axis
if id:match(".+%,.+") then
local b1, b2 = uqt.input.buttonName(id:match("^(.+)%,(.+)$"))
table.insert(ret, b1.." / "..b2)
-- Touch movement
elseif id:match("^touch%.move%.") then
local axis, threshold = id:match("^touch%.move%.(.+)%%(.+)$")
if not axis then axis = id:match("^touch%.move%.(.+)$") end -- no threshold (=0)
threshold = tonumber(threshold) or 0
table.insert(ret, ("Touch %s movement (threshold %s%%)"):format(axis, math.abs(threshold*100)))
-- Touch position
elseif id:match("^touch%.position%.") then
local axis, threshold = id:match("^touch%.position%.(.+)%%(.+)$")
if not axis then axis = id:match("^touch%.position%.(.+)$") end -- no threshold (=0)
threshold = tonumber(threshold) or 0
table.insert(ret, ("Touch %s position (threshold %s%%)"):format(axis, math.abs(threshold*100)))
-- Circle pad axis
elseif id:match("^circle%.") then
local axis, threshold = id:match("^circle%.(.+)%%(.+)$")
if not axis then axis = id:match("^circle%.(.+)$") end -- no threshold (=0)
threshold = tonumber(threshold) or 0
if axis == "x" then
table.insert(ret, ("Circle pad horizontal axis (deadzone %s%%)"):format(math.abs(threshold*100)))
elseif axis == "y" then
table.insert(ret, ("Circle pad vertical axis (deadzone %s%%)"):format(math.abs(threshold*100)))
else
table.insert(ret, ("Circle pad %s axis (deadzone %s%%)"):format(axis, math.abs(threshold*100)))
end
-- C-Stick axis
elseif id:match("^cstick%.") then
local axis, threshold = id:match("^cstick%.(.+)%%(.+)$")
if not axis then axis = id:match("^cstick%.(.+)$") end -- no threshold (=0)
threshold = tonumber(threshold) or 0
if axis == "x" then
table.insert(ret, ("C-Stick horizontal axis (deadzone %s%%)"):format(math.abs(threshold*100)))
elseif axis == "y" then
table.insert(ret, ("C-Stick vertical axis (deadzone %s%%)"):format(math.abs(threshold*100)))
else
table.insert(ret, ("C-Stick %s axis (deadzone %s%%)"):format(axis, math.abs(threshold*100)))
end
-- Accelerometer axis
elseif id:match("^accel%.") then
local axis, threshold = id:match("^accel%.(.+)%%(.+)$")
if not axis then axis = id:match("^accel%.(.+)$") end -- no threshold (=0)
threshold = tonumber(threshold) or 0
table.insert(ret, ("Accelerometer %s axis (deadzone %s%%)"):format(axis, math.abs(threshold*100)))
-- Gyroscope axis
elseif id:match("^gyro%.") then
local axis, threshold = id:match("^gyro%.(.+)%%(.+)$")
if not axis then axis = id:match("^gyro%.(.+)$") end -- no threshold (=0)
threshold = tonumber(threshold) or 0
table.insert(ret, ("Gyroscope %s axis (deadzone %s%%)"):format(axis, math.abs(threshold*100)))
else
table.insert(ret, id)
end
end
return unpack(ret)
end
})
-- Defaults
uqt.input.default.pointer:bind(
{ "absolute", "key.left,key.right", "key.up,key.down" },
{ "absolute", "circle.x", "circle.y" }
)
uqt.input.default.confirm:bind("key.a")
uqt.input.default.cancel:bind("key.b")
end

View file

@ -30,15 +30,20 @@ do
end end
-- Redefine all functions in tbl which also are in toAdd, so when used they call the old function (in tbl) and then the new (in toAdd). -- Redefine all functions in tbl which also are in toAdd, so when used they call the old function (in tbl) and then the new (in toAdd).
-- Functions with names prefixed by a exclamation mark will overwrite the old function.
local function add(tbl, toAdd) local function add(tbl, toAdd)
for k,v in pairs(toAdd) do for k,v in pairs(toAdd) do
local old = tbl[k] local old = tbl[k]
if k:sub(1,1) == "!" then
tbl[k] = v
else
tbl[k] = function(...) tbl[k] = function(...)
old(...) old(...)
return v(...) return v(...)
end end
end end
end end
end
-- uqt -- uqt
uqt.backend = "love" uqt.backend = "love"
@ -48,9 +53,6 @@ if uqt.event then
local updateDefault = uqt.event.update local updateDefault = uqt.event.update
uqt.event.update = function() end uqt.event.update = function() end
function love.update(dt) function love.update(dt)
-- Value update
uqt.draw.fps = love.timer.getFPS()
-- Stuff defined in ubiquitousse.lua -- Stuff defined in ubiquitousse.lua
updateDefault(dt*1000) updateDefault(dt*1000)
@ -93,6 +95,9 @@ add(uqt.draw, {
resizable = p.resizable resizable = p.resizable
}) })
end, end,
fps = function()
return love.timer.getFPS()
end,
color = function(r, g, b, a) color = function(r, g, b, a)
love.graphics.setColor(r, g, b, a) love.graphics.setColor(r, g, b, a)
end, end,
@ -100,16 +105,34 @@ add(uqt.draw, {
love.graphics.setFont(defaultFont) love.graphics.setFont(defaultFont)
love.graphics.print(text, x, y) love.graphics.print(text, x, y)
end, end,
line = function(x1, y1, x2, y2) lineWidth = function(width)
love.graphics.line(x1, y1, x2, y2) love.graphics.setLineWidth(width)
end, end,
rectangle = function(x, y, width, height) line = function(x1, y1, x2, y2, ...)
love.graphics.line(x1, y1, x2, y2, ...)
end,
polygon = function(...)
love.graphics.polygon("fill", ...)
end,
linedPolygon = function(...)
love.graphics.polygon("line", ...)
end,
["!rectangle"] = function(x, y, width, height)
love.graphics.rectangle("fill", x, y, width, height) love.graphics.rectangle("fill", x, y, width, height)
end, end,
["!linedRectangle"] = function(x, y, width, height)
love.graphics.rectangle("line", x, y, width, height)
end,
circle = function(x, y, radius)
love.graphics.circle("fill", x, y, radius)
end,
linedCircle = function(x, y, radius)
love.graphics.circle("line", x, y, radius)
end,
scissor = function(x, y, width, height) scissor = function(x, y, width, height)
love.graphics.setScissor(x, y, width, height) love.graphics.setScissor(x, y, width, height)
end, end,
-- TODO: doc -- TODO: cf draw.lua
image = function(filename) image = function(filename)
local img = love.graphics.newImage(filename) local img = love.graphics.newImage(filename)
return { return {
@ -144,7 +167,7 @@ end
-- uqt.audio -- uqt.audio
if uqt.audio then if uqt.audio then
add(uqt.audio, { add(uqt.audio, {
-- TODO: doc -- TODO: cf audio.lua
load = function(filepath) load = function(filepath)
local audio = love.audio.newSource(filepath) local audio = love.audio.newSource(filepath)
return { return {
@ -199,18 +222,6 @@ function love.mousemoved(x, y, dx, dy)
if dx ~= 0 then axesInUse["mouse.move.x"] = dx/love.graphics.getWidth() end if dx ~= 0 then axesInUse["mouse.move.x"] = dx/love.graphics.getWidth() end
if dy ~= 0 then axesInUse["mouse.move.y"] = dy/love.graphics.getHeight() end if dy ~= 0 then axesInUse["mouse.move.y"] = dy/love.graphics.getHeight() end
end end
-- love.wheelmoved doesn't trigger when the wheel stop moving, so we need to clear up our stuff after love.update (so in love.draw)
add(love, {
draw = function()
buttonsInUse["mouse.wheel.up"] = nil
buttonsInUse["mouse.wheel.down"] = nil
buttonsInUse["mouse.wheel.right"] = nil
buttonsInUse["mouse.wheel.left"] = nil
-- Same for mouse axis
axesInUse["mouse.move.x"] = nil
axesInUse["mouse.move.y"] = nil
end
})
function love.gamepadpressed(joystick, button) function love.gamepadpressed(joystick, button)
buttonsInUse["gamepad.button."..joystick:getID().."."..button] = true buttonsInUse["gamepad.button."..joystick:getID().."."..button] = true
end end
@ -228,6 +239,17 @@ end
love.mouse.setVisible(false) love.mouse.setVisible(false)
add(uqt.input, { add(uqt.input, {
-- love.wheelmoved doesn't trigger when the wheel stop moving, so we need to clear up our stuff at each update
update = function()
buttonsInUse["mouse.wheel.up"] = nil
buttonsInUse["mouse.wheel.down"] = nil
buttonsInUse["mouse.wheel.right"] = nil
buttonsInUse["mouse.wheel.left"] = nil
-- Same for mouse axis
axesInUse["mouse.move.x"] = nil
axesInUse["mouse.move.y"] = nil
end,
buttonDetector = function(...) buttonDetector = function(...)
local ret = {} local ret = {}
for _,id in ipairs({...}) do for _,id in ipairs({...}) do
@ -356,7 +378,7 @@ add(uqt.input, {
buttonsInUse = function(threshold) buttonsInUse = function(threshold)
local r = {} local r = {}
local threshold = threshold or 0.5 threshold = threshold or 0.5
for b in pairs(buttonsInUse) do for b in pairs(buttonsInUse) do
table.insert(r, b) table.insert(r, b)
end end
@ -370,7 +392,7 @@ add(uqt.input, {
axesInUse = function(threshold) axesInUse = function(threshold)
local r = {} local r = {}
local threshold = threshold or 0.5 threshold = threshold or 0.5
for b,v in pairs(axesInUse) do for b,v in pairs(axesInUse) do
if math.abs(v) > threshold then if math.abs(v) > threshold then
table.insert(r, b.."%"..threshold) table.insert(r, b.."%"..threshold)
@ -416,7 +438,7 @@ add(uqt.input, {
table.insert(ret, ("Gamepad %s axis %s (deadzone %s%%)"):format(gid, axis, math.abs(threshold*100))) table.insert(ret, ("Gamepad %s axis %s (deadzone %s%%)"):format(gid, axis, math.abs(threshold*100)))
end end
else else
table.insert(r, id) table.insert(ret, id)
end end
end end
return unpack(ret) return unpack(ret)
@ -429,13 +451,13 @@ add(uqt.input, {
if id:match(".+%,.+") then if id:match(".+%,.+") then
local b1, b2 = uqt.input.buttonName(id:match("^(.+)%,(.+)$")) local b1, b2 = uqt.input.buttonName(id:match("^(.+)%,(.+)$"))
table.insert(ret, b1.." / "..b2) table.insert(ret, b1.." / "..b2)
-- Mouse move -- Mouse movement
elseif id:match("^mouse%.move%.") then elseif id:match("^mouse%.move%.") then
local axis, threshold = id:match("^mouse%.move%.(.+)%%(.+)$") local axis, threshold = id:match("^mouse%.move%.(.+)%%(.+)$")
if not axis then axis = id:match("^mouse%.move%.(.+)$") end -- no threshold (=0) if not axis then axis = id:match("^mouse%.move%.(.+)$") end -- no threshold (=0)
threshold = tonumber(threshold) or 0 threshold = tonumber(threshold) or 0
table.insert(ret, ("Mouse %s movement (threshold %s%%)"):format(axis, math.abs(threshold*100))) table.insert(ret, ("Mouse %s movement (threshold %s%%)"):format(axis, math.abs(threshold*100)))
-- Mouse move -- Mouse position
elseif id:match("^mouse%.position%.") then elseif id:match("^mouse%.position%.") then
local axis, threshold = id:match("^mouse%.position%.(.+)%%(.+)$") local axis, threshold = id:match("^mouse%.position%.(.+)%%(.+)$")
if not axis then axis = id:match("^mouse%.position%.(.+)$") end -- no threshold (=0) if not axis then axis = id:match("^mouse%.position%.(.+)$") end -- no threshold (=0)
@ -458,7 +480,7 @@ add(uqt.input, {
table.insert(ret, ("Gamepad %s axis %s (deadzone %s%%)"):format(gid, axis, math.abs(threshold*100))) table.insert(ret, ("Gamepad %s axis %s (deadzone %s%%)"):format(gid, axis, math.abs(threshold*100)))
end end
else else
table.insert(r, id) table.insert(ret, id)
end end
end end
return unpack(ret) return unpack(ret)

View file

@ -11,6 +11,8 @@ local uqt = require((...):match("^(.-ubiquitousse)%."))
-- --
-- x and y values can be float, so make sure to perform math.floor if your engine only support -- x and y values can be float, so make sure to perform math.floor if your engine only support
-- integer coordinates. -- integer coordinates.
--
-- Mostly plagiarized from the Löve API, with some parts from ctrµLua.
local draw local draw
draw = { draw = {
--- Initial game view paramters (some defaults). --- Initial game view paramters (some defaults).
@ -46,9 +48,10 @@ draw = {
draw.height = params.height draw.height = params.height
end, end,
--- Frames per second (the backend should update this value). --- Return the number of frames per second.
-- @treturn number the current FPS
-- @impl backend -- @impl backend
fps = 60, fps = function() end,
--- Sets the drawing color --- Sets the drawing color
-- @tparam number r the red component (0-255) -- @tparam number r the red component (0-255)
@ -65,21 +68,63 @@ draw = {
-- @impl backend -- @impl backend
text = function(x, y, text) end, text = function(x, y, text) end,
--- Sets the width.
-- @tparam number width the line width
-- @impl backend
lineWidth = function(width) end,
--- Draws a line. --- Draws a line.
-- @tparam number x1 line start x coordinate -- @tparam number x1 line start x coordinate
-- @tparam number y1 line start y coordinate -- @tparam number y1 line start y coordinate
-- @tparam number x2 line end x coordinate -- @tparam number x2 line end x coordinate
-- @tparam number y2 line end y coordinate -- @tparam number y2 line end y coordinate
-- @tparam number ... other vertices to continue drawing a polyline
-- @impl backend -- @impl backend
line = function(x1, y1, x2, y2) end, line = function(x1, y1, x2, y2, ...) end,
--- Draws a filled rectangle --- Draws a filled polygon.
-- @tparam number x1,y1,x2,y2... the vertices of the polygon
-- @impl backend
polygon = function(...) end,
--- Draws a polygon outline.
-- @tparam number x1,y1,x2,y2... the vertices of the polygon
-- @impl backend
linedPolygon = function(...) end,
--- Draws a filled rectangle.
-- @tparam number x rectangle top-left x coordinate -- @tparam number x rectangle top-left x coordinate
-- @tparam number y rectangle top-left x coordinate -- @tparam number y rectangle top-left x coordinate
-- @tparam number width rectangle width -- @tparam number width rectangle width
-- @tparam number height rectangle height -- @tparam number height rectangle height
-- @impl ubiquitousse
rectangle = function(x, y, width, height)
draw.polygon(x, y, x + width, y, x + width, y + height, x, y + height)
end,
--- Draws a rectangle outline.
-- @tparam number x rectangle top-left x coordinate
-- @tparam number y rectangle top-left x coordinate
-- @tparam number width rectangle width
-- @tparam number height rectangle height
-- @impl ubiquitousse
linedRectangle = function(x, y, width, height)
draw.linedPolygon(x, y, x + width, y, x + width, y + height, x, y + height)
end,
--- Draws a filled circle.
-- @tparam number x center x coordinate
-- @tparam number y center x coordinate
-- @tparam number radius circle radius
-- @impl backend -- @impl backend
rectangle = function(x, y, width, height) end, circle = function(x, y, radius) end,
--- Draws a circle outline.
-- @tparam number x center x coordinate
-- @tparam number y center x coordinate
-- @tparam number radius circle radius
-- @impl backend
linedCircle = function(x, y, radius) end,
--- Enables the scissor test. --- Enables the scissor test.
-- When enabled, every pixel drawn outside of the scissor rectangle is discarded. -- When enabled, every pixel drawn outside of the scissor rectangle is discarded.
@ -102,9 +147,9 @@ draw = {
-- TODO: doc & api -- TODO: doc & api
push = function() end, push = function() end,
pop = function() end, pop = function() end,
polygon = function(...) end,
circle = function(x, y, radius) end,
translate = function(x, y) end, translate = function(x, y) end,
rotate = function(angle) end,
scale = function(sx, sy) end,
font = function(filename) end, font = function(filename) end,
image = function(filename) end, image = function(filename) end,
} }

View file

@ -5,7 +5,8 @@ local scene = require((...):match("^(.-ubiquitousse)%.")..".scene")
--- The events: callback functions that will be called when something interesting occurs. --- The events: callback functions that will be called when something interesting occurs.
-- Theses are expected to be redefined in the game. -- Theses are expected to be redefined in the game.
-- For backend writers: if they already contain code, then this code has to be called on each call. -- For backend writers: if they already contain code, then this code has to be called on each call, even
-- if the user manually redefines them.
-- @usage -- in the game's code -- @usage -- in the game's code
-- ubiquitousse.event.draw = function() -- ubiquitousse.event.draw = function()
-- ubiquitousse.draw.text(5, 5, "Hello world") -- ubiquitousse.draw.text(5, 5, "Hello world")

View file

@ -11,19 +11,30 @@
-- --
-- For backend writers: -- For backend writers:
-- If a function defined here already contains some code, this means this code is mandatory and you must put/call -- If a function defined here already contains some code, this means this code is mandatory and you must put/call
-- it in your implementation. -- it in your implementation (except if the backend provides a more efficient implementation).
-- Also, a backend file shouldn't redefine the ubiquitousse table itself but only redefine the backend-dependant fields. -- Also, a backend file shouldn't redefine the ubiquitousse table itself but only redefine the backend-dependant fields.
-- The API doesn't make the difference between numbers and integers, so convert to integers when needed. -- Lua 5.3: The API doesn't make the difference between numbers and integers, so convert to integers when needed.
--
-- For game writer:
-- Ubiquitousse works with Lua 5.1 to 5.3, including LuaJit, but doesn't provide any version checking or compatibility layer
-- between the different versions, so it's up to you to handle that in your game (or ignore the problem and sticks to your
-- main's backend Lua version).
-- --
-- Ubiquitousse's goal is to run everywhere with the least porting effort possible. -- Ubiquitousse's goal is to run everywhere with the least porting effort possible.
-- To achieve this, the engine needs to stay simple, and only provide features that are almost sure to be -- To achieve this, the engine needs to stay simple, and only provide features that are almost sure to be
-- available everywhere, so writing a backend should be straighforward. -- available everywhere, so writing a backend should be straighforward.
-- However, Ubiquitousse still make some small assumptions about the engine:
-- * The engine has some kind of main loop, or at least a function called very often (may or may not be the
-- same as the redraw screen callback).
-- * 32bit color depth.
-- --
-- Regarding data formats, Ubiquitousse reference implemtations expect and recommend: -- However, a full Ubiquitousse backend still have a few requirement about the destination platform:
-- * The backend needs to have access to some kind of main loop, or at least a function called very often (may or may not be the
-- same as the redraw screen callback).
-- * A 2D matrix graphic output with 32bit RGB color depth.
-- * Inputs which match ubiquitousse.input.default (a pointing/4 direction input, a confirm button, and a cancel button).
-- * Some way of measuring time with millisecond-precision.
-- * Some kind of filesystem.
-- * An available audio output would be preferable.
-- * Lua 5.1, 5.2, 5.3 or LuaJit.
--
-- Regarding data formats, Ubiquitousse implementations expect and recommend:
-- * For images, PNG support is expected. -- * For images, PNG support is expected.
-- * For audio files, OGG Vorbis support is expected. -- * For audio files, OGG Vorbis support is expected.
-- * For fonts, TTF support is expected. -- * For fonts, TTF support is expected.
@ -47,6 +58,10 @@
-- * ubiquitousse: fully-working version in Ubiquitousse, may or may not be redefined in backend -- * ubiquitousse: fully-working version in Ubiquitousse, may or may not be redefined in backend
-- The implementation level is indicated using the "@impl level" annotation. -- The implementation level is indicated using the "@impl level" annotation.
-- --
-- Regarding the documentation: Ubiquitousse used LDoc/LuaDoc styled-comments, but since LDoc hates me and my code, the
-- generated result is complete garbage, so please read the documentation directly in the comments here.
-- Stuff you're interested in starts with triple - (e.g., "--- This functions saves the world").
--
-- @usage local ubiquitousse = require("ubiquitousse") -- @usage local ubiquitousse = require("ubiquitousse")
local p = ... -- require path local p = ... -- require path

View file

@ -12,9 +12,6 @@ local function getPath(modname)
return filepath return filepath
end end
-- FIXME: http://hump.readthedocs.io/en/latest/gamestate.html
-- FIXME: call order
--- Scene management. --- Scene management.
-- You can use use scenes to seperate the different states of your game: for example, a menu scene and a game scene. -- You can use use scenes to seperate the different states of your game: for example, a menu scene and a game scene.
-- This module is fully implemented in Ubiquitousse and is mostly a "recommended way" of organising an Ubiquitousse-based game. -- This module is fully implemented in Ubiquitousse and is mostly a "recommended way" of organising an Ubiquitousse-based game.
@ -22,19 +19,17 @@ end
-- make them scene-independent, for example by creating a scene-specific TimerRegistry (TimedFunctions that are keept accross -- make them scene-independent, for example by creating a scene-specific TimerRegistry (TimedFunctions that are keept accross
-- states are generally a bad idea). Theses scene-specific states should be created and available in the table returned by -- states are generally a bad idea). Theses scene-specific states should be created and available in the table returned by
-- ubiquitousse.scene.new. -- ubiquitousse.scene.new.
-- Currently, the implementation always execute a scene's file before setting it as current, but this may change in the future or -- Currently, the implementation always execute a scene's file before switching to it or adding it to the stack, but this may change in
-- for some implementations (e.g., on a computer where memory isn't a problem, the scene may be put in a cache). The result of this -- the future or for some implementations (e.g., on a computer where memory isn't a problem, the scene may be put in a cache). The result
-- is that you can load assets, libraries, etc. outside of the enter callback, so they can be cached and not reloaded each time -- of this is that you can load assets, libraries, etc. outside of the enter callback, so they can be cached and not reloaded each time
-- the scene is entered, but all the other scene initialization should be done in the enter callback, since it won't be executed on -- the scene is entered, but all the other scene initialization should be done in the enter callback, since it won't be executed on
-- each enter otherwise. -- each enter otherwise.
-- The expected code-organisation is: -- The expected code-organisation is:
-- * each scene is in a file, identified by its module name (same identifier used by Lua's require) -- * each scene is in a file, identified by its module name (same identifier used by Lua's require)
-- * each scene file create a new scene table using ubiquitousse.scene.new and returns it at the end of the file -- * each scene file create a new scene table using ubiquitousse.scene.new and returns it at the end of the file
-- Order of callbacks: -- Order of callbacks:
-- * all scene exit callbacks are called before changing the stack or the current scene (ie, ubiquitousse.scene.current and the -- * all scene change callbacks are called after setting scene.current to the new scene but before changing scene.stack
-- last stack element is the scene in which the exit or suspend function was called) -- * all scene exit/suspend callbacks are called before scene enter/resume callbacks
-- * all scene enter callbacks are called before changing the stack or the current scene (ie, ubiquitousse.scene.current and the
-- last stack element is the previous scene which was just exited, and not the new scene)
local scene local scene
scene = { scene = {
--- The current scene table. --- The current scene table.
@ -53,31 +48,35 @@ scene = {
-- @impl ubiquitousse -- @impl ubiquitousse
new = function() new = function()
return { return {
name = "loading scene", -- The scene name.
time = time.new(), -- Scene-specific TimerRegistry. time = time.new(), -- Scene-specific TimerRegistry.
enter = function(...) end, -- Called when entering a scene. enter = function(self, ...) end, -- Called when entering a scene.
exit = function() end, -- Called when exiting a scene, and not expecting to come back (scene may be unloaded). exit = function(self) end, -- Called when exiting a scene, and not expecting to come back (scene may be unloaded).
suspend = function() end, -- Called when suspending a scene, and expecting to come back (scene won't be unloaded). suspend = function(self) end, -- Called when suspending a scene, and expecting to come back (scene won't be unloaded).
resume = function() end, -- Called when resuming a suspended scene (after calling suspend). resume = function(self) end, -- Called when resuming a suspended scene (after calling suspend).
update = function(dt, ...) end, -- Called on each ubiquitousse.event.update on the current scene. update = function(self, dt, ...) end, -- Called on each ubiquitousse.event.update on the current scene.
draw = function(...) end -- Called on each ubiquitousse.event.draw on the current scene. draw = function(self, ...) end -- Called on each ubiquitousse.event.draw on the current scene.
} }
end, end,
--- Switch to a new scene. --- Switch to a new scene.
-- The current scene exit function will be called, the new scene will be loaded, -- The new scene will be loaded and the current scene will be replaced by the new one,
-- the current scene will then be replaced by the new one, and then the enter callback is called. -- then the previous scene exit function will be called, then the enter callback is called on the new scence.
-- Then the stack is changed to replace the old scene with the new one.
-- @tparam string scenePath the new scene module name -- @tparam string scenePath the new scene module name
-- @param ... arguments to pass to the scene's enter function -- @param ... arguments to pass to the scene's enter function
-- @impl ubiquitousse -- @impl ubiquitousse
switch = function(scenePath, ...) switch = function(scenePath, ...)
if scene.current then scene.current.exit() end local previous = scene.current
scene.current = dofile(getPath(scene.prefix..scenePath)) scene.current = dofile(getPath(scene.prefix..scenePath))
local i = #scene.stack scene.current.name = scenePath
scene.stack[math.max(i, 1)] = scene.current if previous then previous:exit() end
scene.current.enter(...) scene.current:enter(...)
scene.stack[math.max(#scene.stack, 1)] = scene.current
end, end,
--- Push a new scene to the scene stack. --- Push a new scene to the scene stack.
@ -88,21 +87,23 @@ scene = {
-- @param ... arguments to pass to the scene's enter function -- @param ... arguments to pass to the scene's enter function
-- @impl ubiquitousse -- @impl ubiquitousse
push = function(scenePath, ...) push = function(scenePath, ...)
if scene.current then scene.current.suspend() end local previous = scene.current
scene.current = dofile(getPath(scene.prefix..scenePath)) scene.current = dofile(getPath(scene.prefix..scenePath))
scene.current.name = scenePath
if previous then previous:suspend() end
scene.current:enter(...)
table.insert(scene.stack, scene.current) table.insert(scene.stack, scene.current)
scene.current.enter(...)
end, end,
--- Pop the current scene from the scene stack. --- Pop the current scene from the scene stack.
-- The current scene exit function will be called, then the previous scene resume function will be called. -- The previous scene will be set as the current scene, then the current scene exit function will be called,
-- Then the current scene will be removed from the stack, and the previous scene will be set as the current scene. -- then the previous scene resume function will be called, and then the current scene will be removed from the stack.
-- @impl ubiquitousse -- @impl ubiquitousse
pop = function() pop = function()
if scene.current then scene.current.exit() end local previous = scene.current
local previous = scene.stack[#scene.stack-1] scene.current = scene.stack[#scene.stack-1]
scene.current = previous if previous then previous:exit() end
if previous then previous.resume() end if scene.current then scene.current:resume() end
table.remove(scene.stack) table.remove(scene.stack)
end, end,
@ -114,7 +115,7 @@ scene = {
update = function(dt, ...) update = function(dt, ...)
if scene.current then if scene.current then
scene.current.time.update(dt) scene.current.time.update(dt)
scene.current.update(dt, ...) scene.current:update(dt, ...)
end end
end, end,
@ -123,7 +124,7 @@ scene = {
-- @param ... arguments to pass to the scene's draw function -- @param ... arguments to pass to the scene's draw function
-- @impl ubiquitousse -- @impl ubiquitousse
draw = function(...) draw = function(...)
if scene.current then scene.current.draw(...) end if scene.current then scene.current:draw(...) end
end end
} }