【语法算法】
上一版埋了个越用越错的隐蔽报错:长按的计时不在抬手时清零——第一次长按松手后,秒表没归零,第二次轻轻一点直接被算成长按,大招莫名其妙放了出去。追到底是清零挂在按下的分支里,而有的入口根本不走那个分支,秒表带着旧账继续跑。修完把清零挪进抬手结账的那一拍,顺手把这套机制写成篇。长按判定的账是按住计时:按下起表,抬手结账——满了限额判长按,没满判点按,一次结清不带旧账。这篇把整改后的整套模块拆开:配置表、按住计时、抬手结账、双分判定、定时器、调试按钮,一段一段照抄能跑。
一、效果演示:按住计时,松手结账
演示场一枚圆形的判定钮。点「按下」:秒表起跳,圈环从十二点方向开始填——零点几秒、零点八秒,绿环慢慢合龙。一秒整,环变绿,"长按已成立"当场亮出。这时候松手——长按生效,大招伺候。换个玩法:按下零点四秒就松——"点按成立",只算一下小手。演示里试两笔账:满一秒的长按和零点四秒的点按,两种结账两种反馈——判定不看快慢看时长,秒表就是裁判。长按判定教的是结账:按下是记账的开始,松手才是结账的一拍,账要当面结清。
flowchart TD
A[按下 起表] --> B[逐拍累加时长]
B --> C{满一秒了吗}
C -- 是 --> D[绿环合龙 长按成立]
C -- 否 --> E{抬手了吗}
E -- 否 --> B
E -- 是 --> F[点按成立 清表]
D --> G{抬手了吗}
G -- 是 --> H[长按生效 清表]
G -- 否 --> G
F --> A
H --> A
fx-longpress
二、底层原理:一次按住计时加一次抬手结账
模块的机关是一对搭档。按住计时:按下的那一拍秒表起跳,按住的每一拍时长累加——环环相扣地把按住的时长记成进度。抬手结账:松手的那一拍看秒表读数——满限额判长按、没满判点按,判完当场清表,一次结清。和双击判定的分野在"长按结的是自己的账":双击比的是两次按下的间隔,长按比的是一次按住的时长——一个是看两次的关系,一个是看一次的坚持,计时器和间隔表是两套家伙。
成立之后为什么还要等抬手?长按成立立即可发的版本玩家按住的手还没抬,第二段逻辑已经开始,手感错位一拍;等抬手才生效,按住的时长自己决定发招的时机,手一抬招就走,干净利落。
三、核心代码:完整模块(上·骨架)
-- @file LongPress.lua
-- 长按判定 —— 按住计时的两分口径
local LongPress = {}
local CONST = {
PRESS_LIFE = 1.0,
AUTOINC_BASE = 1161000,
}
local _holding = false
local _holdT = 0
local _qualified = false
local _scheduler = nil
local _autoInc = 0
local function GenID(prefix)
_autoInc = _autoInc + 1
return string.format("%s_%d", prefix or "LPR",
CONST.AUTOINC_BASE + _autoInc)
end
local function ShowTip(msg)
if msg and msg ~= "" then SL:ShowSystemTips(msg) end
end
function LongPress.Holding()
return _holding
end
function LongPress.Qualified()
return _qualified
end
function LongPress.HoldTime()
return _holdT
end
四、核心代码:完整模块(下·按住计时与抬手结账)
-- 按下:起表
function LongPress.PressDown()
_holding = true
_holdT = 0
_qualified = false
ShowTip("按住了——计时开始")
end
-- 按住计时:逐拍累加,满限即成立
if not _scheduler then
_scheduler = SL:Schedule(function(dt)
if not _holding or _qualified then return end
_holdT = _holdT + dt
if _holdT >= CONST.PRESS_LIFE then
_qualified = true
ShowTip("长按已成立——大招伺候")
end
end, 0.02)
end
-- 抬手结账:满限长按生效,不满算点按
function LongPress.PressUp()
if not _holding then
ShowTip("还没按——先按下")
return
end
_holding = false
if _qualified then
ShowTip("长按生效——大招出手")
SL:FireBigSkill()
else
ShowTip("点按成立——只算一下小手")
SL:FireTapSkill()
end
_holdT = 0
_qualified = false
end
SL:ScheduleOnce(function()
SL:BindDebugButton("按下", function()
LongPress.PressDown()
end)
SL:BindDebugButton("松开", function()
LongPress.PressUp()
end)
ShowTip("技能已加载: 长按判定 (调试按钮触发)")
end, 1.0)
function LongPress.Unload()
if _scheduler then SL:Unschedule(_scheduler) end
_holding = false
_holdT = 0
_qualified = false
end
return LongPress
五、机制问答
问:成立之后按住不放会一直发吗?
答:不会——长按只发一次,按住不松只是绿环合龙的状态,想再发要松手重新按,一按一发的规矩不破。
问:满限的前一刻松手算什么?
答:算点按——结账看的是松手那一刻的读数,差半拍的点按也是点按,秒表不讲人情。
问:按住期间被眩晕打断了算什么?
答:作废清表——打断的同一拍强制抬手结账,时长没满就按点按处理,账面不欠。
问:长按的环和冷却的环是一个东西吗?
答:不是——长按环量的是你按了多久,冷却环量的是技能还剩多久,一个朝满走一个朝空走,别共用一张皮。
问:点按的响应能提前到按下瞬间吗?
答:不能——点按要等抬手才能和长按分家,按下即发的版本长按永远带着一个已发出去的点按,两分的代价就是点按慢半拍。
六、调参与实战怎么用
第四笔是清账的时机:抬手结账即清表是那场旧账报错的正解——清零挂按下分支的版本,走不到那个分支的入口全带旧账,清账必须跟着结账走。第三笔是限额的位置:一秒的限额是长按和点按的分水岭——限额太短轻点就出大招,太长长按变成憋气比赛,限额要拿最自然的按住时长校。第二笔是成立和生效的两拍:成立是环合龙,生效是抬手出手——两拍分开,按住的力度才有的放矢,混成一拍的手感是按下即抽搐。第一笔是环的读数:绿环的合龙就是秒表的实体——环合到哪按到哪,读数可见的长按才敢让人按满。常见坑三个:计时清零挂在按下分支的旧账报错;成立后按住不放重复触发;点按的响应抢在抬手前把长按的账提前结了。
实战里长按判定是"一钮两用"的通用答案:普攻键的长按蓄力、地图的长按标点、聊天键的长按转语音,全是"按住计时抬手结账"的同一副骨架。组里的约定是:一个按钮要承担两档功能时一律走长按判定,不靠连点猜意图——时长是玩家自己按出来的,判定只是念账。这篇的模块照抄能跑,改的就是 CONST 那张表。
写完留一句给做输入系的同学:长按判定卖的是"按多久你说了算"——那道慢慢合龙的绿环和抬手一刻的两分结账,是把按住的时长变成两档功能的一只秒表。
全站技术干货持续更新:996 引擎 / Lua 实战帖,语法、参数与示例一篇讲透。进入文章地图 · 查看全部 →
【语法算法】 上一版的炮台索敌逻辑有个隐蔽报错:炮台永远打同一个怪——即使那只怪已经死了,索敌的游标也不挪窝。追到底索敌的游…
【语法算法】 先抛一个坑:打不完的火系怪怎么办?火抗怪火打不动,换冰系技能要切装备要换面板——切完黄花菜都凉了。元素转换的答…
【语法算法】 上个月的事故复盘会上有个数字被念了三遍:四成——策划写的是"同伴陪疼四成",代码落下去成了"陪疼四十点",两只…
【语法算法】 单行代码拆解:弹射初速=-420——弹射的全部动力就这一行的负初速。负号朝上、四百二十是弹射的初速大小——踩上…
【语法算法】 上一版的减速类模块全按"乘以零点五"来写,帧率无关的版本照搬了这套写法——结果高帧率机上减速效果好,低帧率机上…
【语法算法】 先抛一个坑:怎么把散在四处的怪聚到一起打?逐个拉是笨办法,一个范围技又只能打一片——引力球的答案是一个会动的吸…