【游戏功能】
if _mirrorT > 0 then SL:TipAboveMe("免疫") return end——一道写在扣血最前面的闸门,就是弹反镜的全部骨架:举镜半秒,镜面撞上箭就原路弹回去咬它三十五。半秒的窗口、两秒的冷却,举早了空镜、举晚了挨箭——这是给手速党的镜子。核心代码照引擎技能脚本的全规格写:配置表、镜面构件、弹道反转、定时器、调试按钮。
一、效果演示:半秒镜面,一支回旋箭
演示场里大怪两秒射一箭、一箭二十五。点「弹反!」:一面椭圆的镜在玩家身前举起,半秒里泛着白光——箭飞到脸上撞上镜面,叮的一声原路折返,扎进怪身上就是三十五,比它的箭还疼十点;镜举晚了半秒,箭扎你二十五,镜子白收。演示里故意空举一次镜、再掐着箭的拍子弹一次——空镜的尴尬和弹中的爽快摆在一起,这道题的答案就两个字:掐拍。
fx-mirrorparry
二、底层原理:一道半秒闸门加一次弹道反转
模块的机关是三个部件。镜面态:举镜的半秒里 _mirrorT 大于零,玩家周身挂镜光——镜面态不改移动不改攻击,它只是"举着一面镜"。镜面态不改移动不改攻击,它只是"举着一面镜"。举镜期间照常走位——镜随人走,挡箭的判定跟着玩家的实时位置算,站着不动才是唯一挨箭的姿势。闸门态:箭的命中判定先查镜面态——举着,箭的行程反转、伤害易主:二十五的箭变成三十五咬回怪身上,多出来的十是弹反的利息。空窗态:镜收了、箭来了,闸门不存在,伤害原路落下。三个部件的时序全在两秒冷却和半秒窗口的对比里:冷却四倍于窗口,玩家每一次举镜都是一次押注。三个部件的时序全在两秒冷却和半秒窗口的对比里:冷却四倍于窗口,玩家每一次举镜都是一次押注。押注的赔率也写在数字上——弹回的箭比原箭疼十点,接住一箭等于白赚一次普攻,这笔账让每次举镜都有正期望的冲动。弹回的箭比原箭疼十点,接住一箭等于白赚一次普攻,这笔账让每次举镜都有正期望的冲动。镜面态里的粒子也别浪费——镜沿飘着的白屑是"还开着"的常驻提示,提示断镜的时机全靠它。
三、核心代码:完整模块(上·骨架)
--------------------------------------------------------------------------------
-- @file MirrorParry.lua
-- @brief 弹反镜 —— 架起一面镜,箭从哪来回哪去
-- 客户端表现模块:配置表 / 镜面构件 / 弹道反转 / 定时器
--------------------------------------------------------------------------------
local MirrorParry = {}
--------------------------------------------------------------------------------
-- 配置与常量
--------------------------------------------------------------------------------
local CONST = {
MIRROR_LIFE = 0.5, -- 镜面半秒
MIRROR_CD = 2.0, -- 冷却两秒
REFLECT_DMG = 35, -- 弹回三十五
BOLT_SPEED = 250, -- 箭速
-- 颜色
COLOR_MIRROR = "#a8c8f0", -- 镜蓝
COLOR_DEEP = "#2a4a7a", -- 深蓝
COLOR_GHOST = "#e0ecff", -- 镜白
ZORDER_GROUND = 118,
ZORDER_BOLT = 133,
ZORDER_EFFECT = 135,
AUTOINC_BASE = 939000,
}
--------------------------------------------------------------------------------
-- 模块内部状态
--------------------------------------------------------------------------------
local _mirrorT = 0 -- 镜面余时
local _cdLeft = 0
local _bolts = {} -- 弹道表:{ x, vx, back }
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 "MPP", 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 CreateMirrorNode()
local mp = GetActorPos(SL:GetValue("USER_ID"))
if not mp then return end
local node = GUI:Layout_Create(GUI:Attach_SceneF(), GenID("Mir"),
mp.x + 20, mp.y - 58, 0, 0, false)
GUI:setLocalZOrder(node, CONST.ZORDER_EFFECT)
for i = 1, 24 do
local a1 = (i - 1) / 24 * 6.28
local a2 = i / 24 * 6.28
GUI:DrawLine_Create(node, string.format("m_%d", i), 0, 0, 2.5,
m_cos(a1) * 8, m_sin(a1) * 26, m_cos(a2) * 8, m_sin(a2) * 26,
CONST.COLOR_GHOST, 220)
end
return node
end
骨架的两处立规。弹道表 _bolts 的元素只有横坐标、速度、方向三个字段——箭是一维的,飞矢只有正反两向,二维弹道的复杂度留给别的机制。镜面构件竖椭圆贴在玩家身前二十步——镜要立在人和箭之间,贴身画框的镜子挡不了远来的箭。镜要立在人和箭之间,贴身画框的镜子挡不了远来的箭。举镜的那半秒里玩家的移动照旧——举镜跑位两不误,镜是随手的一抬手,不是站定的仪式。
四、核心代码:完整模块(下·定时器与弹道反转)
--------------------------------------------------------------------------------
-- 举镜与箭的入账
--------------------------------------------------------------------------------
function MirrorParry.Cast()
if _cdLeft > 0 or _mirrorT > 0 then return end
_cdLeft = CONST.MIRROR_CD
_mirrorT = CONST.MIRROR_LIFE
CreateMirrorNode()
end
local function Bolt_OnHitPlayer(bolt)
if _mirrorT > 0 then
bolt.vx = -bolt.vx
bolt.back = true
SL:TipAboveMe("弹回去了")
return false
end
SL:PlayerHurt(25)
return true
end
local function Bolt_OnHitMonster(bolt)
SL:ActorHurt(SL:GetValue("MARK_TARGET_ID"), CONST.REFLECT_DMG)
SL:TipAbove(SL:GetValue("MARK_TARGET_ID"), "-" .. CONST.REFLECT_DMG)
end
--------------------------------------------------------------------------------
-- 定时器:弹道推进与两面判定
--------------------------------------------------------------------------------
if not _scheduler then
_scheduler = SL:Schedule(function(dt)
_cdLeft = math.max(0, _cdLeft - dt)
if _mirrorT > 0 then
_mirrorT = _mirrorT - dt
if _mirrorT <= 0 then
ShowTip("镜收了——没接到箭就白举")
end
end
local mp = GetActorPos(SL:GetValue("USER_ID"))
local tp = GetActorPos(SL:GetValue("MARK_TARGET_ID"))
for i = #_bolts, 1, -1 do
local b = _bolts[i]
b.x = b.x + b.vx * dt
if not b.back and mp and (b.x - mp.x) * b.vx <= 0 then
if not Bolt_OnHitPlayer(b) then
b.vx = math.abs(b.vx)
b.back = true
SL:ActorHurt(SL:GetValue("MARK_TARGET_ID"), 0)
end
end
if b.back and tp and math.abs(b.x - tp.x) < 20 then
Bolt_OnHitMonster(b)
SafeRemove(b.node)
table.remove(_bolts, i)
elseif b.x < -20 or b.x > 700 then
SafeRemove(b.node)
table.remove(_bolts, i)
end
end
end, 0.02)
end
SL:RegisterLUAEvent(LUA_EVENT_MONSTER_SHOOT, "MPP_Shoot", function(data)
if data and data.fromX then
table.insert(_bolts, { x = data.fromX, vx = -CONST.BOLT_SPEED, back = false })
end
end)
SL:ScheduleOnce(function()
ShowTip("技能已加载: 弹反镜 (调试按钮触发)")
end, 1.0)
return MirrorParry
下半场的三段判定是全模块的节拍器。箭的每帧推进后先问"过没过玩家的脸"——方向和位置一齐验,穿脸的那一帧查镜面态:举着,速度取反、方向标回、利息十点记账;没举,扣血出表。反程的箭只认怪的坐标,扎中就结算三十五——弹回去的箭走的是自己的结算口,和玩家的扣血再无瓜葛。射箭事件从 LUA_EVENT_MONSTER_SHOOT 订阅,怪的每一次出手都在模块的账上挂号。
五、调参指南与商业服怎么用
调参三笔账。窗口半秒对箭速二百五十步是"看清箭再举也来得及"的底线——窗口压到零点三秒就是纯手速考试,受众从所有人缩成主播。弹回三十五对原箭二十五是利息四成——利息必须高于原伤,玩家弹中的那一刻要有"赚了"的手感;低于原伤的弹反是哑巴亏,没人会练。冷却两秒对箭周期两点二秒是"每一箭都有机会弹"的配置——冷却长过箭的周期,某些箭注定免弹,弹反从节奏题变成抽签题。常见坑三个:弹回的箭穿过玩家自己二次判定,回程箭要标向;镜面态的攻击没冻结,举镜同时照常输出,弹反成了零成本常驻;空举没有任何反馈,玩家不知道镜收了。镜面的朝向也要跟着怪的方位转——怪绕到背后射箭,镜还举在正面就是摆设。
商业服里弹反镜是"操作流"的门面:弹反成功的特写做成品级——铁镜银光、玉镜金光,主播集锦里每一帧弹反都是免费广告。比武场里它是心理战的筹码:镜的冷却看得见,对手掐你冷却的真空期倾泻弹雨,读冷却的博弈直接长在两秒的冷却上。变体做成镜面吸收:弹回的箭再举镜再弹,连弹三次翻三倍——极限手速的天花板明码标价。变体做成镜面吸收:弹回的箭再举镜再弹,连弹三次翻三倍——极限手速的天花板明码标价。再进一步的弹反成就是镜面的新卖法:百次弹反的称号、千次弹反的称号边框,手速党的荣誉墙从此有了第一块砖。这一篇的演示和整套模块照抄能跑,改的就是 CONST 那张表。
写完留一句给做格挡系的同学:弹反卖的是"半秒的胆量"——窗口越窄,撞上镜面的那一声叮越像勋章。
全站技术干货持续更新:996 引擎 / Lua 实战帖,语法、参数与示例一篇讲透。进入文章地图 · 查看全部 →
【游戏功能】 怪的仇恨为什么永远追着你?因为它看得见你。迷魂香把"看得见"这三个字没收了:一炉香落在它脚下,烟圈六秒——圈里…
【游戏功能】 连击值攒着有什么用?多数战斗的答案是攒个伤害加成——气势给了第二个答案:攒的是一支军队的士气。每一刀涨十二点、…
【游戏功能】 一道割裂挂上去,一秒一跳——它站着跳四,追你的时候崩成八:伤口一动就崩,跑得越快掉得越凶。崩血把"放风筝"从走…
【游戏功能】 突进出去了,回不回得来?多数突进的答案是"不回"——人留在怪堆里听天由命。燕回式给突进装了归途:穿身而过去时一…
【游戏功能】 读条变红的那一秒,全世界都在等一个决定。破招式把这一秒做成整场战斗的心跳:怪交替出红劈和蓝刺,红劈读条时按下破…
【游戏功能】 出招的顺序算不算伤害?大多数战斗里不算——三招乱按和按谱出,账面一个样。连招谱把顺序变成了伤害本身:「劈→撩→…