【游戏功能】
双形武的全机制就一句:一念换形,刀快锤沉——刀形一秒一拍一口十一,锤形两秒一拍一口二十四还震得怪半拍出不了手,换形冷却两秒,拍子自己掐。这篇的核心代码照引擎技能脚本的全规格来:配置表、双形态视觉构件、姿态状态机、定时器、调试按钮,一个模块一根骨头都不少。
一、效果演示:两套拍子,一把武器
演示场点「换形」按钮就出发:默认刀形——金色细弧一秒一甩,十一的数字一拍一跳,整个节奏像织布;再点一下换锤形——按钮当场换成橙色,玩家身后甩出一记锤形残影,之后每两秒一记重锤:二十四的大数字、更宽的橙色弧光、震屏,怪头上蹦出"踉跄"——它下一次出手被硬生生拖慢半拍。两种形各自打十秒,总伤害几乎一样,节奏差的不是快慢是性格:刀形十口一百一,锤形五口一百二外加五次踉跄——差别不在伤害在节奏,什么时候换形,就是把两种节奏缝起来的针法——针脚落准了,两种形是一把武器。
fx-twostance
二、底层原理:一张姿态表加一次攻击循环重写
模块的账本是一张姿态表。每个姿态描述三件事:攻击间隔、单口伤害、命中附加——刀形是快而轻、无附加;锤形是慢而重、附加踉跄。换形动作只做一件事:把当前姿态指针切到另一行——攻击循环的下一次读表自然用上新系数,进行中的攻击不中断、已挥出的刀不撤销,形态切换永远从"下一拍"开始生效。踉跄是锤形的附加件:命中的瞬间给怪的攻击计时器加零点五秒、头顶亮半拍踉跄标记——注意是加在计时器上不是直接跳过怪的出手,加法的踉跄可叠加可预判,跳过式的打断就是另一门控制系了。换形冷却两秒卡在刀形的一个拍子和锤形的半个拍子之间——没有冷却的双形会被玩家当成交响乐指挥棒乱挥。
三、核心代码:完整模块(上·骨架)
--------------------------------------------------------------------------------
-- @file TwoStance.lua
-- @brief 双形武 —— 一念换形,刀快锤沉
-- 客户端表现模块:配置表 / 姿态表 / 攻击循环 / 定时器
--------------------------------------------------------------------------------
local TwoStance = {}
--------------------------------------------------------------------------------
-- 配置与常量
--------------------------------------------------------------------------------
local CONST = {
-- 刀形
BLADE_GAP = 1.1, -- 一秒一拍
BLADE_DMG = 11, -- 一口十一
-- 锤形
HAMMER_GAP = 2.0, -- 两秒一拍
HAMMER_DMG = 24, -- 一口二十四
HAMMER_DAZE = 0.5, -- 踉跄加时
-- 换形
SWAP_CD = 2.0, -- 换形冷却
-- 颜色
COLOR_BLADE = "#e3b341", -- 刀金
COLOR_HAMMER = "#f0883e", -- 锤橙
COLOR_GHOST = "#ffd866", -- 残影金
ZORDER_GROUND = 118,
ZORDER_ARC = 134,
ZORDER_EFFECT = 135,
AUTOINC_BASE = 924000,
}
--------------------------------------------------------------------------------
-- 姿态表:两种形各一行
--------------------------------------------------------------------------------
local STANCES = {
blade = { gap = CONST.BLADE_GAP, dmg = CONST.BLADE_DMG,
daze = 0, arc = 28, lw = 4 },
hammer = { gap = CONST.HAMMER_GAP, dmg = CONST.HAMMER_DMG,
daze = CONST.HAMMER_DAZE, arc = 36, lw = 7 },
}
--------------------------------------------------------------------------------
-- 模块内部状态
--------------------------------------------------------------------------------
local _stance = STANCES.blade
local _atkT = 0.6
local _swapCd = 0
local _scheduler = nil
local _autoInc = 0
local m_cos = math.cos
local m_sin = math.sin
--------------------------------------------------------------------------------
-- 辅助函数
--------------------------------------------------------------------------------
local function GenID(prefix)
_autoInc = _autoInc + 1
return string.format("%s_%d", prefix or "TWS", 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 CreateArc(x, y, ang, r, color, lw)
local node = GUI:Layout_Create(GUI:Attach_SceneF(), GenID("Arc"), x, y, 0, 0, false)
GUI:setLocalZOrder(node, CONST.ZORDER_ARC)
GUI:setRotation(node, -math.deg(ang))
for i = -2, 2 do
local a = i * 0.28
GUI:DrawLine_Create(node, string.format("s_%d", i), 0, 0, lw,
m_cos(a - 0.5) * r * 0.8, m_sin(a - 0.5) * r * 0.8,
m_cos(a + 0.5) * r * 0.8, m_sin(a + 0.5) * r * 0.8,
color, 230)
end
GUI:setScale(node, 0.5)
GUI:runAction(node, GUI:ActionSequence(
GUI:ActionSpawn(
GUI:ActionScaleTo(0.14, 1.4, 1.4),
GUI:ActionFadeOut(0.20)
),
GUI:CallFunc(function() SafeRemove(node) end)
))
end
骨架的题眼是 STANCES 这张姿态表:两种形的全部差别浓缩成五行字段——攻击循环读表干活,姿态切换只换指针,新增第三种形态就是加一行表。弧光构件做成参数化的共用件:半径、线宽、颜色全部由姿态行传入,刀的细弧和锤的宽弧是同一个构件的两件衣裳。
四、核心代码:完整模块(下·定时器与攻击循环)
--------------------------------------------------------------------------------
-- 攻击循环:读姿态表干活
--------------------------------------------------------------------------------
local function Stance_Strike(myID, targetID, st)
local mp = GetActorPos(myID)
local tp = GetActorPos(targetID)
if not mp or not tp then return end
local ang = (mp.x < tp.x) and 0.5 or 2.6
CreateArc(mp.x + m_cos(ang) * 26, mp.y - 52, ang, st.arc, st == STANCES.hammer
and CONST.COLOR_HAMMER or CONST.COLOR_BLADE, st.lw)
SL:ActorHurt(targetID, st.dmg)
SL:TipAbove(targetID, "-" .. st.dmg)
if st.daze > 0 then
SL:SlowAttack(targetID, 1.0, st.daze) -- 加时踉跄
SL:TipAbove(targetID, "踉跄")
end
if st == STANCES.hammer then
SL:ScreenShake(4, 0.22)
end
end
--------------------------------------------------------------------------------
-- 换形入口
--------------------------------------------------------------------------------
function TwoStance.Swap()
if _swapCd > 0 then return end
_swapCd = CONST.SWAP_CD
_stance = (_stance == STANCES.blade) and STANCES.hammer or STANCES.blade
ShowTip(_stance == STANCES.blade and "刀形——一秒一拍" or "锤形——两秒一拍")
end
function TwoStance.IsBlade()
return _stance == STANCES.blade
end
--------------------------------------------------------------------------------
-- 定时器与调试
--------------------------------------------------------------------------------
if not _scheduler then
_scheduler = SL:Schedule(function(dt)
_swapCd = math.max(0, _swapCd - dt)
_atkT = _atkT - dt
if _atkT <= 0 then
_atkT = _stance.gap
local myID = SL:GetValue("USER_ID")
local targetID = SL:GetValue("MARK_TARGET_ID")
if targetID then
Stance_Strike(myID, targetID, _stance)
end
end
end, 0.02)
end
SL:ScheduleOnce(function()
ShowTip("技能已加载: 双形武 (调试按钮触发)")
end, 1.0)
SL:RegisterLUAEvent(LUA_EVENT_TARGET_DEAD, "TWS_Dead", function()
_atkT = 0.3
end)
return TwoStance
下半场的攻击循环是全模块最薄的一段——_atkT 每拍递减、归零就按姿态表打一口,打完用姿态行自己的间隔重置计时器:攻击循环里没有任何一行认识"刀"或"锤",它只认识表——表驱动写到底,加第三种形态也不碰循环一行。换形入口只改指针不动计时器——换形的瞬间正在走的攻击间隔保留原形节奏,下一拍才上新形,这个"从下一拍生效"的规矩避免了换形瞬间连发的刷拍漏洞。目标死亡钩子把攻击计时器压到零点三秒,下一只怪刷新立刻接上节奏,磨刀不误砍柴工。
五、调参指南与商业服怎么用
调参的对手戏在两个形的期望伤害:刀形十除以一点一是每秒十,锤形二十四除以二是每秒十二——锤形比刀形贵两成,贵出来的部分用踉跄付账,这个不等式一破,双形就退化成"永远锤形"的单形武。踉跄零点五秒对怪两秒二的出手间隔是四分之一的拖延,五锤下去怪白丢一拍——拖延可叠不可跳,是双形武和打断系的国界。换形冷却两秒卡在刀形的一个拍子和锤形的半个拍子之间——冷却长于三秒,玩家换形前要先算账,手感就从"切歌"变成"填表"。常见坑三个:换形瞬间攻击计时器被重置,玩家卡冷却连拍;踉跄标记没有持续时间,永久慢半拍的怪被玩家当成残血;姿态图标不跟按钮色走,玩家不知道现在是什么形。攻击循环的落空也要有交代:目标已死的那一拍不重置计时器,下一只怪刷新立刻满拍接上,节奏不断电。
商业服里双形武是"武器大师"人设的发动机:一把武器两种形,每种形一套词条,商城的武器皮肤从一件卖成两件。副本里它是节奏关的钥匙——快拍小怪用刀形清、重甲门神用锤形磨,读怪换形的水平就是通关速度。变体做成三形:刀锤弓,远程形收钱,姿态表加一行的成本,付费深度加一档。这一篇的演示和整套模块照抄能跑,改的就是 CONST 和姿态表那两处。
写完留一句给做姿态系的同学:双形卖的是"节奏的选择权"——两张拍子差得越有性格,换形那一念越像指挥。
全站技术干货持续更新:996 引擎 / Lua 实战帖,语法、参数与示例一篇讲透。进入文章地图 · 查看全部 →
【游戏功能】 if _shadowT 0 then spd = noShadow and 12 or 24 end ——一行…
【游戏功能】 魂卫的全机制就一句:杀掉小怪化一盏魂火跟着你走,每盏魂火两秒自动劈最近的大怪一口八点,至多三盏——「引魂!」按…
【游戏功能】 一道咒飞过来,硬吃是四秒里一秒十的钝疼——有没有第三条路?纸人替灾给的就是第三条路:贴一个纸人在身后,八秒里头…
【游戏功能】 挨打还能赚钱?水镜式的答案是:挨的刀七成存进镜里不扣血、三成照常疼,六秒后点「还镜」,存量乘一点五放成水炮——…
【游戏功能】 if _mirrorT 0 then SL:TipAboveMe("免疫") return end ——一道写…
【游戏功能】 远近战的矛盾怎么解?远战的答案是把怪挡在射程外,近战的答案是把人拖进射程内——缩地阵给的第三个答案最蛮横:圈只…