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

Roblox Studio Bug:Remote Event传递OilBarrel参数为nil的修复

问题描述

在Roblox项目中遇到Bug:本地脚本通过Remote Event触发拾取油桶操作,传递参数OilBarrel,但服务器脚本接收该参数时显示为nil(本地脚本打印参数正常),执行拾取时抛出错误:Attempt to index nil with CFrame。

本地脚本代码

local WS = game:GetService("Workspace")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")

local RemoteEvents = ReplicatedStorage:WaitForChild("RemoteEvents")
local TakeOilBarrelEvent = RemoteEvents.TakeOilBarrel
local ThrowOilBarrelEvent = RemoteEvents.ThrowOilBarrel
local SpawnOilBarrelEvent = RemoteEvents.SpawnOilBarrel

local plr = Players.LocalPlayer
local char = plr.Character
local torso = char:WaitForChild("LowerTorso")

local mouse = plr:GetMouse()

local OilBarrelGui = script.Parent.Parent.OilBarrelGui
local DropOilBarrel = OilBarrelGui.DropOilBarrel

local debounce = false

local function getTycoon(player)
    for _, tycoon in game.Workspace.Tycoons:GetChildren() do
        if tycoon:GetAttribute("UserId") == player.UserId then
            return tycoon
        end
    end
end

local function ThrowOilBarrel(OilBarrel)
    ThrowOilBarrelEvent:FireServer(OilBarrel)
    OilBarrelGui.Enabled = false

    debounce = false
end

local function TakeOilBarrel(OilBarrel)
    TakeOilBarrelEvent:FireServer(OilBarrel)

    OilBarrelGui.Enabled = true
end

SpawnOilBarrelEvent.OnClientEvent:Connect(function()
    local Tycoon = getTycoon(plr)
    
    local OilBarrelStation = Tycoon.Decoration.OilBarrelStation.Platform
    local OilBarrel = ReplicatedStorage:WaitForChild("OilBarrel")
    
    local OilBarrelClone = OilBarrel:Clone()

    OilBarrelClone.Parent = WS:WaitForChild("OilBarrels")
    OilBarrelClone.Position = OilBarrelStation.Position + Vector3.new(0, 2, 0)
    
    local OilBarrels = WS:WaitForChild("OilBarrels"):GetChildren()
    
    for _, OilBarrel in ipairs(OilBarrels) do
        
        if OilBarrel.Name == "OilBarrel" then
            
            OilBarrel.ClickDetector.MouseClick:Connect(function(player)
                if OilBarrel:FindFirstChild("Weld") then return end
                
                if player == plr then
                    
                    if debounce then return end
                    debounce = true
                    
                    TakeOilBarrel(OilBarrel)

                    DropOilBarrel.MouseButton1Click:Connect(function()
                        ThrowOilBarrel(OilBarrel)
                    end)

                    mouse.KeyDown:Connect(function(key)
                        if key == "q" then
                            ThrowOilBarrel(OilBarrel)
                        end
                    end)
                end
            end)
        else
            continue
        end
    end
end)

服务器脚本代码

TakeOilBarrelEvent.OnServerEvent:Connect(function(player, OilBarrel)
    
    local char = workspace:WaitForChild(player.Name)
    
    local Animate = char:FindFirstChild("Animate")
    
    if Animate then
        Animate:Destroy()
    end
    
    local OilBarrelAnimation = game:GetService("ReplicatedStorage"):WaitForChild("Animations").OilBarrelAnimation
    
    if char:FindFirstChild("OilBarrelAnimation") == nil then
        local OilBarrelAnimationClone = OilBarrelAnimation:Clone()
        
        OilBarrelAnimationClone.Parent = char
    end
    
    local torso = char:WaitForChild("LowerTorso")
    
    local weld = Instance.new("Weld")
    weld.Parent = OilBarrel
    weld.Part0 = OilBarrel
    weld.Part1 = torso
        
    OilBarrel.CFrame = torso.CFrame
    OilBarrel.Rotation = Vector3.new(0, -180, -90)
    OilBarrel.Position = OilBarrel.Position + torso.CFrame.LookVector * 2.2
end)

修复方案

1. 核心问题根源

客户端直接在本地Workspace克隆生成油桶,该实例仅存在于客户端本地,服务器没有对应的实例副本。通过Remote Event传递客户端本地实例时,服务器无法解析,因此得到nil。

2. 具体修复步骤

步骤1:将油桶生成逻辑移至服务器

创建新的Remote Event让客户端触发生成请求,服务器负责克隆并放置油桶(保证服务器和客户端都能看到实例):

服务器新增代码:

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Workspace = game:GetService("Workspace")
local RemoteEvents = ReplicatedStorage:WaitForChild("RemoteEvents")

-- 创建请求生成油桶的Remote Event
local RequestSpawnOilBarrel = Instance.new("RemoteEvent")
RequestSpawnOilBarrel.Name = "RequestSpawnOilBarrel"
RequestSpawnOilBarrel.Parent = RemoteEvents

RequestSpawnOilBarrel.OnServerEvent:Connect(function(player)
    -- 查找玩家对应的Tycoon
    local function getTycoon(p)
        for _, tycoon in Workspace.Tycoons:GetChildren() do
            if tycoon:GetAttribute("UserId") == p.UserId then
                return tycoon
            end
        end
    end
    local Tycoon = getTycoon(player)
    if not Tycoon then return end

    local OilBarrelStation = Tycoon.Decoration.OilBarrelStation.Platform
    local OilBarrel = ReplicatedStorage:WaitForChild("OilBarrel")
    local OilBarrelClone = OilBarrel:Clone()

    OilBarrelClone.Parent = Workspace:WaitForChild("OilBarrels")
    OilBarrelClone.Position = OilBarrelStation.Position + Vector3.new(0, 2, 0)
    
    -- 通知客户端更新相关逻辑
    RemoteEvents.SpawnOilBarrelEvent:FireClient(player)
end)

客户端修改Spawn逻辑:
移除本地克隆代码,改为触发服务器生成请求,并通过ChildAdded监听新油桶来绑定事件:

-- 替换原SpawnOilBarrelEvent.OnClientEvent的逻辑
RemoteEvents.RequestSpawnOilBarrel:FireServer()

-- 监听Workspace中新增的油桶,绑定点击事件(避免重复绑定)
Workspace.OilBarrels.ChildAdded:Connect(function(child)
    if child.Name ~= "OilBarrel" then return end
    local clickDetector = child:WaitForChild("ClickDetector")
    
    clickDetector.MouseClick:Connect(function(player)
        if child:FindFirstChild("Weld") then return end
        if player ~= plr then return end
        if debounce then return end
        debounce = true
        
        TakeOilBarrel(child)

        -- 解绑之前的丢弃事件,避免重复触发
        DropOilBarrel.MouseButton1Click:DisconnectAll()
        DropOilBarrel.MouseButton1Click:Connect(function()
            ThrowOilBarrel(child)
        end)

        -- 用UserInputService替代已废弃的KeyDown
        local inputConnection
        inputConnection = game:GetService("UserInputService").InputBegan:Connect(function(input, gameProcessed)
            if gameProcessed then return end
            if input.KeyCode == Enum.KeyCode.Q then
                ThrowOilBarrel(child)
                inputConnection:Disconnect()
            end
        end)
    end)
end)

步骤2:传递实例标识而非直接传Instance

修改客户端传递参数的逻辑,改为传递油桶的InstanceId,服务器根据ID查找实例:

客户端修改TakeOilBarrel函数:

local function TakeOilBarrel(OilBarrel)
    TakeOilBarrelEvent:FireServer(OilBarrel.InstanceId)
    OilBarrelGui.Enabled = true
end

服务器修改接收逻辑:

TakeOilBarrelEvent.OnServerEvent:Connect(function(player, oilBarrelId)
    -- 根据InstanceId查找油桶实例
    local OilBarrel = nil
    for _, barrel in ipairs(game:GetService("Workspace").OilBarrels:GetChildren()) do
        if barrel.InstanceId == oilBarrelId then
            OilBarrel = barrel
            break
        end
    end
    if not OilBarrel then
        warn("无法找到对应油桶实例")
        return
    end

    -- 验证油桶是否属于玩家的Tycoon(防止作弊)
    local function getTycoon(p)
        for _, tycoon in game.Workspace.Tycoons:GetChildren() do
            if tycoon:GetAttribute("UserId") == p.UserId then
                return tycoon
            end
        end
    end
    local Tycoon = getTycoon(player)
    if not Tycoon then return end

    -- 原逻辑继续执行
    local char = workspace:WaitForChild(player.Name)
    local Animate = char:FindFirstChild("Animate")
    if Animate then
        Animate:Destroy()
    end
    local OilBarrelAnimation = game:GetService("ReplicatedStorage"):WaitForChild("Animations").OilBarrelAnimation
    if char:FindFirstChild("OilBarrelAnimation") == nil then
        local OilBarrelAnimationClone = OilBarrelAnimation:Clone()
        OilBarrelAnimationClone.Parent = char
    end
    local torso = char:WaitForChild("LowerTorso")
    local weld = Instance.new("Weld")
    weld.Parent = OilBarrel
    weld.Part0 = OilBarrel
    weld.Part1 = torso
    OilBarrel.CFrame = torso.CFrame
    OilBarrel.Rotation = Vector3.new(0, -180, -90)
    OilBarrel.Position = OilBarrel.Position + torso.CFrame.LookVector * 2.2
end)

步骤3:优化事件绑定逻辑

原客户端每次Spawn事件触发时都会遍历所有油桶重复绑定点击事件,改用ChildAdded监听新增实例,只绑定一次新生成的油桶;同时替换已废弃的KeyDown为UserInputService,并在触发后解绑事件避免内存泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 03:35:56