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
相关产品推荐
相关产品推荐

