【语法算法】
接着抛坑:返回键到底回哪一页?主界面进背包,背包进详情,详情进强化——一路点进来四层,返回键一按,回的该是最开始的主页还是上一层强化?页面栈的答案是:回上一层,因为界面的进出本来就是压栈出栈——进一页压一层,返回出一层,永远只露栈顶。这篇把整套写法拆开:配置表、压栈结算、出栈判定、层叠输出、定时器、调试按钮,一段一段照抄能跑。
一、效果演示:界面前进后退的层叠管理
演示场四层界面的卡片。点「进入下一页」:新一层的卡片从右往左滑进来,压住旧的一层——栈深加一,左上角的栈路实时播报"主页 > 背包 > 详情"。点「返回」:栈顶的卡片滑走,露出底下的那一层——栈深减一。栈满四层再点进入,提示到底了;栈剩一层再点返回,提示已经是最底层——两头的提示把栈的脾气交代得明明白白。演示里试两笔账:栈式管理的返回次序和记不住来路的返回——栈式的每一步返回都精确落在来时的上一层,永不错乱。页面栈教的是来路:界面的返回不是猜的,是栈里记着的——栈路播报就是这份记性露在明处的样子。
flowchart TD
A[点击进入] --> B{栈满了吗}
B -- 是 --> C[提示到底]
B -- 否 --> D[新页压栈 滑入]
D --> A
E[点击返回] --> F{栈只剩一层}
F -- 是 --> G[提示最底层]
F -- 否 --> H[栈顶出栈 露出下层]
H --> E
fx-pagestack
二、底层原理:一次压栈结算加一次出栈判定
模块的机关是一对搭档。压栈结算:进入的那一拍把新页推进栈——新页盖住栈顶成为新的栈顶,旧页原地留在下层不销毁,回来时还是离开时的样子。出栈判定:返回的那一拍先看栈深——剩一层按返回是到底的提示,两层以上栈顶出栈,露出的下层自动成为新的界面。和单页跳转的分野在"页面栈记得来路":跳转是 A 换成 B,回来要自己写回 A 的逻辑;页面栈是 A 上摞 B,返回时 B 走人 A 原样在——来路是栈替你记的,你只管往前点。
下层为什么不销毁?销毁的版本返回时要重建整页,状态全丢——背包的滚动位置、详情的展开项全飞了;不销毁的版本下层原地睡觉,返回即醒,状态原封不动——滚动条停在哪、勾选勾了哪,睁眼还是那样。
三、核心代码:完整模块(上·骨架)
-- @file PageStack.lua
-- 页面栈 —— 界面前进后退的层叠管理
local PageStack = {}
local CONST = {
STACK_CAP = 4,
PAGE_HOME = "主页",
PAGE_BAG = "背包",
PAGE_DETAIL = "详情",
PAGE_FORGE = "强化",
AUTOINC_BASE = 1154000,
}
local _stack = { CONST.PAGE_HOME }
local _pages = {}
local _autoInc = 0
local function GenID(prefix)
_autoInc = _autoInc + 1
return string.format("%s_%d", prefix or "PGS",
CONST.AUTOINC_BASE + _autoInc)
end
local function ShowTip(msg)
if msg and msg ~= "" then SL:ShowSystemTips(msg) end
end
local function PageName(def)
return def or "未名页"
end
function PageStack.Depth()
return #_stack
end
function PageStack.Top()
return _stack[#_stack]
end
四、核心代码:完整模块(下·压栈与出栈)
-- 压栈:新页入栈,旧页原地睡觉
function PageStack.Push(def)
if #_stack >= CONST.STACK_CAP then
ShowTip("到底了——只有四层")
return false
end
local node = GUI:Layout_Create(GUI:GetRootWindow(),
GenID("Page"), 0, 0, 0, 0, false)
_pages[#_stack + 1] = node
_stack[#_stack + 1] = PageName(def)
SL:PlayPageInFx(node)
ShowTip("进了" .. _stack[#_stack]
.. "——栈深" .. #_stack)
return true
end
-- 出栈:栈顶离场,露出下层
function PageStack.Pop()
if #_stack <= 1 then
ShowTip("已经是最底层")
return false
end
local node = _pages[#_stack]
if node and not GUI:Widget_IsNull(node) then
GUI:removeFromParent(node)
end
_pages[#_stack] = nil
_stack[#_stack] = nil
ShowTip("返回" .. _stack[#_stack])
return true
end
SL:ScheduleOnce(function()
SL:BindDebugButton("进入下一页", function()
PageStack.Push(CONST.PAGE_BAG)
end)
SL:BindDebugButton("返回", function()
PageStack.Pop()
end)
ShowTip("技能已加载: 页面栈 (调试按钮触发)")
end, 1.0)
function PageStack.Unload()
for i = #_pages, 1, -1 do
local node = _pages[i]
if node and not GUI:Widget_IsNull(node) then
GUI:removeFromParent(node)
end
end
_pages = {}
_stack = { CONST.PAGE_HOME }
end
return PageStack
五、机制问答
问:栈里的下层页面会收不到刷新吗?
答:会睡着——下层的刷新请求攒着,出栈回层的那一拍一次性补刷新,睡觉的页面不干活也不错过账。
问:跨层返回能一次退两层吗?
答:能——按住返回连出两层就是跨层返回,栈的规矩是出几层就揭几层,露出来的还是准的。
问:栈满四层为什么不再放?
答:省内存也省脑子——层数越深垫背的页面越多,四层是玩家心算得清来路的深度,再深返回就成了解谜,再深返回键都按迷糊。
问:从栈中间跳到栈顶的页面算什么?
答:照常压栈——跳转永远是压新层,中间的层原地留着,栈不做剪裁,剪裁是返回时的事——规矩单一才不会出现回不去的死角。
问:返回到主页再返回呢?
答:到底提示——主页是压舱的底层,出了主页就是退出游戏的活,栈不管,那是另一套流程的事。
六、调参与实战怎么用
第四笔是睡觉与唤醒:下层不销毁是状态不丢的根——销毁重建的版本滚动位置和勾选状态全飞,睡觉唤醒的版本返回即原样,这一条是页面栈对玩家的基本礼貌。第三笔是栈深的封顶:四层的帽是来路可心算的深度——帽太深垫背页面吃内存还让玩家迷路,太浅复杂玩法装不下,四层够走完"列表到详情到操作到确认"的标准链。第二笔是出栈的清理:离场的节点要亲手摘——只出栈名不摘节点,看不见的界面叠在屏外吃渲染,摘干净是出栈的收尾。第一笔是栈路的播报:左上角的来路串实时更新——玩家迷路的时候看一眼栈路就知道自己怎么进来的,播报是栈的导航,导航清楚了返回键就不背锅。常见坑三个:出栈只弹名字不摘节点;下层页面的定时器没停白白耗拍;跨层返回后中间层的输入没有屏蔽。
实战里页面栈是所有多级界面的骨架:设置的三层、商城的两层、任务的四层,全是"压栈出栈露栈顶"同一副骨架。组里的约定是:多级界面一律过页面栈,跳转写法只准用在平级切换——来路要栈记,不要人记。这篇的模块照抄能跑,改的就是 CONST 那张表。
写完留一句给做导航系的同学:页面栈卖的是"返回永远回上一层"——那一张张滑入滑出的层卡和左上角不撒谎的来路串,是把返回键变成确定性按钮的一摞层卡。
全站技术干货持续更新:996 引擎 / Lua 实战帖,语法、参数与示例一篇讲透。进入文章地图 · 查看全部 →
【语法算法】 上一版的炮台索敌逻辑有个隐蔽报错:炮台永远打同一个怪——即使那只怪已经死了,索敌的游标也不挪窝。追到底索敌的游…
【语法算法】 先抛一个坑:打不完的火系怪怎么办?火抗怪火打不动,换冰系技能要切装备要换面板——切完黄花菜都凉了。元素转换的答…
【语法算法】 上个月的事故复盘会上有个数字被念了三遍:四成——策划写的是"同伴陪疼四成",代码落下去成了"陪疼四十点",两只…
【语法算法】 单行代码拆解:弹射初速=-420——弹射的全部动力就这一行的负初速。负号朝上、四百二十是弹射的初速大小——踩上…
【语法算法】 上一版的减速类模块全按"乘以零点五"来写,帧率无关的版本照搬了这套写法——结果高帧率机上减速效果好,低帧率机上…
【语法算法】 先抛一个坑:怎么把散在四处的怪聚到一起打?逐个拉是笨办法,一个范围技又只能打一片——引力球的答案是一个会动的吸…