【游戏功能】
box.charge = math.min(box.charge + 8, box.cap)——一行充能封顶的写法,撑起了蓄雷匣全部的账本:每一刀给匣子充八格雷,八十格封顶,闲两秒开始漏电;点「放雷」把存量乘一点二一口砸出去。攒雷不是攒怒气——怒气是白给的,雷是拿普攻的每一拍换的。这篇的核心代码按引擎技能脚本的全规格写:配置表、闪电链视觉构件、充能漏电的定时器、调试按钮,一个模块的骨头一根不少。
一、效果演示:八十格雷,一口清算
演示场点不点按钮都在打:普攻每一刀命中,玩家身侧的雷匣亮起一格蓝光、头顶跳"+8雷",八十格封顶;两秒不出手,匣子开始漏电——蓝粒一粒粒往上飘,存量一格一格掉。点「放雷!」:七节闪电从匣子一路跳到大怪身上,节节带光斑,落定一口大数字——攒八十放九十六,攒三十只放三十六。试两条路:攒满八十再放,和一够三十就放——前者的那一口能直接咬掉大怪一成血,后者的雷雨密但每口都细。什么时候放,就是这只匣子的全部学问——攒是纪律,放是时机,两只手各管一样。
fx-thunderbox
二、底层原理:一份存量加一条漏电曲线
模块的账本是一个带损耗的电容。充能侧:命中钩子往存量加八、用封顶函数压住上限;漏电侧:闲置计时器超过两秒后按每秒三格损耗——漏电是这套机制和普通怒气条的分水岭,怒气只涨不跌,雷匣越攒越亏,逼着玩家"要么攒满要么趁早"。释放侧是一次乘法清算:存量乘一点二作为单发伤害,清算完归零,清算是整套机制里唯一一笔大进大出的账——乘一点二而不是原额返还,是给"攒"这个行为的利息;利息只有三成,攒雷的收益才不会盖过普攻本身,普攻永远是一号位。三条时间线——充能、漏电、冷却——全部挂在模块唯一的一条定时器上,两百分之一秒一拍,一拍不多开。
三、核心代码:完整模块(上·骨架)
--------------------------------------------------------------------------------
-- @file ThunderBox.lua
-- @brief 蓄雷匣 —— 打它攒雷,攒满一键全放
-- 客户端表现模块:配置表 / 闪电链构件 / 电容状态机 / 定时器
--------------------------------------------------------------------------------
local ThunderBox = {}
--------------------------------------------------------------------------------
-- 配置与常量
--------------------------------------------------------------------------------
local CONST = {
CHARGE_PER_HIT = 8, -- 一刀充八格
BOX_CAP = 80, -- 匣子八十格封顶
RELEASE_MIN = 30, -- 三十格起放
RELEASE_RATE = 1.2, -- 释放乘一点二
LEAK_DELAY = 2.0, -- 闲两秒开始漏
LEAK_RATE = 3.0, -- 每秒漏三格
BOLT_SEGMENTS = 7, -- 闪电七节
BOLT_STEP_GAP = 0.042, -- 每节间隔 (秒)
-- 颜色
COLOR_BOLT = "#58a6ff", -- 雷蓝
COLOR_DEEP = "#1f4f8f", -- 深蓝
COLOR_CORE = "#c8e1ff", -- 电白
ZORDER_BOX = 130,
ZORDER_BOLT = 134,
ZORDER_EFFECT = 135,
AUTOINC_BASE = 922000,
}
--------------------------------------------------------------------------------
-- 模块内部状态
--------------------------------------------------------------------------------
local _box = { charge = 0 }
local _idleT = 0
local _scheduler = nil
local _autoInc = 0
local m_sin = math.sin
local m_cos = math.cos
local m_rand = math.random
--------------------------------------------------------------------------------
-- 辅助函数
--------------------------------------------------------------------------------
local function GenID(prefix)
_autoInc = _autoInc + 1
return string.format("%s_%d", prefix or "TBX", 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 CreateBoltNode(x, y, seg)
local node = GUI:Layout_Create(GUI:Attach_SceneF(), GenID("Bolt" .. seg), x, y, 0, 0, false)
GUI:setLocalZOrder(node, CONST.ZORDER_BOLT)
local r = 6 + (seg % 2) * 4
for i = 1, 8 do
local a1 = (i - 1) / 8 * 6.28
local a2 = i / 8 * 6.28
GUI:DrawLine_Create(node, string.format("b_%d", i), 0, 0, 2,
m_cos(a1) * r, m_sin(a1) * r, m_cos(a2) * r, m_sin(a2) * r,
seg % 2 == 0 and CONST.COLOR_CORE or CONST.COLOR_BOLT, 220)
end
GUI:runAction(node, GUI:ActionSequence(
GUI:ActionFadeOut(0.20),
GUI:CallFunc(function() SafeRemove(node) end)
))
return node
end
骨架先记三件事。_box 是表不是数——存量的字段、上限、漏电状态迟早要加兄弟,开局就给壳子。RELEASE_MIN 单独一个常量立着而不是写死在判断里,调参的时候它和 BOX_CAP 的比值才是这套机制的手感。CreateBoltNode 一节一个节点、八段折线一个圈、自带零点二秒的自毁动作——闪电链就是七节这样的光斑按间隔排着队炸。
四、核心代码:完整模块(下·定时器与电容状态机)
--------------------------------------------------------------------------------
-- 充能与漏电
--------------------------------------------------------------------------------
local function Box_OnHit()
_box.charge = math.min(_box.charge + CONST.CHARGE_PER_HIT, CONST.BOX_CAP)
_idleT = 0
end
local function Box_Update(dt)
_idleT = _idleT + dt
if _idleT > CONST.LEAK_DELAY and _box.charge > 0 then
_box.charge = math.max(0, _box.charge - CONST.LEAK_RATE * dt)
end
end
--------------------------------------------------------------------------------
-- 释放:闪电链与清算
--------------------------------------------------------------------------------
local function Bolt_Fire(fromPos, toPos)
for seg = 1, CONST.BOLT_SEGMENTS do
local k = seg / CONST.BOLT_SEGMENTS
local lx = fromPos.x + (toPos.x - fromPos.x) * k
local ly = fromPos.y - 58 - m_sin(k * 3.14) * 40 + (m_rand() - 0.5) * 22
SL:ScheduleOnce(function()
CreateBoltNode(lx, ly, seg)
end, (seg - 1) * CONST.BOLT_STEP_GAP)
end
end
function ThunderBox.Release(myID, targetID)
if _box.charge < CONST.RELEASE_MIN then
ShowTip("雷不足" .. CONST.RELEASE_MIN .. "格")
return
end
local fromPos = GetActorPos(myID)
local toPos = GetActorPos(targetID)
if not fromPos or not toPos then return end
local vd = math.round(_box.charge * CONST.RELEASE_RATE)
_box.charge = 0
Bolt_Fire(fromPos, toPos)
SL:ScheduleOnce(function()
SL:ActorHurt(targetID, vd)
ShowTip("放雷 " .. vd)
end, CONST.BOLT_SEGMENTS * CONST.BOLT_STEP_GAP)
end
--------------------------------------------------------------------------------
-- 命中钩子与定时器
--------------------------------------------------------------------------------
SL:RegisterLUAEvent(LUA_EVENT_LOCAL_HIT, "TBX_Hit", function(data)
if data and data.isNormal then
Box_OnHit()
end
end)
if not _scheduler then
_scheduler = SL:Schedule(function(dt)
ThunderBox.Update(dt)
Box_Update(dt)
end, 0.02)
end
SL:ScheduleOnce(function()
ShowTip("技能已加载: 蓄雷匣 (调试按钮触发)")
end, 1.0)
return ThunderBox
下半场两处见功夫。闪电链的排程用 ScheduleOnce 一节一挂——七节七条延时任务,节距四十二毫秒,比在一条定时器里记步数的写法干净;清算也挂一条延时,雷走到头那一帧才落伤害,光和账同帧到。命中钩子 LUA_EVENT_LOCAL_HIT 只认普攻——技能命中也充能的版本,雷匣就变成技能循环的挂件,普攻重新有了存在感。闪电链的落点高度做了正弦抬升,七节光斑走一道拱桥而不是一条直线——电会绕路,直线的雷是激光。漏电的曲线是直线每秒三格,你要更狠就改成超线性:闲置越久漏得越快,拖延症的雷匣会被电工直接没收。
五、调参指南与商业服怎么用
调参三笔账。八格一刀对八十格封顶是十刀满匣——对一点二秒的普攻间隔是十二秒的积累,比大多数战斗的一个回合略长,攒得到又不能无脑攒;漏电每秒三格对两秒延迟,意味着满匣放三十秒后归零,"攒着过年"这条路被数学堵死;释放乘一点二是利息线,利息高于普攻的两倍收益时玩家会只攒不放,普攻沦为充电动作——乘一点五就是警戒线。常见坑三个:释放后没有清闲置计时,匣子空了还在漏,负数存量在界面显示成鬼影;闪电链的节距配了零点二秒以上,雷飞得比人走路慢;命中钩子把玩家的空刀也算充能,对着空气挥刀充满匣子。匣子的显示还有一课:存量条要挂在玩家身侧跟着走,钉死在屏幕角落的版本,玩家放了雷才发现匣子早就空了。
商业服里蓄雷匣是"平砍复兴"的旗手:普攻有了第二份收益,攻速装备的价值直接翻页,商城的攻速称号换了个理由涨价。副本里它是爆发排班的工具——攒满的匣子留着给头目的破甲期,全队的雷同一秒落地,那口集火的数字就是行会排面。变体做成双人雷匣:队友的普攻也给你充能,一人攒一人放,配对的默契明码标价。这一篇的演示和整套模块照抄能跑,改的就是 CONST 那张表。
写完留一句给做存量系的同学:电容类机制卖的是"攒与放"的节奏——漏电曲线画得越狠,放雷那一口越是玩家自己挣的。
全站技术干货持续更新:996 引擎 / Lua 实战帖,语法、参数与示例一篇讲透。进入文章地图 · 查看全部 →
【游戏功能】 安全巡检扫出一笔可疑的挂账:玩家的"周围单位结算"列表里,同一只怪每零点八秒出现一次,可战斗记录里玩家根本没出…
【游戏功能】 困灵阵的全机制就一句:阵落在它脚下,八秒里它就是圈中之物——敢跨出圈沿,一步吃十五还被弹回去,你在圈外安安稳稳…
【游戏功能】 先泼油还是先点火?这道题答错的代价是一地白烧的火——灯点在空地上,怪还没踩上来,四秒的火一点没沾着它的毛。火油…
【游戏功能】 寻路组收到一笔诡异的工单:怪物巡逻到某个坐标段必卡三秒,像踩了看不见的门槛——排查两小时,发现那段路上有一个上…
【游戏功能】 local a, b = math.min(x1, x2), math.max(x1, x2) ——一行把两根…
【游戏功能】 测试组报过一口邪门的单:头目的读条明明被打断过,技能照样出了膛——查了半天不是打断失效,是打断只"清了"不"冻…