【游戏功能】
上周在测试服翻日志,控制台里每隔十几秒冒一条红字:attempt to perform arithmetic on a nil value (field 'z')。追了半天才发现是狼怪落地的那一帧,垂直高度 z 被清成了 nil,下一次取它算位移就直接炸了。报错本身五分钟修完,但顺着这条线索把击飞这一套表现翻出来重新过了一遍,发现能聊的东西比想象中多——今天就拆击飞。
一、起跳、浮空、落地三段缺一不可
击飞的本质是"借走"目标的一段时间,三段式结构。起跳段:一锤砸在狼怪胸口,接触瞬间给它一个垂直初速度,初速度决定飞多高——430 的初速度配 900 的重力,浮空时间约 0.9 秒,这个数一改手感就变。浮空段:高度按抛物线走,z = v0·t − ½g·t²,起和落是对称的;浮空期间狼怪跳过整个 AI 轮询,不追人不还手,控制的核心就在这。落地段:砸地扬尘、屏幕轻震,再补 0.5 秒落地硬直——硬直是击飞白送的第二段控制,连招窗口从这里开始算。还有个容易被忽略的细节:影子。狼怪飞在天上时,地面的影子要跟着高度缩小,影子是玩家读高度的唯一参照,影子不缩,画面就是一张贴图平移,完全没有"腾空"的体感。
浮空的镜头表现还有一个取舍:镜头要不要跟着目标抬?演示里镜头纹丝不动,高度全靠影子缩放表达——镜头一跟,整个画面都在动,打击感反而散了;不动镜头、动影子,玩家的视线锚在地面,高度反而读得更准。另一个细节是飘字的挂点:浮空期狼怪挨打,伤害数字要挂在它身上跟着飞,挂在世界坐标上的数字会停在半空——狼怪落了地,数字还飘在原来的高度,画面一下就穿帮。起跳前的那一锤也要有预备:锤子抡起到落下留 0.26 秒延迟,这半拍是发力的预告,接触瞬间才起跳的击飞是没有重量的,玩家感觉狼怪是被吸附上去而不是被砸上去的——预备、接触、腾空三拍的节奏对齐之后,一锤的重量感才立得住。
完整演示在这——点「崩天锤!」亲手把狼怪砸上天,盯着地上的影子看:
fx-knockup
二、幂尔框架实现:垂直位移与操作锁
幂尔框架里击飞不走动作序列,走的是垂直坐标偏移。挂一个垂直速度,每帧积分高度,落地回调里收尾:
local Knockup = {}
local GRAVITY, V0 = 900, 430
local z, vz, airborne = 0, 0, false
function Knockup.Launch(target)
local x = SL:GetValue("x", target)
SL:PlayActorAct("player", "attack")
SL:ScheduleOnce(function()
z, vz, airborne = 0, V0, true
SL:SetControl(target, false) -- 浮空锁操作, AI 轮询一并跳过
SL:PlayBurst(x, SL:GetValue("y", target), "dust", 10)
SL:ShowTipAbove(target, "崩天!")
SL:ShakeCamera(6, 0.3)
end, 0.26)
end
function Knockup.Tick(dt)
if not airborne then return end
vz = vz - GRAVITY * dt
z = z + vz * dt
SL:SetVertOffset(target, z) -- 高度只改绘制, 不改逻辑坐标
SL:SetShadowScale(target, math.max(0.35, 1 - z / 280))
if z <= 0 then
z, airborne = 0, false
SL:SetVertOffset(target, 0)
SL:SetShadowScale(target, 1)
SL:SetControl(target, true)
SL:PlayBurst(SL:GetValue("x", target), SL:GetValue("y", target), "land_dust", 12)
SL:SetStagger(target, 0.5) -- 落地硬直: 第二段控制
end
end
SL:RegTick("knockup_tick", Knockup.Tick)
return Knockup
三个调用点值得记:SL:SetVertOffset 只动绘制层不动逻辑坐标,浮空的狼怪在逻辑上原地不动,命中判定才不会飞出范围;SL:SetControl(false) 一口气锁掉玩家输入和 AI 两套驱动,浮空期它什么都干不了;SL:SetStagger 的落地硬直是独立的一段控制,别和浮空共用一个计时器,不然落地即解控,硬直就白设计了。
框架调用方式再顺一遍:Tick 由 SL:RegTick 挂进主循环,每帧拿到 dt;Launch 里用 SL:ScheduleOnce 把"接触起跳"推迟 0.26 秒,好让攻击动作先播;浮空期每帧把高度喂给 SL:SetVertOffset,落地瞬间反向喂零并解 SetControl——整个技能没有一条 For 循环遍历场景,所有调用都围着 target 一个对象转,这是幂尔框架单对象技能的标准写法。初学最容易犯的错是把浮空写成动画:给狼怪播一个"跳起"的动作序列,播完就算击飞——动画是表现,控制是状态,状态不在,AI 照样追着玩家咬,玩家隔着屏幕都能感觉到这只狼是"装的浮空"。
三、浮空期的判定隔离与硬直的补偿
浮空期间最容易出的事故是受击位移叠加:浮空里再挨一刀,击退位移照算,狼怪直接飞出地图边界。规矩很简单——浮空目标免疫一切水平位移,只吃伤害数字。另一个是浮空时长和硬直时长的配比:0.9 秒浮空加 0.5 秒硬直,一共 1.4 秒控制,足够接一个蓄力技能;要是把硬直砍了,击飞就只是个观赏动作,连不上任何东西。最后是数值两侧的对称感:起跳初速度和重力定下之后别再分开调,改重力不改初速,抛物线就变了形——玩家对"这一锤能飞多高"是有肌肉记忆的,曲线一歪手感就散。击飞看着是个最朴素的控制,真正做起来,一半工作量在影子、落地尘和硬直这些没人明说的边角上。
联调时还有三类边界要过一遍:浮空读到一半切场景,垂直偏移要随场景销毁清零,不然下一场开局狼怪悬在半空;浮空中目标死亡,落地硬直就不要再挂,尸体不硬直;两个玩家同时砸同一只狼怪,浮空取刷新不取叠加——二次击飞把垂直速度重置回初速度,浮空时间重新起算,这是几乎所有游戏通行的处理,叠成双倍高度反而假。还有一类更隐蔽:浮空期玩家切后台再切回来,帧间隔被拉到几秒,一步积分直接把抛物线积分穿地——Tick 里对单帧 dt 做上限裁剪,超过 0.05 秒按 0.05 算,浮空时间拉长几帧无所谓,穿地了就穿帮。这些边界单测都测不出来,全是上线后玩家拿脚投票试出来的。
全站技术干货持续更新:996 引擎 / Lua 实战帖,语法、参数与示例一篇讲透。进入文章地图 · 查看全部 →
【游戏功能】 if _shadowT 0 then spd = noShadow and 12 or 24 end ——一行…
【游戏功能】 魂卫的全机制就一句:杀掉小怪化一盏魂火跟着你走,每盏魂火两秒自动劈最近的大怪一口八点,至多三盏——「引魂!」按…
【游戏功能】 一道咒飞过来,硬吃是四秒里一秒十的钝疼——有没有第三条路?纸人替灾给的就是第三条路:贴一个纸人在身后,八秒里头…
【游戏功能】 挨打还能赚钱?水镜式的答案是:挨的刀七成存进镜里不扣血、三成照常疼,六秒后点「还镜」,存量乘一点五放成水炮——…
【游戏功能】 if _mirrorT 0 then SL:TipAboveMe("免疫") return end ——一道写…
【游戏功能】 远近战的矛盾怎么解?远战的答案是把怪挡在射程外,近战的答案是把人拖进射程内——缩地阵给的第三个答案最蛮横:圈只…