【游戏功能】
线上事故引入:早上钟乳洞的狮吼出了名场面——吼声的环都荡出屏幕了,伤害的账还在怪身上跳:环的可见半径和结算的判定各荡各的,屏幕外的环还在隔空咬人。荡音的账必须拴在环的半径上:环荡到哪,账才落到哪。这篇把荡音环纹整套写法重写成篇:起吼生环、环荡渐大、荡身即疼、三环荡尽,一段一段照抄能跑。
一、效果演示
演示场是挂满钟乳石的石洞。点「狮吼!」:一圈音环从侠客身周荡出——环越荡越大,像水面的涟漪一层追一层;环荡过怪身的那一拍,八点入账。三环接连荡出,环与环隔着八分之三秒,荡尽收声。七秒一吼,怪站得越近环荡到得越早,离得远就得等环慢慢荡过去。
flowchart TD
A[点狮吼] --> B[身周生第一环]
B --> C[环半径放大]
C --> D{荡过怪身了吗}
D -- 没有 --> C
D -- 荡过 --> E[八点入账]
E --> F{三环荡尽了吗}
F -- 没有 --> B
F -- 荡尽 --> G[收声 七秒后再吼]
fx-crest-roarrings
二、底层原理
荡音环的机关是"半径即判据"——环从身周生出来只带一个数:半径。半径每拍放大,放大到压过怪的距离那一拍,账才落。那场隔空咬人的笑话就是判据和半径分了家:画面画半径、账本掷骰子。正解是判据直接读半径——环荡到哪,账落到哪,一个数说了算。三环接连是节奏的骨架:一环快过一环的心理压迫,环与环隔八分之三秒,荡得有条不紊。
环的可视化就是伤害本身:淡蓝的圈从身周荡开、渐大渐淡,圈压过怪身的那一下配一声入账的响——玩家看着环荡过去,心里数着账。环荡出屏就消,账跟着环的消亡一起清——荡音没有残留账,每一环自生自灭。幂尔框架这边生环收在吼的口子里,半径的推进和判据写在每拍推进的同一行,账与环共一个数。
这套玩法的代码量不到七十行,难的不是荡环而是判据拴半径——判据自己掷骰子的环,荡出屏幕也咬人。
再看环为什么从身周荡出而不是从怪脚下炸开——从身周荡出,环要"走完"这段距离才咬到人,距离成了天然的节拍器:怪近咬得快、怪远咬得慢,狮吼的威胁随距离伸缩。从怪脚下炸开的版本就成了定点爆,荡的意象全无。到达方式的距离感,就是荡音环和一切定点爆的分野。
三、核心代码:完整模块(上·骨架)
-- @file CrestRoarRings.lua
-- 荡音环纹 —— 一圈一圈荡出去 荡到就疼
local CrestRoarRings = {}
local CONST = {
RING_N = 3,
RING_GAP = 0.8,
RING_SPEED = 240,
RING_DMG = 8,
ROAR_CD = 7,
AUTOINC_BASE = 1205000,
}
local _rings = {}
local _n = 0
local _shot = 0
local _cd = 0
local _autoInc = 0
local function ShowTip(msg)
if msg and msg ~= "" then SL:ShowSystemTips(msg) end
end
local function GenID()
_autoInc = _autoInc + 1
return CONST.AUTOINC_BASE + _autoInc
end
function CrestRoarRings.Count() return _n end
-- 生环: 只带一个数 半径
local function Spawn()
_n = _n + 1
_rings[#_rings + 1] = { r = 24, hit = false }
ShowTip("吼出第" .. _n .. "环——荡过去")
end
四、核心代码:完整模块(下·推进与卸载)
-- 每拍推进: 半径放大 荡身即疼
if not _ticker then
_ticker = SL:Schedule(function(dt)
if _cd > 0 then _cd = _cd - dt end
if _n < CONST.RING_N and _cd <= 0 then
_shot = _shot - dt
if _shot <= 0 then
_shot = CONST.RING_GAP
Spawn()
end
end
local dist = SL:GetValue("mobDist") or 0
for i = #_rings, 1, -1 do
local ring = _rings[i]
ring.r = ring.r + CONST.RING_SPEED * dt
if not ring.hit and dist > 0 and ring.r >= dist - 16 then
ring.hit = true
ShowTip("环荡过身——" .. CONST.RING_DMG)
end
if ring.r > 760 then table.remove(_rings, i) end
end
end, 0.02)
end
SL:ScheduleOnce(function()
SL:BindDebugButton("狮吼!", function()
_n = 0
_shot = 0
ShowTip("深吸气——三环连荡!")
end)
ShowTip("技能已加载: 荡音环纹")
end, 1.0)
function CrestRoarRings.Unload()
if _ticker then SL:Unschedule(_ticker) end
_rings = {}
end
return CrestRoarRings
五、机制问答
问:荡出屏幕的环为什么还咬人?
答:判据和半径分了家——正解是判据直接读半径,环荡到哪账落到哪。
问:环为什么从身周荡出不从怪脚下炸?
答:距离是天然节拍器——怪近咬得快怪远咬得慢,荡的意象随距离伸缩。
问:三环为什么隔八分之三秒?
答:环追环的压迫感——荡得有条不紊,一环紧着一环。
问:环荡出屏为什么就消?
答:荡音没有残留账——环自生自灭,账随环的消亡一起清。
问:怪贴脸的时候吼值不值?
答:最值——环生出来一拍就荡到,三环连环快得像一记重锤。
六、调参与实战怎么用
参数层面三个量——环数、环间隔、荡速。三环配八分之三秒间隔是压迫的甜点位:环多了噪少了松。实战里这套半径即判据的骨架能搬去冲击波、音爆、钟鸣震,全是生环、荡大、荡身即疼一条线。常见坑两个:判据分家荡出屏还咬人;环不清亡,幽灵环常驻。
补充一个设计层面的思考——招式的味道来自它的"到达方式":环是荡过去的、藤是爬过去的、剑是点名放过去的、线是滑出来的、虫是追过去的、涡是拖过来的——同样的伤害换一种到达方式就是一个新招。设计新招先设计它的到达方式,伤害只是到达之后的那声响。
补充一个维护层面的思考——会自己运动的活物(荡出的环、爬行的藤、追咬的虫)每样都要有寿终正寝的一行:环荡出屏就消、藤爬到就卷、虫咬完就散、时辰到了就散场。活物的账最怕长寿——一只永不散场的虫群会把后续每一场的账都啃一遍。
补充一个工程层面的思考——追踪类的运动体(追咬的虫、跟着走的藤头、朝人滑的霜线)每帧都要重新问一遍目标还在不在:目标换了、倒了、退场了,运动体就地改道或散场。追踪不问目标,追的就是上一次的记忆——记忆里的人早就不在那儿了。
补充一个手感层面的思考——到达方式的节奏各有各的拍子:环靠荡的匀、藤靠爬的扭、剑靠点名的匀拍、线靠滑的疾、虫靠追的摇、涡靠拖的沉,六种拍子六种手感。手感层面的调参先调拍子的松紧再调数值的大小——拍子顺了数值只是乘数,拍子别扭了数值越大越别扭。这轮六只演武的手感各是一路,摆在一处能一眼看出谁是谁,靠的就是拍子先分了家。
写完留一句给做音波系的同学:玩家数的是环也是那根半径——判据拴在半径上,荡出去的每一环都疼得有出处。
全站技术干货持续更新:996 引擎 / Lua 实战帖,语法、参数与示例一篇讲透。进入文章地图 · 查看全部 →
接手二开的兄弟几乎都撞过同一堵墙:玩家嫌走路"一格一格像机器人",老板拍板要"丝滑移动",改服务端速度?不对。改客户端动画帧…
【游戏功能】 先看一个熟场面:法师的火球特效挂在怪的脚后跟上,明明技能说明写着"焚烧其面",火星子却在地上打滚。群里有位策划…
【游戏功能】 上周有个开服的老板在群里发了一段录屏:他的战士明明刀刀都砍中了,观众却一片"这刀挥空了吧"。他原话是"判定日志…
【游戏功能】 先说结论放在开头:怪的皮一会儿绿一会儿红,不是美术换了几套贴图,是染色系统在怪身上盖了一层"色镜"。开服群里有…
前两天群里有个架设的朋友问了个需求:中毒的人要变绿、每两秒掉一次血、毒还能叠五层,问是不是要在服务端把整套状态机重写。我说你…
【游戏功能】 先抛一个坑:五口钟一模一样大,为什么一口比一口疼?先后次序动不得吗?倒过来敲会怎样?这篇把编钟纹整套写法拆开:…