CHUAN2 DEV ENGINE
996 正版授权研发中心 · 360 授权合作教学中心 · 抖音传奇直播合作授权 · 快手推广运营商授权
OFFICIAL LICENSED ACADEMY 查验官方授权证书 →
// 威海旷世互娱教学基地 · 技术文章
进阶实战游戏功能996引擎

Lua页面栈:界面前进后退的层叠管理

2026-10-02 02:28 作者:996 技术组 996引擎Lua教程传奇脚本进阶实战游戏功能996引擎

【语法算法】

接着抛坑:返回键到底回哪一页?主界面进背包,背包进详情,详情进强化——一路点进来四层,返回键一按,回的该是最开始的主页还是上一层强化?页面栈的答案是:回上一层,因为界面的进出本来就是压栈出栈——进一页压一层,返回出一层,永远只露栈顶。这篇把整套写法拆开:配置表、压栈结算、出栈判定、层叠输出、定时器、调试按钮,一段一段照抄能跑。

一、效果演示:界面前进后退的层叠管理

演示场四层界面的卡片。点「进入下一页」:新一层的卡片从右往左滑进来,压住旧的一层——栈深加一,左上角的栈路实时播报"主页 > 背包 > 详情"。点「返回」:栈顶的卡片滑走,露出底下的那一层——栈深减一。栈满四层再点进入,提示到底了;栈剩一层再点返回,提示已经是最底层——两头的提示把栈的脾气交代得明明白白。演示里试两笔账:栈式管理的返回次序和记不住来路的返回——栈式的每一步返回都精确落在来时的上一层,永不错乱。页面栈教的是来路:界面的返回不是猜的,是栈里记着的——栈路播报就是这份记性露在明处的样子。

mermaid
flowchart TD
A[点击进入] --> B{栈满了吗}
B -- 是 --> C[提示到底]
B -- 否 --> D[新页压栈 滑入]
D --> A
E[点击返回] --> F{栈只剩一层}
F -- 是 --> G[提示最底层]
F -- 否 --> H[栈顶出栈 露出下层]
H --> E
demo
 fx-pagestack

二、底层原理:一次压栈结算加一次出栈判定

模块的机关是一对搭档。压栈结算:进入的那一拍把新页推进栈——新页盖住栈顶成为新的栈顶,旧页原地留在下层不销毁,回来时还是离开时的样子。出栈判定:返回的那一拍先看栈深——剩一层按返回是到底的提示,两层以上栈顶出栈,露出的下层自动成为新的界面。和单页跳转的分野在"页面栈记得来路":跳转是 A 换成 B,回来要自己写回 A 的逻辑;页面栈是 A 上摞 B,返回时 B 走人 A 原样在——来路是栈替你记的,你只管往前点。

下层为什么不销毁?销毁的版本返回时要重建整页,状态全丢——背包的滚动位置、详情的展开项全飞了;不销毁的版本下层原地睡觉,返回即醒,状态原封不动——滚动条停在哪、勾选勾了哪,睁眼还是那样。

三、核心代码:完整模块(上·骨架)

lua
-- @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

四、核心代码:完整模块(下·压栈与出栈)

lua
-- 压栈:新页入栈,旧页原地睡觉
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 技术组基于 996 引擎官方知识库与浮生梦老师课程体系整理。团队长期从事传奇类引擎 Lua 后端逻辑、客户端界面与商业版本交付,内容以官方知识库与真实项目为出处,按版本持续修订。

← 返回文章地图返回研学路径

幂尔框架 · 实战干货 · 接口调用

LATEST ARTICLES

全站技术干货持续更新:996 引擎 / Lua 实战帖,语法、参数与示例一篇讲透。进入文章地图 · 查看全部 →

进阶实战游戏功能

Lua自动炮台:架一个自动索敌开火

【语法算法】 上一版的炮台索敌逻辑有个隐蔽报错:炮台永远打同一个怪——即使那只怪已经死了,索敌的游标也不挪窝。追到底索敌的游…

2026-10-02 04:50 996 技术组
进阶实战游戏功能

Lua元素转换:火伤转冰伤附带减速

【语法算法】 先抛一个坑:打不完的火系怪怎么办?火抗怪火打不动,换冰系技能要切装备要换面板——切完黄花菜都凉了。元素转换的答…

2026-10-02 04:50 996 技术组
进阶实战游戏功能

Lua连锁引燃:一个着火引燃下一个

【语法算法】 上个月的事故复盘会上有个数字被念了三遍:四成——策划写的是"同伴陪疼四成",代码落下去成了"陪疼四十点",两只…

2026-10-02 04:50 996 技术组
进阶实战游戏功能

Lua弹射地垫:踩上去弹飞到空中

【语法算法】 单行代码拆解:弹射初速=-420——弹射的全部动力就这一行的负初速。负号朝上、四百二十是弹射的初速大小——踩上…

2026-10-02 04:50 996 技术组
进阶实战游戏功能

Lua时间减缓:流速减半的局部时差

【语法算法】 上一版的减速类模块全按"乘以零点五"来写,帧率无关的版本照搬了这套写法——结果高帧率机上减速效果好,低帧率机上…

2026-10-02 04:50 996 技术组
进阶实战游戏功能

Lua引力球:丢出去把怪吸过来

【语法算法】 先抛一个坑:怎么把散在四处的怪聚到一起打?逐个拉是笨办法,一个范围技又只能打一片——引力球的答案是一个会动的吸…

2026-10-02 04:50 996 技术组