【游戏功能】
隐蔽报错引入:一个很隐蔽的报错——火纹明明烧尽了,地上的圈还亮着,怪走过去一滴血没掉,玩家对着不冒火的圈骂了半天。追到底火圈的账和圈的画面各存了一份,烧尽只清了账没清画,屏上的圈成了骗人的装饰。这篇把烙地炎纹整套写法重写成篇:落烙起烧、路过就烧、挪烙追身、烧尽清场,一段一段照抄能跑。
一、效果演示
演示场的道面上可以烙火纹。点「烙地!」:一圈火纹烙在道上,烧六秒,怪进场路过时脚底下秒秒冒火星,半秒烧它八点。怪停脚了也别急,点「挪烙」把火圈直接挪到它脚底下——站着也得挨烧。六秒烧尽圈自动消,八秒地气一缓又能再烙。烙在哪、什么时候烙、要不要追着挪,猎手的算计全在这几秒里。
flowchart TD
A[点烙地] --> B{地气缓过来了}
B -- 没有 --> C[拦下等CD]
B -- 好了 --> D[火纹落道 烧六秒]
D --> E{怪在圈里吗}
E -- 是 --> F[半秒烧八点]
E -- 否 --> E
F --> G{烧尽了吗}
G -- 没有 --> E
G -- 烧尽 --> H[圈消账清]
I[挪烙] --> J[火圈挪到怪脚下]
fx-crest-burnzone
二、底层原理
烙地系统的机关是"圈账一体"——火圈的余秒就是它的命,烧尽的那一拍圈和账一起消,屏上不留一个不冒火的装饰圈。那场报错的病根就是账画两本:烧尽清了账没清画,玩家对着死圈等火星。挪烙是这套玩法的主动档:把圈挪到怪脚底下,站桩怪也得挨烧——烙在道上赚路费,烙在脚下收全款,一挪之间烙地从碰运气变成了追着烧的手艺。
灼烧的账跟位置走:怪在圈里才烧,半秒八点,出圈立停——烧的是"踩着"这件事本身,不是给怪挂一个长期的火标。这个区分是烙地和普通灼烧的分野:烙地烧的是位置关系,位置散了火就断。地气八秒一缓是节奏的闸,烙地不能铺成火海,一口一口的烧才有排布的算计。幂尔框架这边圈的余秒、怪的位置、烧的节拍收在每拍推进里,落烙挪烙各一个口子,圈消账清一拍完成。
这套玩法的代码量不到八十行,难的不是放火而是圈账一体——圈是画面也是账本,烧尽那拍谁也不许留。
再看挪烙为什么允许追着怪挪——火纹按理是烙死在地上的,能追身挪动会不会太赖?恰恰是这一挪把烙地从"摆好等路过"变成了"跟着盯烧"的主动玩法:挪烙花的是同一个冷却,挪到脚下和烙在道上是两个决策,算计的深度反而厚了。要是不许挪,火圈的六秒里玩家有大半时间在干看——给一桩布置玩法一条主动的手筋,等待就变成了操作。
三、核心代码:完整模块(上·骨架)
-- @file CrestBurnzone.lua
-- 烙地炎纹 —— 地上烙一圈 踩着就烧
local CrestBurnzone = {}
local CONST = {
DURATION = 6,
BURN_PER = 8,
TICK = 0.5,
GROUND_CD = 8,
ZONE_HALF = 48,
AUTOINC_BASE = 1201100,
}
local _x = 440
local _left = 0
local _tick = 0
local _cd = 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 CrestBurnzone.Left() return _left end
function CrestBurnzone.Pos() return _x end
-- 落烙: 圈账一起立
function CrestBurnzone.Place()
if _cd > 0 then
ShowTip("地气还烫着——再等" .. math.ceil(_cd) .. "秒")
return
end
_cd = CONST.GROUND_CD
_left = CONST.DURATION
_tick = 0
ShowTip("烙下了——烧" .. CONST.DURATION .. "秒")
end
四、核心代码:完整模块(下·推进与卸载)
-- 挪烙: 圈挪账不熄
function CrestBurnzone.MoveTo(nx)
if _left <= 0 then
ShowTip("先烙一处再挪")
return
end
_x = nx
ShowTip("火纹挪到它脚底下——烧它个正着")
end
-- 每拍推进: 圈内就烧 圈消账清
function CrestBurnzone.Tick(dt, mobX)
if _cd > 0 then _cd = _cd - dt end
if _left <= 0 then return 0 end
_left = _left - dt
_tick = _tick - dt
local hit = 0
if _tick <= 0 and math.abs(mobX - _x) < CONST.ZONE_HALF then
_tick = CONST.TICK
hit = CONST.BURN_PER
end
if _left <= 0 then
ShowTip("火纹烧尽了——圈消账清")
end
return hit
end
if not _ticker then
_ticker = SL:Schedule(function(dt)
CrestBurnzone.Tick(dt, SL:GetValue("mobX") or 0)
end, 0.05)
end
SL:ScheduleOnce(function()
SL:BindDebugButton("烙地!", function()
CrestBurnzone.Place()
end)
SL:BindDebugButton("挪烙", function()
CrestBurnzone.MoveTo(SL:GetValue("mobX") or 440)
end)
ShowTip("技能已加载: 烙地炎纹")
end, 1.0)
function CrestBurnzone.Unload()
if _ticker then SL:Unschedule(_ticker) end
_left = 0
end
return CrestBurnzone
五、机制问答
问:不冒火的圈骂了半天的根因是什么?
答:账画两本,烧尽清账没清画——正解是圈账一体,烧尽那一拍一起消。
问:灼烧为什么跟着位置不走火标?
答:烙地烧的是踩着这件事——位置散了火就断,和挂身上的长期灼烧是两码事。
问:挪烙追身会不会太赖?
答:一挪把等待变操作——烙道上赚路费挪脚下收全款,算计的深度反而厚了。
问:地气八秒一缓管什么?
答:烙地不铺火海——一口一口的烧才有排布的算计。
问:灼烧的节拍为什么定在半秒?
答:火星一蹦一蹦看得见——连成一条灼烧条反而没了"踩一下烧一下"的位置感。
六、调参与实战怎么用
参数层面四个量——圈径、节拍、单跳灼烧、地气冷却。圈径卡在怪身位一倍半:太小踩不实太大成火海。实战里这套圈账一体的骨架能搬去毒沼、冰面、电网区域,全是落圈、踩烧、尽灭一条线。常见坑两个:账画两本,烧尽留死圈;灼烧挂火标不认位置,烙地成了普通毒。
补充一个设计层面的思考——主动型机制的反馈要"先给判断依据,再给判定的结果":预警先亮、窗口先画、结果后到,玩家输赢都输赢在自己看得懂的信息上。判定依据藏起来的主动机制是运气测试器,玩家赢了不知道为什么赢,输了不知道该改什么——信息给足,操作才有成长感。
补充一个维护层面的思考——带位置和窗口的状态,重载的时候要跟着账本一起走:火圈烧到第几秒、窗口还剩几刀、闪身的朝向停在哪一边,这些都是账,不是画面。画面可以重建,账不能丢也不能多——重载后圈还在账没了、账还在圈没了,都是清场清了一半的活。
补充一个工程层面的思考——延后结算的跟随判定要带自检:影子补的那一刀、反噬的那一记,落地前先看怪还在不在场、窗口还开不开,条件不成立就安静作废。延后的结算最会撞上"世界已经变了"的尴尬,每一次延后都要自己重新问一遍前提。
写完留一句给做地形系的同学:地上的圈是画面也是账本——烧尽那一拍一起消,玩家脚底下踩的每一格火星都作数。
全站技术干货持续更新:996 引擎 / Lua 实战帖,语法、参数与示例一篇讲透。进入文章地图 · 查看全部 →
接手二开的兄弟几乎都撞过同一堵墙:玩家嫌走路"一格一格像机器人",老板拍板要"丝滑移动",改服务端速度?不对。改客户端动画帧…
【游戏功能】 先看一个熟场面:法师的火球特效挂在怪的脚后跟上,明明技能说明写着"焚烧其面",火星子却在地上打滚。群里有位策划…
【游戏功能】 上周有个开服的老板在群里发了一段录屏:他的战士明明刀刀都砍中了,观众却一片"这刀挥空了吧"。他原话是"判定日志…
【游戏功能】 先说结论放在开头:怪的皮一会儿绿一会儿红,不是美术换了几套贴图,是染色系统在怪身上盖了一层"色镜"。开服群里有…
前两天群里有个架设的朋友问了个需求:中毒的人要变绿、每两秒掉一次血、毒还能叠五层,问是不是要在服务端把整套状态机重写。我说你…
【游戏功能】 先抛一个坑:五口钟一模一样大,为什么一口比一口疼?先后次序动不得吗?倒过来敲会怎样?这篇把编钟纹整套写法拆开:…