mirror of
https://github.com/Reuh/ubiquitousse.git
synced 2025-10-27 17:19:31 +00:00
Code reorganization, added uqt.ecs, removed LÖVE duplicates (uqt.audio, uqt.draw, uqt.filesystem)
This commit is contained in:
parent
523c5d36c0
commit
16e533d176
28 changed files with 2544 additions and 2107 deletions
163
scene/scene.lua
Normal file
163
scene/scene.lua
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
--- ubiquitousse.scene
|
||||
-- Optional dependencies: ubiquitousse.time (to provide each scene a time registry)
|
||||
local loaded, time = pcall(require, (...):match("^.-ubiquitousse%.").."time")
|
||||
if not loaded then time = nil end
|
||||
|
||||
--- Scene management.
|
||||
-- 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.
|
||||
-- However, you don't have to use this if you don't want to. ubiquitousse.scene handles all the differents Ubiquitousse-states and
|
||||
-- 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
|
||||
-- ubiquitousse.scene.new.
|
||||
-- Currently, the implementation always execute a scene's file before switching to it or adding it to the stack, but this may change in
|
||||
-- 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
|
||||
-- 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
|
||||
-- each enter otherwise.
|
||||
-- FIXME: actually more useful to never cache?
|
||||
-- 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 file create a new scene table using ubiquitousse.scene.new and returns it at the end of the file
|
||||
-- Order of callbacks:
|
||||
-- * all scene change callbacks are called after setting scene.current to the new scene but before changing scene.stack
|
||||
-- * all scene exit/suspend callbacks are called before scene enter/resume callbacks
|
||||
local scene
|
||||
scene = setmetatable({
|
||||
--- The current scene table.
|
||||
-- @impl ubiquitousse
|
||||
current = nil,
|
||||
|
||||
--- Shortcut for scene.current.time.
|
||||
-- @impl ubiquitousse
|
||||
time = nil,
|
||||
|
||||
--- The scene stack: list of scene, from the farest one to the nearest.
|
||||
-- @impl ubiquitousse
|
||||
stack = {},
|
||||
|
||||
--- A prefix for scene modules names.
|
||||
-- Will search in the "scene" directory by default. Redefine it to fit your own ridiculous filesystem.
|
||||
-- @impl ubiquitousse
|
||||
prefix = "scene.",
|
||||
|
||||
--- Function which load a scene file.
|
||||
-- @impl ubiquitousse
|
||||
load = function(sceneModule)
|
||||
local scenePath = sceneModule:gsub("%.", "/")
|
||||
for path in package.path:gmatch("[^;]+") do
|
||||
path = path:gsub("%?", scenePath)
|
||||
local f = io.open(path)
|
||||
if f then
|
||||
f:close()
|
||||
return dofile(path)
|
||||
end
|
||||
end
|
||||
if package.loaded["candran"] then -- Candran support
|
||||
for path in package.path:gsub("%.lua", ".can"):gmatch("[^;]+") do
|
||||
path = path:gsub("%?", scenePath)
|
||||
local f = io.open(path)
|
||||
if f then
|
||||
f:close()
|
||||
return package.loaded["candran"].dofile(path)
|
||||
end
|
||||
end
|
||||
end
|
||||
error("can't find scene "..tostring(sceneModule))
|
||||
end,
|
||||
|
||||
--- Creates and returns a new Scene object.
|
||||
-- @tparam[opt="unamed"] string name the new scene name
|
||||
-- @impl ubiquitousse
|
||||
new = function(name)
|
||||
return {
|
||||
name = name or "unamed", -- The scene name.
|
||||
|
||||
time = time and time.new(), -- Scene-specific TimerRegistry, if uqt.time is available.
|
||||
|
||||
enter = function(self, ...) end, -- Called when entering a scene.
|
||||
exit = function(self) end, -- Called when exiting a scene, and not expecting to come back (scene may be unloaded).
|
||||
|
||||
suspend = function(self) end, -- Called when suspending a scene, and expecting to come back (scene won't be unloaded).
|
||||
resume = function(self) end, -- Called when resuming a suspended scene (after calling suspend).
|
||||
|
||||
update = function(self, dt, ...) end, -- Called on each update on the current scene.
|
||||
draw = function(self, ...) end -- Called on each draw on the current scene.
|
||||
}
|
||||
end,
|
||||
-- TODO: handle love.quit / exit all scenes in stack
|
||||
|
||||
--- Switch to a new scene.
|
||||
-- The new scene will be loaded and the current scene will be replaced by the new one,
|
||||
-- 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/table scenePath the new scene module name, or the scene table directly
|
||||
-- @param ... arguments to pass to the scene's enter function
|
||||
-- @impl ubiquitousse
|
||||
switch = function(scenePath, ...)
|
||||
local previous = scene.current
|
||||
scene.current = type(scenePath) == "string" and scene.load(scene.prefix..scenePath) or scenePath
|
||||
scene.time = scene.current.time
|
||||
scene.current.name = scene.current.name or tostring(scenePath)
|
||||
if previous then previous:exit() end
|
||||
scene.current:enter(...)
|
||||
scene.stack[math.max(#scene.stack, 1)] = scene.current
|
||||
end,
|
||||
|
||||
--- Push a new scene to the scene stack.
|
||||
-- Similar to ubiquitousse.scene.switch, except suspend is called on the current scene instead of exit,
|
||||
-- and the current scene is not replaced: when the new scene call ubiquitousse.scene.pop, the old scene
|
||||
-- will be reused.
|
||||
-- @tparam string/table scenePath the new scene module name, or the scene table directly
|
||||
-- @param ... arguments to pass to the scene's enter function
|
||||
-- @impl ubiquitousse
|
||||
push = function(scenePath, ...)
|
||||
local previous = scene.current
|
||||
scene.current = type(scenePath) == "string" and scene.load(scene.prefix..scenePath) or scenePath
|
||||
scene.time = scene.current.time
|
||||
scene.current.name = scene.current.name or tostring(scenePath)
|
||||
if previous then previous:suspend() end
|
||||
scene.current:enter(...)
|
||||
table.insert(scene.stack, scene.current)
|
||||
end,
|
||||
|
||||
--- Pop the current scene from the scene stack.
|
||||
-- The previous scene will be set as the current scene, then the current scene exit function will be called,
|
||||
-- then the previous scene resume function will be called, and then the current scene will be removed from the stack.
|
||||
-- @impl ubiquitousse
|
||||
pop = function()
|
||||
local previous = scene.current
|
||||
scene.current = scene.stack[#scene.stack-1]
|
||||
scene.time = scene.current.time
|
||||
if previous then previous:exit() end
|
||||
if scene.current then scene.current:resume() end
|
||||
table.remove(scene.stack)
|
||||
end,
|
||||
|
||||
--- Update the current scene.
|
||||
-- Should be called at every game update; called by ubiquitousse.update.
|
||||
-- @tparam number dt the delta-time (milisecond)
|
||||
-- @param ... arguments to pass to the scene's update function after dt
|
||||
-- @impl ubiquitousse
|
||||
update = function(dt, ...)
|
||||
if scene.current then
|
||||
if time then scene.current.time:update(dt) end
|
||||
scene.current:update(dt, ...)
|
||||
end
|
||||
end,
|
||||
|
||||
--- Draw the current scene.
|
||||
-- Should be called every time the game is draw; called by ubiquitousse.draw.
|
||||
-- @param ... arguments to pass to the scene's draw function
|
||||
-- @impl ubiquitousse
|
||||
draw = function(...)
|
||||
if scene.current then scene.current:draw(...) end
|
||||
end
|
||||
}, {
|
||||
--- scene(...) is a shortcut for scene.new(...)
|
||||
__call = function(self, ...)
|
||||
return scene.new(...)
|
||||
end
|
||||
})
|
||||
|
||||
return scene
|
||||
Loading…
Add table
Add a link
Reference in a new issue