【游戏功能】
一、一个盖住脸的圈
冰霜新星的第一版被测试打回来的理由很清奇:"怪被冻住之后看不见怪了"。检查发现新星特效的扩散环和地面寒雾全部画在了角色层之上,半径一百五十像素的半透明蓝雾正好糊在怪脸上,冻住的目标成了蓝雾里的一团影子。没有报错、没有崩溃,就是"看不见"——层级错了。地面系的范围特效,必须老老实实躺在地面层,哪怕它再漂亮。
二、机制原理:地面层与角色层
冰霜新星的全部表现都应该发生在脚下:范围圈是地面上的虚线椭圆,绽开的双环压着地皮扩张,碎冰晶散落在圈内随机位置,寒气粒子低空横飘。绘制顺序上这些全在角色之前完成,角色永远踩在冰面上而不是被冰面糊住。唯一盖在角色身上的只有受冻目标身上那层半透明冰壳——那是状态标记不是场景特效。范围判定与视觉范围严格同源:椭圆横向半径、纵向压扁到零点五五,判定和画图用同一组参数,玩家看到多大的圈,圈就冻多大的范围。
fx-nova
三、正确写法:地面层新星(引擎侧)
local NOVA_FLAT = 0.55 -- 纵向压扁比: 判定与绘制同源
function CastFrostNova(caster, radius)
-- 施法动作 + 出手帧触发
caster:PlayAction(global.MMO.ANIM_SKILL)
local hitFrame = caster:GetHitFrame()
OnFrameReached(hitFrame, function()
local ground = GetGroundLayer() -- 地面层: 永远在角色之下
ground:AddChild(CreateNovaRings(caster, radius, NOVA_FLAT))
ground:AddChild(CreateFrostShards(caster, radius, NOVA_FLAT, 26))
local frozen = 0
for _, m in ipairs(GetMonstersAround(caster, radius, NOVA_FLAT)) do
m:ApplyFrozen(2.6) -- 冻结 + 播受击
frozen = frozen + 1
end
if frozen == 0 then ShowSystemTips("没有冻到任何目标") end
end)
end
function GetMonstersAround(center, radius, flat)
local out = {}
for _, m in ipairs(scene:GetMonsters()) do
local dx, dy = m:GetX() - center.x, m:GetY() - center.y
if math.sqrt(dx * dx + (dy / flat) * (dy / flat)) <= radius then
table.insert(out, m) -- 椭圆判定: y 除压扁比还原
end
end
return out
end
四、四个必须钉死的细节
五、心法:特效先问画在哪层
写任何特效前先回答一个问题:它属于哪一层?地面层、角色层、状态层、界面层,四层从下到上各有主人。放错层的特效不报错,只会被测试用"看不见""挡住了""怎么飘到界面上了"这种模糊语言打回来。层级和坐标、锚点一样是特效的先天属性,出生时就定好,别等出了问题再挪。
全站技术干货持续更新:996 引擎 / Lua 实战帖,语法、参数与示例一篇讲透。进入文章地图 · 查看全部 →
【语法算法】 元方法 __close 的错误传递就这两行: to-be-closed 变量离开作用域时自动调用 __clos…
【语法算法】 goto 向后跳的控制就这一行: ::retry:: 定义标签, goto retry 跳回标签——向后跳的全…
【语法算法】 元方法 __close 的多资源清理就这两行: 两个 to-be-closed 变量——声明的逆序清理——先关…
【语法算法】 随机种子的确定性本质就这一行: 固定种子意味着随机数序列完全确定——同样的种子产生同样的序列——随机种子的全部…
【语法算法】 协程的状态机就这一行: status 返回四种字符串之一:"suspended"、"running"、"nor…
【语法算法】 xpcall 的错误对象就这两行: xpcall 和 pcall 的本质区别就是第二个参数——错误处理函数——…