【游戏功能】
一、一次裸眼抓哑巴的潜行
烟幕弹玩法首版测试,玩家丢完烟幕就站在烟里发呆,怪在烟边上转圈也不进来——表现对了,AI 没跟上:怪没有"丢失目标"的状态转移,站在原地反而像在守尸。烟幕的完整体验是双向的:玩家看怪若隐若现,怪看玩家一片模糊——丢了目标就转巡逻,巡逻路线路过玩家再重新发现。视觉遮蔽和 AI 感知必须同时成立,潜行才有博弈。
二、机制原理:遮蔽即失忆
烟幕的表现与感知一体两面。表现层:烟幕是一片多团软圆粒子叠加的灰色雾区,团与团缓慢漂移呼吸,盖在最上层把雾中角色遮成半透剪影。感知层:怪的仇恨判定加一个遮蔽检查——目标在雾区内则视为丢失目标,怪从追击态转入巡逻态,沿烟幕边缘绕圈。玩家的脱身窗口就是怪的"失忆期":趁巡逻绕开的空档从反方向穿出去,潜行的博弈感由此而来。
fx-smoke
三、正确写法:遮蔽感知(幂尔版)
local SmokeScreen = {}
local CONST = { MSG_ID = 12380, KEY = "SmokeScreen", RADIUS = 90, DURATION = 4 }
function SmokeScreen.Cast(myID, x, y)
SmokeScreen._x, SmokeScreen._y = x, y
SmokeScreen._left = CONST.DURATION
SmokeScreen._sched = SL:Schedule(function(dt)
SmokeScreen:Update(dt)
end, 0.02)
SL:SendLuaNetMsg(CONST.MSG_ID, 0, 0, 0,
SL:JsonEncode({ key = CONST.KEY, op = 1, actorID = myID, x = x, y = y }))
end
function SmokeScreen:Update(dt)
self._left = self._left - dt
if self._left <= 0 then
SL:UnSchedule(self._sched)
self._sched = nil
return
end
local inSmoke = self:InSmoke(SL:GetValue("USER_ID"))
-- 玩家在烟中: 怪的仇恨目标置空
local mob = FindNearestMob(self._x, self._y)
if mob then
if inSmoke then
mob:LostTarget() -- 感知遮蔽: 转巡逻
else
mob:RegainTarget() -- 出烟: 重新索敌
end
end
end
function SmokeScreen:InSmoke(actorID)
local ax = SL:GetValue("ACTOR_POSITION_X", actorID)
local ay = SL:GetValue("ACTOR_POSITION_Y", actorID)
return math.sqrt((ax - self._x) ^ 2 + ((ay - self._y) / 0.5) ^ 2) <= 90
end
return SmokeScreen
四、四个必须钉死的细节
五、心法:视野是一种可出售的资源
烟幕、迷雾、草丛、黑暗视野,全是在经营"信息的可见与不可见"。表现层画雾,感知层定规则,两层配合把"看不见"变成玩法:隐身职业、蹲草伏击、烟雾脱身,全部建立在这一条上。判断一个潜行系统做得好不好,就看玩家能不能准确回答"我现在被谁看见"——答得清,博弈成立;答不清,潜行就是掩耳盗铃。
全站技术干货持续更新:996 引擎 / Lua 实战帖,语法、参数与示例一篇讲透。进入文章地图 · 查看全部 →
【游戏功能】 一、一次裸眼抓哑巴的潜行 烟幕弹玩法首版测试,玩家丢完烟幕就站在烟里发呆,怪在烟边上转圈也不进来——表现对了,…
【游戏功能】 一、一个震了个寂寞的大招 跺地震波类技能首版,特效华丽、震屏到位,测试却打回:"这大招怎么只炸了一下就没了?"…
【游戏功能】 一、一个卡进墙里的箱子 推箱谜题移植首版,测试发现一个无解局面:箱子被推到墙角,四个方向都推不动,谜题卡死只能…
【游戏功能】 一、一个永远差一口气的接线 星轨接电类旋转解谜首版,测试卡在第三关:四段线路怎么转都差一口气,明明视觉上头尾相…
【游戏功能】 一、一个被风卷走的判空 龙卷风聚怪技能首测,最灵异的 bug:怪被吸到风眼附近后集体"抽搐"——坐标每帧在风眼…
【游戏功能】 一、一个只闪不中的斩击 斩钢闪类突进斩首版被吐槽"人过去了刀没过去"——突进的位移做了,斩击的刀痕却只在终点画…