You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Roblox:Humanoid.Died事件触发函数重复执行20+次问题求助

Roblox开发问题排查:事件重复触发与数值多次累加

问题描述

在Roblox开发中,Humanoid.Died()或BindableEvent触发时,绑定的函数会执行20多次,对应的IntegerValue也会被累加相应次数。已尝试防抖(debounce)、BindableEvent、connection:Disconnect()等方法,但问题仍未解决。

相关代码

模块中的damageHumanoid函数

function module.damageHumanoid(attacker, player, humanoid, damage)
    local connection
    local debounce = false
    connection = humanoid.Died:Connect(function()
        if not debounce then
            debounce = true
            connection:Disconnect()
            module.onDeath(player)
        end
    end)
    

    if player:FindFirstChild("damage") == nil then
        newFolder = Instance.new("Folder")
        newFolder.Name = "damage"
        newFolder.Parent = player
    end

    if newFolder:FindFirstChild(tostring(attacker)) == nil then
        record = Instance.new("IntValue")
        record.Name = tostring(attacker)
        record.Parent = newFolder
    end

    if record.Value >= 100 then -- sanity check

    else
        record.Value = record.Value + damage
    end

    return humanoid:TakeDamage(damage)  

end

触发的onDeath函数

function module.onDeath(player)
if player:FindFirstChild("damage") ~= nil then
    local folder = player:WaitForChild("damage")
    local contents = folder:GetChildren()

    for i,v in pairs(contents) do
        local item = folder[tostring(v)]
        damages[item.Name] = item.Value
    end
       
end

local debounce = false
if not debounce then
    debounce = true
    module.handleKill(damages)
end
end

剑脚本的Blow函数

function Blow(Hit)
    if not Hit or not Hit.Parent or not CheckIfAlive() or not ToolEquipped then
        return
    end
    local RightArm = Character:FindFirstChild("Right Arm") or Character:FindFirstChild("RightHand")
    if not RightArm then
        return
    end
    local RightGrip = RightArm:FindFirstChild("RightGrip")
    if not RightGrip or (RightGrip.Part0 ~= Handle and RightGrip.Part1 ~= Handle) then
        return
    end
    local character = Hit.Parent
    if character == Character then
        return
    end
    local humanoid = character:FindFirstChildOfClass("Humanoid")
    if not humanoid or humanoid.Health == 0 then
        return
    end
    local player = Players:GetPlayerFromCharacter(character)
    if player and player == Player then
        return
    end
    UntagHumanoid(humanoid)
    TagHumanoid(humanoid, Player)
    module.damageHumanoid(plur, player, humanoid, Damage)
end

问题根源分析

  1. 重复绑定Died事件:每次调用damageHumanoid都会给同一个Humanoid绑定一次Died事件,多次攻击后会形成多个事件连接,Humanoid死亡时所有连接的回调都会执行,导致函数重复触发。
  2. 防抖变量作用域错误:damageHumanoid中的debounce是局部变量,每个事件连接的回调都拥有独立的debounce,无法起到全局防抖的作用;onDeath中的debounce同样是函数内局部变量,每次调用都会重置为false,完全无效。
  3. 未声明局部变量:newFolder、record未用local声明,成为全局变量,可能导致变量被意外覆盖,引发逻辑错误。
  4. 全局damages变量污染:onDeath中直接使用全局damages表,多次调用会导致数据叠加,且未清理旧数据。

解决方案与修正代码

核心修复点

  • 确保Humanoid.Died事件只绑定一次,使用Once连接(自动执行一次后断开)或维护已绑定标记表。
  • 将防抖变量提升到模块级,确保同一玩家/对象的事件回调共享防抖状态。
  • 所有局部变量添加local声明,避免全局污染。
  • 每次处理死亡时创建独立的damages表,避免数据残留。

修正后的代码

模块中的damageHumanoid函数

-- 模块内维护已绑定Died事件的Humanoid,避免重复绑定
local boundHumanoids = {}

function module.damageHumanoid(attacker, player, humanoid, damage)
    -- 仅为未绑定过的Humanoid添加Died事件
    if not boundHumanoids[humanoid] then
        boundHumanoids[humanoid] = true
        -- 使用Once连接,自动执行一次后断开
        humanoid.Died:Once(function()
            boundHumanoids[humanoid] = nil -- 清理标记,允许玩家复活后重新绑定
            module.onDeath(player)
        end)
    end

    -- 局部变量声明,避免全局污染
    local damageFolder = player:FindFirstChild("damage")
    if not damageFolder then
        damageFolder = Instance.new("Folder")
        damageFolder.Name = "damage"
        damageFolder.Parent = player
    end

    local attackerId = tostring(attacker)
    local damageRecord = damageFolder:FindFirstChild(attackerId)
    if not damageRecord then
        damageRecord = Instance.new("IntValue")
        damageRecord.Name = attackerId
        damageRecord.Parent = damageFolder
    end

    -- 简化逻辑
    if damageRecord.Value < 100 then
        damageRecord.Value += damage
    end

    return humanoid:TakeDamage(damage)
end

触发的onDeath函数

-- 模块级防抖表,防止同一玩家死亡事件被重复处理
local deathHandled = {}

function module.onDeath(player)
    -- 已处理过死亡则直接返回
    if deathHandled[player] then return end
    deathHandled[player] = true

    local damageFolder = player:FindFirstChild("damage")
    if not damageFolder then return end

    -- 创建独立的damages表,避免全局数据污染
    local damages = {}
    for _, record in ipairs(damageFolder:GetChildren()) do
        if record:IsA("IntValue") then
            damages[record.Name] = record.Value
        end
    end

    module.handleKill(damages)

    -- 清理玩家的damage文件夹,避免下次死亡重复读取
    damageFolder:Destroy()

    -- 5秒后清理防抖标记,允许玩家复活后重新处理死亡事件
    task.delay(5, function()
        deathHandled[player] = nil
    end)
end

剑脚本的Blow函数

function Blow(Hit)
    if not Hit or not Hit.Parent or not CheckIfAlive() or not ToolEquipped then
        return
    end
    local RightArm = Character:FindFirstChild("Right Arm") or Character:FindFirstChild("RightHand")
    if not RightArm then
        return
    end
    local RightGrip = RightArm:FindFirstChild("RightGrip")
    if not RightGrip or (RightGrip.Part0 ~= Handle and RightGrip.Part1 ~= Handle) then
        return
    end
    local character = Hit.Parent
    if character == Character then
        return
    end
    local humanoid = character:FindFirstChildOfClass("Humanoid")
    -- 修正判断条件,Health<=0时直接返回
    if not humanoid or humanoid.Health <= 0 then
        return
    end
    local player = Players:GetPlayerFromCharacter(character)
    if player and player == Player then
        return
    end
    UntagHumanoid(humanoid)
    TagHumanoid(humanoid, Player)
    -- 修正笔误:plur改为Player
    module.damageHumanoid(Player, player, humanoid, Damage)
end

内容的提问来源于stack exchange,提问作者DrGigaByte

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 08:10:31