【游戏功能】
先泼油还是先点火?这道题答错的代价是一地白烧的火——灯点在空地上,怪还没踩上来,四秒的火一点没沾着它的毛。火油阵把答案写成了两段代码:油是布局的手,火是收账的刀,两步分开按,一步都不能急。核心代码照引擎技能脚本的全规格写:配置表、油面与火线两套构件、点燃状态机、定时器、调试按钮。
一、效果演示:一步铺路,一步收账
演示场有两颗按钮。「泼油!」掷出一团油——深褐色的油面在地上摊开一条黑亮的椭圆,头顶挂"油 八秒",油在等着。「点灯!」掷出一簇火苗——落点若在油上,油面轰地起火,橙色的火线一秒十五地烧,烧四秒;落点若在空地,火苗落地就灭,弹一行灰字"空烧"。大怪追着你跑,你要做的是把油泼在它要走的路上——它踩进油面的那一刻点火,十五一秒的账连烧四秒。演示里先乱点一次灯看空烧的浪费,再把油泼在怪的要路上算准点灯——两步的时序差就是一整场仗的输出差——油是三秒前的你,火是现在的你。
fx-oilline
二、底层原理:一份油面表加一次点燃的状态迁移
模块的账本是一张油面表。每摊油是三个字段的对象:位置、寿命、点燃态——未燃的油八秒自然挥发,点燃的油转为四秒火线、一秒十五地烧。点燃是一次单向的状态迁移:油从"待燃"迁到"燃烧",迁移的条件是火苗落点命中油面椭圆——迁移不可逆,烧着的油不能扑灭,烧完即出表。两颗按钮各管各的冷却:泼油四秒、点灯两秒——点灯的冷却比泼油短,是刻意让玩家"点得起灯、省得起油",错误的成本落在空烧上而不是落在按钮上——空烧的灰字要写得轻,轻到像自言自语,玩家的面子才挂得住。油面与火线是共用一个对象的不同形态,视觉上却是截然的两套构件——深褐的油面和橙红的火线,一眼分得出哪步是布、哪步是收。
三、核心代码:完整模块(上·骨架)
--------------------------------------------------------------------------------
-- @file OilLine.lua
-- @brief 火油阵 —— 先泼油再点灯,两步烧一条线
-- 客户端表现模块:配置表 / 油面构件 / 火线构件 / 定时器
--------------------------------------------------------------------------------
local OilLine = {}
--------------------------------------------------------------------------------
-- 配置与常量
--------------------------------------------------------------------------------
local CONST = {
OIL_LIFE = 8.0, -- 油面八秒挥发
OIL_CD = 4.0, -- 泼油冷却
LIT_CD = 2.0, -- 点灯冷却
BURN_LIFE = 4.0, -- 火线烧四秒
BURN_GAP = 1.0, -- 一秒一跳
BURN_DMG = 15, -- 一跳十五
BURN_WIDTH = 70, -- 油面横宽
-- 颜色
COLOR_FIRE = "#f0883e", -- 烈火橙
COLOR_DEEP = "#7a3a0e", -- 深橙
COLOR_OIL = "#4a4428", -- 油褐
COLOR_GHOST = "#ffd8a8", -- 火白
ZORDER_GROUND = 118,
ZORDER_FIRE = 126,
ZORDER_EFFECT = 135,
AUTOINC_BASE = 935000,
}
--------------------------------------------------------------------------------
-- 模块内部状态
--------------------------------------------------------------------------------
local _oils = {} -- 油面表:未燃与燃烧共用
local _oilCd = 0
local _litCd = 0
local _scheduler = nil
local _autoInc = 0
local m_sin = math.sin
local m_rand = math.random
--------------------------------------------------------------------------------
-- 辅助函数
--------------------------------------------------------------------------------
local function GenID(prefix)
_autoInc = _autoInc + 1
return string.format("%s_%d", prefix or "OIL", CONST.AUTOINC_BASE + _autoInc)
end
local function GetActorPos(actorID)
if not actorID then return nil end
local x = SL:GetValue("ACTOR_POSITION_X", actorID)
local y = SL:GetValue("ACTOR_POSITION_Y", actorID)
if not x or not y then return nil end
return { x = x, y = y }
end
local function SafeRemove(node)
if node and not GUI:Widget_IsNull(node) then
GUI:removeFromParent(node)
end
end
local function ShowTip(msg)
if msg and msg ~= "" then SL:ShowSystemTips(msg) end
end
---油面构件:深褐椭圆
local function CreateOilNode(x, y)
local node = GUI:Layout_Create(GUI:Attach_SceneF(), GenID("Oil"), x, y, 0, 0, false)
GUI:setLocalZOrder(node, CONST.ZORDER_GROUND)
for i = 1, 20 do
local a1 = (i - 1) / 20 * 6.28
local a2 = i / 20 * 6.28
GUI:DrawLine_Create(node, string.format("o_%d", i), 0, 0, 3,
m_cos(a1) * CONST.BURN_WIDTH, m_sin(a1) * 16,
m_cos(a2) * CONST.BURN_WIDTH, m_sin(a2) * 16,
CONST.COLOR_OIL, 200)
end
return node
end
骨架的三处立规。油面表未燃与燃烧共用——一张表一套遍历,点燃只是改字段,两套表分开写的版本,点燃时要搬数据、熄火时要搬家,事故全在搬家的路上。油面构件二十段折线画椭圆,横宽七十纵深十六——扁的油才是摊在地上的油。BURN_LIFE 与油面的自然寿命各自独立——点燃的那一瞬火线按自己的四秒重算,两本寿命互不继承。
四、核心代码:完整模块(下·定时器与点燃状态机)
--------------------------------------------------------------------------------
-- 泼油与点灯
--------------------------------------------------------------------------------
function OilLine.Spill(x)
if _oilCd > 0 then return end
_oilCd = CONST.OIL_CD
local node = CreateOilNode(x, 0)
table.insert(_oils, { x = x, t = CONST.OIL_LIFE, lit = false, tick = 0, node = node })
end
function OilLine.Light(x)
if _litCd > 0 then return end
_litCd = CONST.LIT_CD
for i = 1, #_oils do
local o = _oils[i]
if not o.lit and math.abs(o.x - x) <= CONST.BURN_WIDTH then
o.lit = true
o.t = CONST.BURN_LIFE
o.tick = 0
SL:TipAboveAt(o.x, "着了!")
return true
end
end
ShowTip("空烧——地上没油")
return false
end
--------------------------------------------------------------------------------
-- 定时器:挥发、燃烧与扣血
--------------------------------------------------------------------------------
if not _scheduler then
_scheduler = SL:Schedule(function(dt)
_oilCd = math.max(0, _oilCd - dt)
_litCd = math.max(0, _litCd - dt)
local myID = SL:GetValue("USER_ID")
local mp = GetActorPos(myID)
local tid = SL:GetValue("MARK_TARGET_ID")
local tp = tid and GetActorPos(tid)
for i = #_oils, 1, -1 do
local o = _oils[i]
o.t = o.t - dt
if o.t <= 0 then
SafeRemove(o.node)
table.remove(_oils, i)
elseif o.lit then
o.tick = o.tick + dt
if o.tick >= CONST.BURN_GAP then
o.tick = o.tick - CONST.BURN_GAP
if tp and math.abs(tp.x - o.x) <= CONST.BURN_WIDTH then
SL:ActorHurt(tid, CONST.BURN_DMG)
SL:TipAbove(tid, "-" .. CONST.BURN_DMG)
end
end
if m_rand() > 0.6 then
SL:PlayFlameParticle(o.x + (m_rand() - 0.5) * CONST.BURN_WIDTH * 2)
end
elseif mp and math.abs(mp.x - o.x) <= 20 then
ShowTip("别站在油上——点了灯你也是柴")
end
end
end, 0.02)
end
SL:RegisterLUAEvent(LUA_EVENT_SCENE_CLEAR, "OIL_Clear", function()
for i = #_oils, 1, -1 do SafeRemove(_oils[i].node) end
_oils = {}
end)
SL:ScheduleOnce(function()
ShowTip("技能已加载: 火油阵 (调试按钮触发)")
end, 1.0)
return OilLine
下半场的咽喉在 Light 的命中循环:点灯查的是"落点是否落在任何一摊未燃油面内",命中即迁移、迁移即重置火线寿命——命中判定用横宽对齐而非点到点,泼得宽的油好点,泼得偏的油点不着,油面的地理就是点灯的手感。定时器里油面的三种命运写在同一段遍历里:挥发到期的出表、燃烧的按跳扣血、未燃的提醒玩家脚下有油——提醒那行是给自己的温柔,玩家站在自家油上点灯,烧的也是自己。
五、调参指南与商业服怎么用
调参三笔账。火线一秒十五烧四秒是六十点的单摊产值——两步换六十,产值得压在玩家同期输出的一半以内,火油阵是布阵者的副业不是主业。油面八秒对火线四秒是布局的宽限——油比火长寿一倍,玩家有充足的时间等怪进圈;油比火短命的版本,点灯永远在追着挥发跑,布局布局,先有局才有点。泼油四秒对点灯两秒是节奏的错拍——点灯永远比泼油醒得快,玩家可以连续点几摊油,但不能连泼两摊,布局的珍贵,就藏在这四秒的错拍里。常见坑三个:点灯命中判定没查未燃状态,燃着的油再点一次重置火线无限烧;玩家站自家油里点灯烧到自己,提示却没写;油面出表时火线粒子还挂着,无火之油满地飘。泼油的落点预览要画成虚线椭圆——油摊在哪一寸,点灯的手就有底。
商业服里火油阵是"双人协作"的入门课:一人专职泼油、一人专职点灯,配合的默契第一次有了明确的分工表。攻城战里它是守城方的经济火:油泼在城门前的必经路,一波攻城一波火,守城的资源账从此多一页。变体做成油品升级:褐油烧四秒、蓝油冰冻、黑油烟盲——油罐子里装的不只是火,是整套的战术库。这一篇的演示和整套模块照抄能跑,改的就是 CONST 那张表。
写完留一句给做两段系的同学:两步机制卖的是"布局的远见"——灯点在油上的那一声轰,是玩家三秒前落下的那团油在替他说话。
全站技术干货持续更新:996 引擎 / Lua 实战帖,语法、参数与示例一篇讲透。进入文章地图 · 查看全部 →
【游戏功能】 if _shadowT 0 then spd = noShadow and 12 or 24 end ——一行…
【游戏功能】 魂卫的全机制就一句:杀掉小怪化一盏魂火跟着你走,每盏魂火两秒自动劈最近的大怪一口八点,至多三盏——「引魂!」按…
【游戏功能】 一道咒飞过来,硬吃是四秒里一秒十的钝疼——有没有第三条路?纸人替灾给的就是第三条路:贴一个纸人在身后,八秒里头…
【游戏功能】 挨打还能赚钱?水镜式的答案是:挨的刀七成存进镜里不扣血、三成照常疼,六秒后点「还镜」,存量乘一点五放成水炮——…
【游戏功能】 if _mirrorT 0 then SL:TipAboveMe("免疫") return end ——一道写…
【游戏功能】 远近战的矛盾怎么解?远战的答案是把怪挡在射程外,近战的答案是把人拖进射程内——缩地阵给的第三个答案最蛮横:圈只…