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

燃香斗纹:一炷香内斩它,烬了白点

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

【游戏功能】

隐蔽报错引入:一个很隐蔽的报错——香早就烬了,香案的计时还在走;玩家在香烬之后两秒才斩下怪,彩头却照发不误。截屏一看时间戳,香案的表和结算的表各记各的。限时挑战这类玩法,表必须只有一张:香烧到哪一秒、斩在烬前还是烬后,全从同一根香的余量里读。这篇把燃香斗纹整套写法拆开:点香起表、香尽判负、烬前得彩、自动续香,一段一段照抄能跑。

一、效果演示

演示场顶上立着一炷香,二十秒从右往左烧,香头一点金火星,青烟一缕缕往上飘。点「燃香!」香即点燃——怪只有两百血,刀刀零点九秒,香尽前斩下它:香火添一、刀重添八,新香自动续上,越战越重。手慢了香烬归零,白点一炷,状态栏喊你再来。香火三炷之后刀刀二十四,斩怪跟切菜一样,这就是攒彩头的分量。

mermaid
flowchart TD
A[点香] --> B[香烧二十秒]
B --> C{烬前斩下它}
C -- 斩了 --> D[香火添一 刀重添八]
D --> E[自动续新香]
E --> B
C -- 烬了 --> F[彩头落空]
F --> A
demo
 fx-crest-incense

二、底层原理

燃香系统的机关是"一根表管到底"——香的余量是唯一的时间事实:烧、判、续全从这一个数里走。那场报错的两张表就是反面:香案一张表走展示,结算另一张表走判定,两边漂移半秒,烬前烬后就判错了。正解是结算直接读香的余量:余量大于零斩下它就是得彩,余量归零那拍就是烬,没有第二处可漂。
彩头的给法走"续"字:得彩之后新香自动续上,挑战不断档,香火一路叠——刀重随香火涨,三炷之后斩怪如切菜,玩家自己就能感觉到滚雪球的分量。烬了的代价也要干脆:彩头落空、香案归零,不扣玩家任何东西——限时挑战罚的是机会不是家底,罚重了没人点香。香的演出是这条玩法的魂:火星一明一暗,青烟一缕缕往上走,余量眼看变短——时间被画出来了。幂尔框架这边续香收在得彩的同一拍里,表还是那一根。

这套玩法的代码量不到八十行,难的不是烧香而是表的唯一——余量一个数管烧、判、续,谁也没处漂。

再看烬后判负的宽松度——为什么烬的瞬间就判负,不给零点几秒的手感余量?因为烬是看得见的事实:火星熄了、烟断了,玩家亲眼看着它灭——这一拍判负,账和眼睛对得上。要是偷偷放水半秒,偶尔有一次"香灭了还得了彩",玩家的账就乱了:下次烬了不得彩,反觉得游戏吞了彩头。判定的尺子要和画面的尺子一样长,放水式的宽容是最贵的错误。

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

lua
-- @file CrestIncense.lua
-- 燃香斗纹 —— 一炷香内斩它 烬了白点

local CrestIncense = {}

local CONST = {
    BURN_TIME     = 20,
    REWARD        = 8,
    DMG_BASE      = 12,

    AUTOINC_BASE  = 1199900,
}

local _burn     = 0
local _fire     = 0
local _autoInc  = 0

local function ShowTip(msg)
    if msg and msg ~= "" then SL:ShowSystemTips(msg) end
end

local function GenID()
    _autoInc = _autoInc + 1
    return CONST.AUTOINC_BASE + _autoInc
end

function CrestIncense.Burning() return _burn end
function CrestIncense.Fire() return _fire end

-- 刀重从香火现场结
function CrestIncense.Dmg()
    return CONST.DMG_BASE + _fire * CONST.REWARD
end

-- 点香:唯一的起表口
function CrestIncense.Light()
    if _burn > 0 then
        ShowTip("香正烧着——趁热打它")
        return
    end
    _burn = CONST.BURN_TIME
    ShowTip("香点着了——" .. CONST.BURN_TIME .. "秒内斩它!")
end

四、核心代码:完整模块(下·推进与卸载)

lua
-- 每拍烧表:余量是唯一的时间事实
function CrestIncense.Tick(dt)
    if _burn <= 0 then return end
    _burn = _burn - dt
    if _burn <= 0 then
        _burn = 0
        ShowTip("香烬了——这一炷没等到它倒")
    end
end

-- 击杀结算:烬前斩它得彩并自动续香
function CrestIncense.OnKill()
    if _burn > 0 then
        _burn = 0
        _fire = _fire + 1
        ShowTip("香尽前斩它!香火添一——刀重"
            .. CrestIncense.Dmg())
        _burn = CONST.BURN_TIME
    else
        ShowTip("它倒了——点「燃香!」再开一局")
    end
end

if not _ticker then
    _ticker = SL:Schedule(function(dt)
        CrestIncense.Tick(dt)
    end, 0.05)
end

SL:ScheduleOnce(function()
    SL:BindDebugButton("燃香!", function()
        CrestIncense.Light()
    end)
    ShowTip("技能已加载: 燃香斗纹")
end, 1.0)

function CrestIncense.Unload()
    if _ticker then SL:Unschedule(_ticker) end
    _burn = 0
    _fire = 0
end

return CrestIncense

五、机制问答

问:烬后得彩那场报错的根因是什么?
答:香案和结算各一张表,漂移半秒就判错——正解是结算直接读香的余量,表只有一根。

问:烬为什么当拍判负不留余量?
答:烬是玩家亲眼看着的事实——火星熄了烟断了,判定和画面同一把尺,放水反而乱账。

问:得彩之后为什么自动续香?
答:挑战不断档——香火一路叠,滚雪球的分量玩家自己摸得出来。

问:烬了的惩罚为什么只落空不扣东西?
答:限时挑战罚机会不罚家底——罚重了没人点香。

问:香的演出为什么值得做足?
答:火星青烟把时间画出来——玩家看着余量变短,紧张感是画面给的。

六、调参与实战怎么用

参数层面三个量——香长、彩头增量、目标的血量。香长和血量要一起调:一炷香刚好够斩一只怪是"跳一跳够得着"的线,永远够不着就是劝退。实战里这套一根表的骨架能搬去护送限时、夺旗倒计时、比武回合,全是余量一个数管到底。常见坑两个:展示结算两张表漂移判错;烬后放水半秒,玩家的账从此对不上。

补充一个设计层面的思考——这类伴随式数值的反馈要分成两层看:即时层是每一刀的飘字和小涨,延迟层是攒满之后的那一下质变。即时层管手感,延迟层管目标,两层各发各的,节奏就不糊。怕的是只有即时层没有目标,玩家砍着砍着就腻;也怕只有延迟层没有手感,攒的过程成了干熬。

补充一个维护层面的思考——带计时和进度的状态,卸载的时候要三清:计时的表清零、进度的条清零、演出的残留清掉。清不干净的这两种状态最会闹鬼——重载之后计时器双份跑、进度条带着旧值进新局,玩家看到的现象邪门到没法报。卸载入口一行都不能省。

补充一个工程层面的思考——伴随式系统的结算入口要收成一个:挨打走一个口、命中走一个口,涨多少、判满、给奖全在口子里办。口子收齐了,改数值动一处,查账看一行,回归测一个函数。结算散在流程各处是这类系统闹账的根,收口一次全年清净。

写完留一句给做限时系的同学:玩家盯着香头那点火星的时候,表就长在他们眼睛里——尺子只有一根,烬前烬后才判得让人服气。

作者履历与出处

本文由 996 技术组基于 996 引擎官方知识库与浮生梦老师课程体系整理。团队长期从事传奇类引擎 Lua 后端逻辑、客户端界面与商业版本交付,内容以官方知识库与真实项目为出处,按版本持续修订。

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

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

LATEST ARTICLES

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

进阶实战游戏功能

一把钥匙开全引擎的配置门:MetaValue元变量系统

全服喇叭喊话"恭喜 &<PLAYER_NAME & 获得 &<ITEM_NAME/2001 &",这条公告里的两个占位符是怎…

2026-10-03 11:55 996 技术组
进阶实战游戏功能

ui.按钮名就能拿到控件:LuaExtend元表的八十行魔法

读引擎 UI 代码时你一定会撞见这样的写法: self._quickUI.btnClose 、 SL:GetValue("x…

2026-10-03 11:30 996 技术组
进阶实战游戏功能

断线是谁先发现的:心跳三兄弟与切后台补发

玩家手机锁屏再解锁,游戏还在原地;地铁过隧道断网半分钟,回连后接着玩——这两件"理所当然"背后是 logic/gameWor…

2026-10-03 11:24 996 技术组
进阶实战游戏功能

全引擎的下一拍在这里敲:主循环Update解剖

每个引擎都有一个"心脏":每帧跳动一次、按固定顺序叫醒所有系统的主循环。996 引擎的心脏在 logic/gameWorld…

2026-10-03 11:17 996 技术组
进阶实战游戏功能

元宝数字为什么全屏同步:CostItemCell消耗格台账

玩家买一瓶药,背包角标的元宝、商店界面的元宝、充值面板的元宝三处数字同时跳——这个瞬间几乎没有玩家会注意到,但做客户端的人都…

2026-10-03 11:10 996 技术组
进阶实战游戏功能

每个东西都有专属座位:sceneGraph功能节点树

新接手引擎渲染层的人,打开场景会看到一锅粥:地图、角色、特效、血条、UI 全糊在一起。996 引擎的答案是把场景拆成一张"座…

2026-10-03 11:02 996 技术组