CHUAN2 DEV ENGINE
996 正版授权研发中心 · 360 授权合作教学中心 · 抖音传奇直播合作授权 · 快手推广运营商授权
OFFICIAL LICENSED ACADEMY 查验官方授权证书 →
// 威海旷世互娱教学基地 · 技术文章
进阶实战游戏功能996引擎

魂卫:杀一葬一,魂火替你出手

2026-10-01 06:03 作者:996 技术组 996引擎Lua教程传奇脚本进阶实战游戏功能996引擎

【游戏功能】

魂卫的全机制就一句:杀掉小怪化一盏魂火跟着你走,每盏魂火两秒自动劈最近的大怪一口八点,至多三盏——「引魂!」按下,三盏魂火齐射一轮二十四。收割的价值第一次写在你的身后。这篇的核心代码照引擎技能脚本的全规格写:配置表、魂火构件、收割表、索敌开火、定时器、调试按钮。

一、效果演示:杀两只,多三口

演示场里大怪带着两只小蛛。你的普攻先点名蛛:一刀一只,蛛倒的瞬间化出一盏青蓝色的魂火,飘到你的轨道上——头顶"魂火 ×1"。两只蛛清完,你身后跟着两盏魂火,两秒一口地自动劈大怪,你普攻的每一拍之间都夹着魂火的八点。点「引魂!」:两盏魂火同时出刀,一轮十六——引魂是把被动收割变成主动齐射的扳机。大怪拍你的时候魂火还在劈——它的仇恨在你身上,魂火的刀在它背上。演示里试一次只杀一只就开打:一盏魂火的账和两盏的账,差的就是那一只你没杀的蛛。

demo
 fx-soulwisp

二、底层原理:一张收割表加一套自动索敌

模块的账本是一张收割表。小怪的死亡钩子往表里压一盏魂火:至多三盏,满了再杀是白给——白给的提示要直说,玩家的下一刀才知道该转向大怪。魂火各自带两秒的出手计时,出手时对最近的敌人劈一口八点——魂火的索敌独立于玩家的目标:玩家打蛛,魂火劈大怪,两线的输出互不占拍。两线的输出互不占拍——你打你的、它劈它的,魂卫从不动玩家的目标锁定。引魂是收割表的扳机:把所有魂火的出手计时清零,下一拍齐射——被动收割攒、主动齐射放,攒放之间就是这门机制的节奏。魂火的轨道是纯视觉:三盏魂火绕着玩家飘,位置只给眼睛看,伤害的账全在索敌的计时器里。

三、核心代码:完整模块(上·骨架)

lua
--------------------------------------------------------------------------------
-- @file        SoulWisp.lua
-- @brief       魂卫 —— 杀一葬一,魂火替你出手
--              客户端表现模块:配置表 / 魂火构件 / 收割表 / 定时器
--------------------------------------------------------------------------------

local SoulWisp = {}

--------------------------------------------------------------------------------
-- 配置与常量
--------------------------------------------------------------------------------
local CONST = {
    WISP_CAP      = 3,          -- 至多三盏
    WISP_GAP      = 2.0,        -- 每盏两秒一劈
    WISP_DMG      = 8,          -- 一劈八点
    ORBIT_R       = 46,         -- 轨道半径四十六

    -- 颜色
    COLOR_WISP    = "#8fd4f0",  -- 魂青
    COLOR_DEEP    = "#1f4f8f",  -- 深青
    COLOR_GHOST   = "#d8f0ff",  -- 魂白

    ZORDER_GROUND = 118,
    ZORDER_WISP   = 133,
    ZORDER_EFFECT = 135,

    AUTOINC_BASE  = 942000,
}

--------------------------------------------------------------------------------
-- 模块内部状态
--------------------------------------------------------------------------------
local _wisps     = {}             -- 魂火表:每盏一个出手计时
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 "SLW", 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 CreateWispNode()
    local node = GUI:Layout_Create(GUI:Attach_SceneF(), GenID("Wisp"), 0, 0, 0, 0, false)
    GUI:setLocalZOrder(node, CONST.ZORDER_WISP)
    for i = 1, 10 do
        local a1 = (i - 1) / 10 * 6.28
        local a2 = i / 10 * 6.28
        GUI:DrawLine_Create(node, string.format("w_%d", i), 0, 0, 2,
            m_cos(a1) * 10, m_sin(a1) * 10, m_cos(a2) * 10, m_sin(a2) * 10,
            CONST.COLOR_WISP, 190)
    end
    return node
end

骨架的三处立规。魂火表按盏存对象、每盏自带头像节点和出手计时——魂火是独立单位不是共享读数,齐射的扳机才能逐盏触发。WISP_CAP 的封顶写在收割钩子里——满了之后的击杀不生成魂火,也不顶掉旧魂火,三盏是上限不是队列。魂火构件双层光珠加外圈光晕——魂火要"轻",画重了像灯笼,轻了才像一缕不肯散的魂。

四、核心代码:完整模块(下·定时器与索敌开火)

lua
--------------------------------------------------------------------------------
-- 收割钩子与索敌开火
--------------------------------------------------------------------------------
local function Wisp_Spawn()
    if #_wisps >= CONST.WISP_CAP then
        ShowTip("三盏满了——这一杀白给")
        return
    end
    local node = CreateWispNode()
    table.insert(_wisps, { node = node, fireT = 1.0, seed = #_wisps * 2.1 })
    SL:TipAboveMe("魂火+" .. #_wisps)
end

local function Wisp_Fire(w, mp, tid, tp)
    SL:ActorHurt(tid, CONST.WISP_DMG)
    SL:TipAbove(tid, "-" .. CONST.WISP_DMG)
    SL:PlayWispBolt(mp.x, mp.y, tp.x, tp.y)
end

--------------------------------------------------------------------------------
-- 引魂齐射
--------------------------------------------------------------------------------
function SoulWisp.CallAll()
    for i = 1, #_wisps do
        _wisps[i].fireT = 0
    end
    if #_wisps > 0 then
        ShowTip(#_wisps .. "盏魂火齐劈——" .. (#_wisps * CONST.WISP_DMG) .. "点一轮")
    else
        ShowTip("没有魂火——先杀小怪攒")
    end
end

--------------------------------------------------------------------------------
-- 定时器:轨道绘制与索敌节拍
--------------------------------------------------------------------------------
if not _scheduler then
    _scheduler = SL:Schedule(function(dt)
        local mp = GetActorPos(SL:GetValue("USER_ID"))
        if not mp then return end
        local tid = SL:GetValue("MARK_TARGET_ID")
        local tp = tid and GetActorPos(tid)
        for i = 1, #_wisps do
            local w = _wisps[i]
            local a = SL:GetValue("SYS_TIME") * 1.6 + w.seed
            local wx = mp.x + m_cos(a) * CONST.ORBIT_R
            local wy = mp.y - 56 + m_sin(a * 1.3) * 20
            SL:MoveNode(w.node, wx, wy)
            if not tp then
                w.fireT = math.min(w.fireT, 0.4)
            else
                w.fireT = w.fireT - dt
                if w.fireT <= 0 then
                    w.fireT = CONST.WISP_GAP
                    Wisp_Fire(w, mp, tid, tp)
                end
            end
        end
    end, 0.02)
end

SL:RegisterLUAEvent(LUA_EVENT_MONSTER_DEAD, "SLW_Kill", function(data)
    if data and data.isMinion then
        Wisp_Spawn()
    end
end)

SL:RegisterLUAEvent(LUA_EVENT_SCENE_CLEAR, "SLW_Clear", function()
    for i = 1, #_wisps do SafeRemove(_wisps[i].node) end
    _wisps = {}
end)

SL:ScheduleOnce(function()
    ShowTip("技能已加载: 魂卫 (调试按钮触发)")
end, 1.0)

return SoulWisp

下半场的两处见功夫。魂火的轨道相位用 seed 错开——三盏魂火各转各的圈,重叠成一盏的版本就是没写 seed。索敌的目标无着落时把出手计时压回零点四——没有可打的目标时魂火憋着不空劈,目标一出现半秒内接上火力;劈完的出手计时重置回两秒,齐射的扳机就是把所有盏的计时器同时清零,清得越齐,那一轮越像一张弓。

五、调参指南与商业服怎么用

调参三笔账。三盏封顶对两秒一劈八点,满魂火的被动产值是每秒十二——贴着玩家普攻产值的八成走,魂卫是副刀,盖过普攻的魂卫会反客为主。收割上限三盏对两只小怪的场面意味着"有杀必满"——想让玩家珍惜每一杀的版本,把上限压到二、把怪提到三只,白给的击杀就有了取舍。引魂齐射是把被动转主动的扳机——齐射产值二十四不设冷却门槛的设计,齐射后所有盏从零计时,齐射的代价是两秒的火力真空。齐射后所有盏从零计时,齐射的代价是两秒的火力真空。魂火的照明也有讲究——魂火飘过的地方地面泛一层淡青,三盏齐亮的时候,玩家身后像提着三盏灯笼。魂火飘过的地方地面泛一层淡青,三盏齐亮的时候,玩家身后像提着三盏灯笼。引魂齐射的出手也要有先后——三盏错开十分之一拍齐劈,数字三连跳比同帧一笔好看得多。常见坑三个:小怪的死因不查,魂火被毒死的也算玩家收割;玩家死亡魂火不散,复活后带着上一世的魂火;魂火的轨道节点不随玩家瞬移更新,传送后魂火从半场外飘过来。

商业服里魂卫是"收割美学"的门面:魂火的颜色按赛季换——春樱、夏萤、秋枫,收割的每一杀都带着当季的限定色。副本里它是清怪速度的乘法:三盏魂火齐劈的清场效率把小怪关的通关时间砍半,效率榜上魂卫玩家的名字永远靠前。变体做成魂火继承:玩家倒下时魂火飞向队友,收割权随魂火移交——团队玩法里最浪漫的交接棒。这一篇的演示和整套模块照抄能跑,改的就是 CONST 那张表。

写完留一句给做收割系的同学:魂卫卖的是"每一杀都有回响"——身后飘着的三盏魂火,是把击杀清单挂在了玩家腰间。

作者履历与出处

本文由 996 技术组基于 996 引擎官方知识库与浮生梦老师课程体系整理。团队长期从事传奇类引擎 Lua 后端逻辑、客户端界面与商业版本交付,内容以官方知识库与真实项目为出处,按版本持续修订。

← 返回文章地图返回研学路径

幂尔框架 · 实战干货 · 接口调用

LATEST ARTICLES

全站技术干货持续更新:996 引擎 / Lua 实战帖,语法、参数与示例一篇讲透。进入文章地图 · 查看全部 →

进阶实战游戏功能

迷魂香:一炉香点着,圈里的怪掉头打转

【游戏功能】 怪的仇恨为什么永远追着你?因为它看得见你。迷魂香把"看得见"这三个字没收了:一炉香落在它脚下,烟圈六秒——圈里…

2026-10-01 06:32 996 技术组
进阶实战游戏功能

气势:连击涨势 挨打掉势 提气回血五十

【游戏功能】 连击值攒着有什么用?多数战斗的答案是攒个伤害加成——气势给了第二个答案:攒的是一支军队的士气。每一刀涨十二点、…

2026-10-01 06:32 996 技术组
进阶实战游戏功能

崩血:伤口一动就崩,跑得越快掉血越凶

【游戏功能】 一道割裂挂上去,一秒一跳——它站着跳四,追你的时候崩成八:伤口一动就崩,跑得越快掉得越凶。崩血把"放风筝"从走…

2026-10-01 06:32 996 技术组
进阶实战游戏功能

燕回式:突进穿它一遭,原路飞回起手点

【游戏功能】 突进出去了,回不回得来?多数突进的答案是"不回"——人留在怪堆里听天由命。燕回式给突进装了归途:穿身而过去时一…

2026-10-01 06:32 996 技术组
进阶实战游戏功能

破招式:它抡重刀你抢半拍,招克招

【游戏功能】 读条变红的那一秒,全世界都在等一个决定。破招式把这一秒做成整场战斗的心跳:怪交替出红劈和蓝刺,红劈读条时按下破…

2026-10-01 06:32 996 技术组
进阶实战游戏功能

连招谱:三招按谱出,谱尾一击翻倍

【游戏功能】 出招的顺序算不算伤害?大多数战斗里不算——三招乱按和按谱出,账面一个样。连招谱把顺序变成了伤害本身:「劈→撩→…

2026-10-01 06:32 996 技术组