【游戏功能】
测试组报过一口邪门的单:头目的读条明明被打断过,技能照样出了膛——查了半天不是打断失效,是打断只"清了"不"冻",怪在冷却的空档里重新抬手,读条从半截续上。真正的镇压不是打断,是让读条停在那儿不许动。镇魂碑干的就是这件事:碑圈里的读条整体冻结。核心代码照引擎技能脚本的全规格写:配置表、碑体构件、读条冻结状态机、定时器、调试按钮。
一、效果演示:一块碑,一条冻住的血槽
演示场里大怪每七秒凝一记六十点的大招,头顶升起两秒的读条——读条走满,红弧横扫六十。点「镇魂碑!」:一块石碑轰然立在怪身侧,紫蓝色的镇域虚线圈开,读条上的"凝大招…"当场变成"读条冻结"——进度条停在原地,怪僵着手维持凝功的姿态,纹丝不能进。碑化之前,它的大招就是一条永远走不完的血槽——镇住的不只是一招,是它全部的威慑。演示里可以试两种站位:碑落在怪脚下,读条当场冻住;碑落远了,读条照走——镇域只认圈内的魂,圈外的读条它管不着。
fx-soulstele
二、底层原理:一条读条通道加一个冻结覆盖层
模块的机关是一对状态。怪的大招自带两秒的凝功通道:chanT 从二秒往下走,走满放雷。镇魂碑立起一块六秒的镇域:域内的怪,通道的时间推进被整体跳过——注意是"跳过推进"而不是"清零"——冻结撤掉的瞬间读条从原刻度续走。这正是它和打断系的分野:打断清账、镇压冻账,清了账的怪换个时间照样出招,冻了账的怪在大招的账本上原地踏步。镇域的判定以碑为圆心横宽九十步——碑立在哪,哪一片的读条都得停。
三、核心代码:完整模块(上·骨架)
--------------------------------------------------------------------------------
-- @file SoulStele.lua
-- @brief 镇魂碑 —— 立碑镇住它,大招读不完
-- 客户端表现模块:配置表 / 碑体构件 / 读条冻结 / 定时器
--------------------------------------------------------------------------------
local SoulStele = {}
--------------------------------------------------------------------------------
-- 配置与常量
--------------------------------------------------------------------------------
local CONST = {
STELE_LIFE = 6.0, -- 碑立六秒
STELE_CD = 10.0, -- 立碑冷却
STELE_RANGE = 90, -- 镇域横宽九十
ULTI_GAP = 7.0, -- 大招七秒一凝
ULTI_CHANNEL = 2.0, -- 读条两秒
ULTI_DMG = 60, -- 大招六十
-- 颜色
COLOR_STELE = "#8b9cf5", -- 碑紫蓝
COLOR_DEEP = "#3a3a7a", -- 深蓝
COLOR_GHOST = "#cdd4ff", -- 碑白
ZORDER_GROUND = 118,
ZORDER_STELE = 130,
ZORDER_EFFECT = 135,
AUTOINC_BASE = 932000,
}
--------------------------------------------------------------------------------
-- 模块内部状态
--------------------------------------------------------------------------------
local _steleT = 0 -- 碑的余时
local _steleX = 0
local _cdLeft = 0
local _ultiT = CONST.ULTI_GAP
local _chanT = 0 -- 读条余量
local _frozen = false
local _scheduler = nil
local _autoInc = 0
local m_cos = math.cos
local m_sin = math.sin
--------------------------------------------------------------------------------
-- 辅助函数
--------------------------------------------------------------------------------
local function GenID(prefix)
_autoInc = _autoInc + 1
return string.format("%s_%d", prefix or "SST", CONST.AUTOINC_BASE + _autoInc)
end
local function GetActorPos(actorID)
if not actorID then return nil end
local x = SL:GetValue("ACTOR_POSITION_X", actorID)
local y = SL:GetValue("ACTOR_POSITION_Y", actorID)
if not x or not y then return nil end
return { x = x, y = y }
end
local function SafeRemove(node)
if node and not GUI:Widget_IsNull(node) then
GUI:removeFromParent(node)
end
end
local function ShowTip(msg)
if msg and msg ~= "" then SL:ShowSystemTips(msg) end
end
---碑体构件:碑身、碑额、镇域圈三件套
local function CreateSteleNode(x, y)
local node = GUI:Layout_Create(GUI:Attach_SceneF(), GenID("Stele"), x, y, 0, 0, false)
GUI:setLocalZOrder(node, CONST.ZORDER_STELE)
GUI:DrawLine_Create(node, "body_l", 0, 0, 16, -8, 0, -8, -58, CONST.COLOR_DEEP, 230)
GUI:DrawLine_Create(node, "body_r", 0, 0, 16, 8, 0, 8, -58, CONST.COLOR_DEEP, 230)
GUI:DrawLine_Create(node, "cap", 0, 0, 10, -12, -58, 12, -58, CONST.COLOR_GHOST, 240)
GUI:Text_Create(node, "word", 0, -30, 14, CONST.COLOR_GHOST, "镇魂")
return node
end
骨架的三处立规。_frozen 是覆盖层的开关而不是通道的属性——冻结是碑施加给通道的外部状态,写在通道自己的字段里,碑一化冻结就成了无主状态。读条余量 _chanT 与冻结分开存放是全套机关的底座:冻的是"推进"、不是"进度"。碑体构件三件一套——碑身两笔厚线、碑额一道亮线、碑文竖排——碑的样子要有分量,没有分量的碑镇不住大招。碑文竖排是老规矩——横排的碑文像告示牌,竖排的碑文才像一块有年头的碑。
四、核心代码:完整模块(下·定时器与读条冻结)
--------------------------------------------------------------------------------
-- 立碑与镇域判定
--------------------------------------------------------------------------------
local function InZone(mx)
if _steleT <= 0 then return false end
return math.abs(mx - _steleX) <= CONST.STELE_RANGE
end
function SoulStele.Cast(x)
if _cdLeft > 0 or _steleT > 0 then return end
_cdLeft = CONST.STELE_CD
_steleT = CONST.STELE_LIFE
_steleX = x
CreateSteleNode(x, SL:GetValue("ACTOR_POSITION_Y", SL:GetValue("USER_ID")) + 2)
ShowTip("碑立了——把它引进圈里")
end
--------------------------------------------------------------------------------
-- 大招通道:冻结覆盖层
--------------------------------------------------------------------------------
local function Ulti_Update(dt, mx)
_frozen = InZone(mx)
if _chanT > 0 then
if not _frozen then
_chanT = _chanT - dt
if _chanT <= 0 then
SL:UltiBlast(mx)
ShowTip("大招出膛了——六十,下块碑立快点")
end
end
return
end
_ultiT = _ultiT - dt
if _ultiT <= 0 then
_ultiT = CONST.ULTI_GAP
_chanT = CONST.ULTI_CHANNEL
ShowTip(_frozen and "它凝大招了——碑镇着,读条冻住" or "它凝大招了——两秒后六十点")
end
end
--------------------------------------------------------------------------------
-- 定时器与调试
--------------------------------------------------------------------------------
if not _scheduler then
_scheduler = SL:Schedule(function(dt)
_cdLeft = math.max(0, _cdLeft - dt)
if _steleT > 0 then
_steleT = _steleT - dt
end
local mp = GetActorPos(SL:GetValue("USER_ID"))
Ulti_Update(dt, mp and mp.x + 340 or 0)
end, 0.02)
end
SL:RegisterLUAEvent(LUA_EVENT_SCENE_CLEAR, "SST_Clear", function()
_steleT, _chanT, _ultiT, _frozen = 0, 0, CONST.ULTI_GAP, false
end)
SL:ScheduleOnce(function()
ShowTip("技能已加载: 镇魂碑 (调试按钮触发)")
end, 1.0)
return SoulStele
下半场两处见功夫。冻结的实现在 Ulti_Update 的分支顺序里:先判 _frozen,冻着就整段跳过推进——通道的每一帧都在问一句"碑还在吗",碑在,两秒的读条就两秒两秒地停。冻结解除的续走是这套机制的德行:进度从原刻度续,玩家抢在碑化前多冻一秒,大招就晚一秒——镇域的时间是玩家一秒一秒买来的。碑化与冻结解除必须同帧——碑的残影还在飘、读条已经复活,这两帧的错位就是玩家投诉的原图。场景清空钩子把大招周期一并复原,新的一场仗从满周期重新数起,一个数字都不带过去。
五、调参指南与商业服怎么用
调参三笔账。镇域六秒对大招周期七秒——贴着周期立碑,大招能被冻过整个读条期再余一秒;镇域短于读条的两倍,冻结就成了薛定谔的镇。横宽九十步是碑的"管辖范围"——范围外的读条照走,逼玩家把碑立在怪的脚下而不是自己的脚边,立碑的位置感就是这个技能的全部操作。大招六十对读条两秒:读条越短越难冻,三秒以上的读条配六秒的碑就是白送镇域。常见坑三个:冻结期怪的普攻还在走,冻大招不冻普攻要写清楚;碑立远了镇域圈不沾怪,读条照走,玩家的第一反应是碑坏了——立碑的落点提示要做一圈落点预览;碑化瞬间冻结立刻解除,读条从原刻度续,玩家以为读条清了;镇域圈不随怪移动,怪走出圈读条复活。
商业服里镇魂碑是"攻坚战"的门槛石:会立碑的队把头目的大招冻成摆设,不会立的队每七秒交一次学费——同一只头目两种难度。行会攻城里它是城门守护者:守方立碑冻攻方的冲锋号,攻方拆碑的战术围绕一块石头展开。变体做成碑文品级:下品碑冻大招、上品碑连普攻一起冻慢,碑的种类多一个,攻坚战法厚一页。再进一步做成双碑共鸣:两块碑距离五十步内同时在场,镇域横宽翻倍——碑与碑的几何也是攻略。这一篇的演示和整套模块照抄能跑,改的就是 CONST 那张表。
写完留一句给做镇压系的同学:冻账和清账是两门手艺——冻住的那条血槽,是玩家用碑的位置一寸一寸换来的。
全站技术干货持续更新:996 引擎 / Lua 实战帖,语法、参数与示例一篇讲透。进入文章地图 · 查看全部 →
【游戏功能】 if _shadowT 0 then spd = noShadow and 12 or 24 end ——一行…
【游戏功能】 魂卫的全机制就一句:杀掉小怪化一盏魂火跟着你走,每盏魂火两秒自动劈最近的大怪一口八点,至多三盏——「引魂!」按…
【游戏功能】 一道咒飞过来,硬吃是四秒里一秒十的钝疼——有没有第三条路?纸人替灾给的就是第三条路:贴一个纸人在身后,八秒里头…
【游戏功能】 挨打还能赚钱?水镜式的答案是:挨的刀七成存进镜里不扣血、三成照常疼,六秒后点「还镜」,存量乘一点五放成水炮——…
【游戏功能】 if _mirrorT 0 then SL:TipAboveMe("免疫") return end ——一道写…
【游戏功能】 远近战的矛盾怎么解?远战的答案是把怪挡在射程外,近战的答案是把人拖进射程内——缩地阵给的第三个答案最蛮横:圈只…