【语法算法】
上一版留了排"齐步走"的图标的隐蔽报错:一排波浪排布的图标同起同落——设计稿上是此起彼伏的波浪,实机上整排图标像心跳一样齐刷刷地蹦——蹦得越齐越显瘆人,设计稿上那条活波浪死活出不来。追到底相位没按下标错开:九个图标共用一个正弦,振幅调得再花也是整齐划一的蹦迪。修完给每个图标按下标加一份相位偏移,波浪才活过来——活过来的波浪一寸一寸地荡,荡出来的全是错拍的功夫。这篇把整改后的整套写法拆开:配置表、错落相位、正弦排布、参数轮换、定时器、调试按钮,一段一段照抄能跑。
一、效果演示:相位错开的错落动画
演示场九枚图标排成一行。开场就是波浪:每枚图标按自己的相位上下浮——左边的刚升到顶,右边的还在谷底,此起彼伏。点「换波幅」:波幅在六、十二、二十二之间轮换——波幅越大起伏越夸张。点「换流速」:流速在三档轮换——慢速像水草,快速像沸腾。演示里试一笔账:相位错开前后的对比——同起同落的一排是心跳图,错开的一排才是波浪,一字之差天壤之别。波浪排布教的是错拍:排布的活气全在相位的错落里,同一个公式,错开一拍就是另一个世界——世界换得便宜,才值得反复换。
flowchart TD
A[每一拍读时钟] --> B[逐个图标算相位]
B --> C[相位 = 时钟乘流速 加下标乘错距]
C --> D[纵偏 = 正弦乘波幅]
D --> E[图标落在基准加纵偏]
E --> A
fx-wavelayout
二、底层原理:一份错落相位加一列正弦排布
模块的机关是一对搭档。错落相位:每个图标的相位是时钟乘流速再加下标乘错距——下标就是相位的错开量,相邻图标永远差着半个拍子,波浪的起伏就是这么错出来的。正弦排布:每个图标的纵偏是相位走正弦乘波幅——正弦的平滑保住起伏的体面,波幅定起伏的深浅。和随机抖动的分野在"波浪的错落是秩序的错落":随机抖动是各自乱蹦,波浪是错拍但同律——相位错开、频率一致,此起彼伏里藏着一条看不见的队形——队形看不见但摸得着,乱中有序三个字全给它。
相位错距为什么是零点五五?错距取整倍数会退化成两两组队齐跳,取整圆则整排同相——零点五五不是整圆的分数,相邻图标永远差着小半拍,波浪才有绵延不断的推进感。
三、核心代码:完整模块(上·骨架)
-- @file WaveLayout.lua
-- 波浪排布 —— 相位错开的错落动画
local WaveLayout = {}
local CONST = {
ITEM_COUNT = 9,
ITEM_GAP = 65,
BASE_Y = 150,
AMPS = { 6, 12, 22 },
SPEEDS = { 1.5, 3, 6 },
PHASE_STEP = 0.55,
AUTOINC_BASE = 1159000,
}
local _ampIdx = 2
local _spdIdx = 2
local _icons = {}
local _scheduler = nil
local _autoInc = 0
local function GenID(prefix)
_autoInc = _autoInc + 1
return string.format("%s_%d", prefix or "WVL",
CONST.AUTOINC_BASE + _autoInc)
end
local function ShowTip(msg)
if msg and msg ~= "" then SL:ShowSystemTips(msg) end
end
function WaveLayout.Amp()
return CONST.AMPS[_ampIdx]
end
function WaveLayout.Speed()
return CONST.SPEEDS[_spdIdx]
end
function WaveLayout.OffsetY(idx, clock)
local phase = clock * WaveLayout.Speed()
+ idx * CONST.PHASE_STEP
return math.sin(phase) * WaveLayout.Amp()
end
四、核心代码:完整模块(下·逐个排布与参数轮换)
-- 逐个排布:每个图标按自己的相位落位
if not _scheduler then
_scheduler = SL:Schedule(function(dt)
pt = pt + dt
for idx = 0, CONST.ITEM_COUNT - 1 do
local node = _icons[idx]
if node and not GUI:Widget_IsNull(node) then
local off = WaveLayout.OffsetY(idx, pt)
GUI:setPosition(node,
70 + idx * CONST.ITEM_GAP,
CONST.BASE_Y + off)
end
end
end, 0.02)
end
function WaveLayout.CycleAmp()
_ampIdx = _ampIdx % #CONST.AMPS + 1
ShowTip("波幅换成" .. WaveLayout.Amp())
end
function WaveLayout.CycleSpeed()
_spdIdx = _spdIdx % #CONST.SPEEDS + 1
ShowTip("流速换成" .. WaveLayout.Speed())
end
SL:ScheduleOnce(function()
for idx = 0, CONST.ITEM_COUNT - 1 do
local node = GUI:Layout_Create(GUI:GetRootWindow(),
GenID("Icon"), 70 + idx * CONST.ITEM_GAP,
CONST.BASE_Y, 48, 48, false)
_icons[idx] = node
end
ShowTip("技能已加载: 波浪排布 (调试按钮触发)")
end, 1.0)
function WaveLayout.Unload()
if _scheduler then SL:Unschedule(_scheduler) end
for _, node in pairs(_icons) do
if node and not GUI:Widget_IsNull(node) then
GUI:removeFromParent(node)
end
end
_icons = {}
end
return WaveLayout
五、机制问答
问:错距取整会怎样?
答:队形分裂——错距取整倍数时图标两两同相,波浪退化成两组齐跳,半拍的距离才是错落的命。
问:波幅和流速哪个改起来风险大?
答:流速——流速快到一定程度相邻图标会在视觉上抖成频闪,波幅只管深浅不管乱,动流速前先降一档波幅。
问:图标多了排不下怎么办?
答:流水折行——一行摆满折到下一行接着错相位,相位的下标是全局连着的,折行折不断波浪。
问:波浪的基准线要动吗?
答:可以动——基准线缓升缓降,整排图标像浮在水面上随水位走,基准是波浪的船,波浪是船上的旗。
问:错落能换成脉冲吗?
答:能但变味——正弦换脉冲波,此起彼伏变成一闪一闪的告警,脉冲留给告警,波浪留给排布。
六、调参与实战怎么用
第四笔是相位的错距:零点五五的错距是波浪的命根——错距写死为零的版本就是那排齐步走的图标事故,每一份错距都在给波浪上发条。第三笔是波幅的档:六、十二、二十二三档覆盖"微动、常规、夸张"三档气质——波幅一大基准线要下移,免得图标跳出上边界。第二笔是流速的档:一点五到六的流速是从水草到沸腾的范围——流速动之前先看波幅,大波幅配快流速画面会闹。第一笔是排布的基准:图标挂在基准线上下浮——基准线是波浪的船,基准一动整排跟着走,动基准比动波幅更适合做过场。常见坑三个:相位没按下标错开的齐步走事故;流速过快引发的视觉频闪;折行后相位的下标没有接续导致两段波浪各行其是。
实战里波浪排布是"让一排东西活起来"的通用答案:技能栏的呼吸、按钮组的待机、奖励列表的展示,全是"同律错拍"的同一副骨架。组里的约定是:凡是一排同类元素的待机动画,一律加相位错距——齐步走是事故,错拍才是排布。这篇的模块照抄能跑,改的就是 CONST 那张表。
写完留一句给做排布系的同学:波浪排布卖的是"错开半拍才叫波浪"——那排此起彼伏的图标和按下标错开的相位,是把整齐划一升级成此起彼伏的一份错拍表。
全站技术干货持续更新:996 引擎 / Lua 实战帖,语法、参数与示例一篇讲透。进入文章地图 · 查看全部 →
【语法算法】 上一版的炮台索敌逻辑有个隐蔽报错:炮台永远打同一个怪——即使那只怪已经死了,索敌的游标也不挪窝。追到底索敌的游…
【语法算法】 先抛一个坑:打不完的火系怪怎么办?火抗怪火打不动,换冰系技能要切装备要换面板——切完黄花菜都凉了。元素转换的答…
【语法算法】 上个月的事故复盘会上有个数字被念了三遍:四成——策划写的是"同伴陪疼四成",代码落下去成了"陪疼四十点",两只…
【语法算法】 单行代码拆解:弹射初速=-420——弹射的全部动力就这一行的负初速。负号朝上、四百二十是弹射的初速大小——踩上…
【语法算法】 上一版的减速类模块全按"乘以零点五"来写,帧率无关的版本照搬了这套写法——结果高帧率机上减速效果好,低帧率机上…
【语法算法】 先抛一个坑:怎么把散在四处的怪聚到一起打?逐个拉是笨办法,一个范围技又只能打一片——引力球的答案是一个会动的吸…