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

尸爆:杀一个炸一片,尸气也能开路

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

【游戏功能】

后台追过一条隐蔽报错:清怪关卡的通关时间偶尔会出现零点五秒的离谱极值——最快的记录快得像开了加速。翻了三天的录像才看明白:那是一位尸爆玩家把"清怪"玩成了"点火"——第一只怪倒下的位置恰到好处,一记引爆把剩下两只全送走,通关时间自然被压缩到极限。报错没修,机制倒是被验证了:尸爆的连锁,上限比谁想的都高。这篇把整套模块拆开:配置表、尸气表、引爆出口、定时器、调试按钮,一段一段照抄能跑。

一、效果演示:杀一个炸一片,越杀越快

演示场是一只大怪带两只小怪。普攻先撂倒一只小怪:它的尸气当场落成一摊土黄色的印记趴在地上。点「引爆!」:印记炸开一圈土黄的光——二十点尸气伤波及九十步内所有活物,大怪掉血,旁边那只小怪也挨了一口;要是这一口把它也送走,它的尸气又落一摊——再点引爆,大怪接着挨。演示里试两条路线:把两只小怪都留成尸气一齐引爆,和杀一只炸一只——一齐炸的那笔总账,比单体输出漂亮得多。尸爆教的是清怪的美学:倒下的位置,比倒下的顺序重要。

demo
 fx-corpseblast

二、底层原理:一张尸气表加一个波及出口

模块的机关是一对搭档。尸气表:每只怪死亡的那一帧,死亡坐标压进尸气表——尸气不消失、不衰减,趴在地上等引爆,表里的每一笔都是一枚待发的雷。波及出口:引爆时把表里的尸气逐一炸开——每摊尸气对九十步内所有存活目标各结二十点,炸完的尸气出表;炸死的目标自己再落一摊新的尸气进表——表进表出之间,连锁就转起来了。和火墙那些地面持续伤害的分野在于"一次性":火墙按拍烧,尸爆按雷炸——尸气是库存不是阵地,攒着,等一个值得的时机一次清仓。

连锁为什么要靠新尸气进表而不是直接续炸?直接续炸的版本一只怪能把全场炸成多米诺,通关变成比谁手快的竞速;新尸气要再花一次引爆才有价值,节奏感留在玩家手里,连锁的上限才有讲究。

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

lua
-- @file CorpseBlast.lua
-- 尸爆 —— 杀一个炸一片,尸气也能开路

local CorpseBlast = {}

local CONST = {
    BLAST_RADIUS  = 90,
    BLAST_DMG     = 20,
    BLAST_CD      = 3.0,

    COLOR_CORPSE  = "#b08a5a",
    COLOR_DEEP    = "#4e3a1e",
    COLOR_GHOST   = "#e8d8b8",

    ZORDER_MARK   = 129,
    ZORDER_FX     = 135,
    AUTOINC_BASE  = 998000,
}

local _marks     = {}
local _boomCd    = 0
local _scheduler = nil
local _autoInc   = 0

local function GenID(prefix)
    _autoInc = _autoInc + 1
    return string.format("%s_%d", prefix or "CBT",
        CONST.AUTOINC_BASE + _autoInc)
end

local function GetActorPos(id)
    if not id then return nil end
    local x = SL:GetValue("ACTOR_POSITION_X", id)
    local y = SL:GetValue("ACTOR_POSITION_Y", id)
    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 DropMark(x, y)
    local node = GUI:Layout_Create(GUI:Attach_SceneF(),
        GenID("Mark"), x, y, 0, 0, false)
    GUI:setLocalZOrder(node, CONST.ZORDER_MARK)
    GUI:DrawRect_Create(node, "m", -4, -14, 8, 14,
        CONST.COLOR_CORPSE, 210)
    table.insert(_marks, { x = x, y = y, node = node })
    return node
end

local function OnAnyDeath(tid)
    local tp = GetActorPos(tid)
    if tp then
        DropMark(tp.x, tp.y)
        ShowTip("倒了一具——尸气落地,等引爆")
    end
end

四、核心代码:完整模块(下·定时器与引爆连锁)

lua
-- 引爆出口:清表逐摊炸
function CorpseBlast.Detonate()
    if _boomCd > 0 then
        ShowTip("尸气在聚——" .. string.format("%.1f", _boomCd) .. "秒")
        return
    end
    if #_marks == 0 then
        ShowTip("地上没尸气——先放倒一只")
        return
    end
    _boomCd = CONST.BLAST_CD
    local hits = 0
    for i = #_marks, 1, -1 do
        local mk = _marks[i]
        SL:PlayBlastFx(mk.x, mk.y)
        for _, tid in ipairs(SL:GetAllActorsInView()) do
            local tp = GetActorPos(tid)
            if tp and math.abs(tp.x - mk.x) <= CONST.BLAST_RADIUS then
                SL:ActorHurt(tid, CONST.BLAST_DMG)
                SL:TipAbove(tid, "-20")
                hits = hits + 1
                if SL:GetValue("ACTOR_DEAD", tid) then
                    OnAnyDeath(tid)
                end
            end
        end
        SafeRemove(mk.node)
        table.remove(_marks, i)
    end
    SL:ScreenShake(3.2, 0.2)
    if hits > 0 then ShowTip("引爆了——" .. hits .. "个挨了尸气") end
end

-- 死亡钩子:尸气自动入表
SL:RegisterLUAEvent(LUA_EVENT_ACTOR_DEATH, "CBT_Drop", function(tid)
    OnAnyDeath(tid)
end)

-- 定时器:冷却推进
if not _scheduler then
    _scheduler = SL:Schedule(function(dt)
        _boomCd = math.max(0, _boomCd - dt)
    end, 0.02)
end

SL:ScheduleOnce(function()
    SL:BindDebugButton("引爆", function()
        CorpseBlast.Detonate()
    end)
    ShowTip("技能已加载: 尸爆 (调试按钮触发)")
end, 1.0)

function CorpseBlast.Unload()
    if _scheduler then SL:Unschedule(_scheduler) end
    SL:UnregisterLUAEvent(LUA_EVENT_ACTOR_DEATH, "CBT_Drop")
    for i = #_marks, 1, -1 do SafeRemove(_marks[i].node) end
    _marks = {}
end

return CorpseBlast

五、机制问答

问:尸气有保质期吗?
答:没有——尸气趴到战斗结束为止,过时清仓的版本玩家会舍不得引爆,攒着等大场面的心理要留给玩家自己,机制不催。

问:尸爆的二十点能吃到破势加成吗?
答:能——尸爆走的是正常结算线,增伤减伤一概照认,机制互不挡道是老规矩。

问:引爆会伤到自己吗?
答:不会——尸气认敌我,波及出口只挑敌方目标,友军站进圈里也白搭,这是引爆敢按的底线。

问:两只尸气挨着炸同一只怪,双倍吗?
答:双倍——每摊尸气独立结算,叠着放的两摊就是四十点,站位摆得好,尸气也能叠出重炮的味道。

问:头目的尸体也能留尸气吗?
答:能而且更大——大体型的头目落两摊尸气,头目战的最后一口要是把它引在怪堆里,清场就是一瞬的事。

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

第四笔是波及的半径:九十步对怪的间距一百二十步是"一摊炸一只、两摊炸一片"的尺度——半径太大引爆成了无脑清屏,太小连锁断在半路。第三笔是单发的账:二十点对尸气落点自由是"一次引爆顶一次普攻"的及格线——尸爆的总账全靠摊数堆,单发太弱没人等连锁,太强杀一只就够了。第二笔是冷却的位置:三秒是"连点两次"的手感间隔——连锁真正的等待是引爆的时机不是按键,冷却只管防手抖。第一笔是尸气的可读性:印记的明暗随尸气摊数呼吸——地上亮几摊,玩家心里就有几发雷,库存可视化是攒雷玩法的全部安全感。常见坑三个:怪被推下场的死亡坐标出界,尸气落在空气里;引爆波及了刚落的新尸气,连锁循环炸成死循环;切场景清表不清理印,下一场地上留着上一场的雷。

商业服里尸爆是"收尸人流派"的独门生意:别人杀怪收经验,他杀怪收库存——商城里尸气印记的样式做成品级,土印、骨印、幽冥印,地上趴着的一排印就是收尸人的账本。运营落地还有两笔:一段是连锁教学——教学关只教一句"把怪杀在一处再点火",一句话讲完流派的灵魂;一段是引爆榜——按单次引爆波及数排名,连锁玩家的每一次极限点火都有据可查。变体做成尸气借风:引爆的波及方向顺着风向拉长成锥形——风大的时候连炸半场,看天吃饭的浪漫。这一篇的演示和整套模块照抄能跑,改的就是 CONST 那张表。

写完留一句给做连锁系的同学:尸爆卖的是"倒下的价值不止一口血"——地上那一摊摊土黄色的印记,是每一场战斗留给你的存货,点火的那一下,全是利息。

作者履历与出处

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

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

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

LATEST ARTICLES

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

进阶实战游戏功能

赊账:这一刀双倍先赊着,三秒后连本带息还

【游戏功能】 先消费后还款这事儿,战场上也行得通?赊账的答案是行得通,但有利息:点一下赊账,下一刀双倍——多出来的那十点伤害…

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

全垒:把小怪挑上天,一记扣杀双倍入账

【游戏功能】 为什么棒球里全垒打最值钱?因为球在天上的那一秒,全场的眼睛都仰着头。全垒把这份仰头搬进了打架:一记挑空把小怪送…

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

迟箭:把它的箭慢放成蜗牛,箭雨里都能散步

【游戏功能】 后台埋点抓到一个怪数据:某位玩家的承伤记录里,被远程命中数是零——不是躲得快,是他挨的每一箭都"消失了"。翻录…

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

借刀:站到它队友身后,借它自己的刀劈它

【游戏功能】 P.x, C.x = C.x, P.x —— 一行换位,借刀的全部机关就藏在这一笔里:玩家和小怪换个位置,怪的…

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

搬兵:抓一只小的抡起来,砸得大的直哆嗦

【游戏功能】 打群架的最高境界是什么?是一个人打,让对面自己人打自己人。搬兵把这句话演成了动作:抓住一只小怪举过头顶,抡圆了…

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

越战:开战越久劲越足,三十秒练出一口气

【游戏功能】 同样的装备,为什么老玩家打得越来越疼?新手第一刀十点,第一百刀还是十点;老玩家第一刀十点,第一百刀已经十四——…

2026-10-01 12:59 996 技术组