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

Roblox可购买墙门系统问题:购买后门对所有玩家消失

Roblox墙门系统:解决购买状态全局共享问题

问题描述

我制作了一个Roblox墙/门系统,玩家靠近墙门时,若leaderstats中的Money满足1000的购买需求,墙门会消失并允许通行,购买状态通过DataStore保存。但当前存在核心问题:一名玩家完成购买后,墙门会对所有玩家消失,不符合每个玩家独立拥有购买状态的预期。

问题根源

  1. 全局属性修改:现有脚本直接修改服务器上墙门实例的Transparency和CanCollide属性,这些属性是全服同步的,所有客户端都会看到相同状态。
  2. 重复脚本冲突:workspace.door.script和ServerScriptService.PurchaseWallScript存在重复逻辑,进一步加剧了状态混乱。

解决方案

核心思路:服务器仅处理购买验证、扣钱和数据存储,墙门的显示/碰撞状态由每个玩家的客户端独立控制,通过RemoteEvent实现服务器与客户端的状态同步。

步骤1:清理冗余脚本

删除以下重复/冲突脚本:

  • workspace.door.script
  • ServerScriptService.PurchaseWallScript
  • ServerScriptService.Script(旧重置逻辑)

步骤2:创建统一服务器逻辑脚本(ServerScriptService内新建DoorPurchaseHandler)

local DataStoreService = game:GetService("DataStoreService")
local PurchaseDataStore = DataStoreService:GetDataStore("PurchaseDataStore")
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

-- 创建状态同步RemoteEvent
local DoorStatusEvent = Instance.new("RemoteEvent")
DoorStatusEvent.Name = "DoorStatusEvent"
DoorStatusEvent.Parent = ReplicatedStorage

-- 创建购买请求RemoteEvent
local PurchaseDoorRequest = Instance.new("RemoteEvent")
PurchaseDoorRequest.Name = "PurchaseDoorRequest"
PurchaseDoorRequest.Parent = ReplicatedStorage

-- 创建重置请求RemoteEvent(仅测试用,正式版可添加权限验证)
local ResetDoorRequest = Instance.new("RemoteEvent")
ResetDoorRequest.Name = "ResetDoorRequest"
ResetDoorRequest.Parent = ReplicatedStorage

local door = workspace.Model.Model.Door
local PURCHASE_REQUIREMENT = 1000 -- 购买所需金额,可替换为door.Requirement.Value

-- 加载玩家购买状态并同步到客户端
local function loadPlayerPurchaseStatus(player)
    local key = "PurchaseStatus_" .. player.UserId
    local success, savedStatus = pcall(function()
        return PurchaseDataStore:GetAsync(key)
    end)
    
    local isPurchased = success and savedStatus == true
    DoorStatusEvent:FireClient(player, isPurchased)
end

-- 处理客户端购买请求
PurchaseDoorRequest.OnServerEvent:Connect(function(player)
    local moneyValue = player.leaderstats and player.leaderstats:FindFirstChild("Money")
    if not moneyValue then return end

    -- 检查是否已购买
    local key = "PurchaseStatus_" .. player.UserId
    local success, savedStatus = pcall(function()
        return PurchaseDataStore:GetAsync(key)
    end)
    if success and savedStatus == true then return end

    -- 验证金额并完成购买
    if moneyValue.Value >= PURCHASE_REQUIREMENT then
        moneyValue.Value -= PURCHASE_REQUIREMENT
        local saveSuccess = pcall(function()
            PurchaseDataStore:SetAsync(key, true)
        end)
        if saveSuccess then
            DoorStatusEvent:FireClient(player, true)
        end
    end
end)

-- 处理重置请求
ResetDoorRequest.OnServerEvent:Connect(function(player)
    local key = "PurchaseStatus_" .. player.UserId
    local success = pcall(function()
        PurchaseDataStore:SetAsync(key, false)
    end)
    if success then
        DoorStatusEvent:FireClient(player, false)
    end
end)

-- 玩家加入时加载状态
Players.PlayerAdded:Connect(function(player)
    player.leaderstats:WaitForChild("Money") -- 等待leaderstats加载完成
    loadPlayerPurchaseStatus(player)
end)

步骤3:创建客户端控制脚本(StarterPlayerScripts内新建DoorClientHandler)

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

local player = Players.LocalPlayer
local originalDoor = Workspace.Model.Model.Door

-- 克隆墙门,仅当前玩家可见/可交互
local localDoor = originalDoor:Clone()
localDoor.Parent = originalDoor.Parent
localDoor.Name = "LocalDoor_" .. player.UserId

-- 隐藏服务器上的原始墙门(避免全局显示)
originalDoor.Transparency = 1
originalDoor.CanCollide = false

-- 更新本地墙门状态
local function updateDoorStatus(isPurchased)
    if isPurchased then
        localDoor.Transparency = 1
        localDoor.CanCollide = false
    else
        localDoor.Transparency = 0
        localDoor.CanCollide = true
    end
end

-- 监听服务器发来的状态通知
local DoorStatusEvent = ReplicatedStorage:WaitForChild("DoorStatusEvent")
DoorStatusEvent.OnClientEvent:Connect(updateDoorStatus)

-- 触摸墙门时发起购买请求
local function onTouched(hit)
    if hit.Parent == player.Character then
        local PurchaseDoorRequest = ReplicatedStorage:WaitForChild("PurchaseDoorRequest")
        PurchaseDoorRequest:FireServer()
    end
end

localDoor.Touched:Connect(onTouched)

步骤4:修改重置按钮脚本(StarterGui内的按钮脚本)

local button = script.Parent
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ResetDoorRequest = ReplicatedStorage:WaitForChild("ResetDoorRequest")

button.MouseButton1Click:Connect(function()
    ResetDoorRequest:FireServer()
end)

关键修改说明

  • 客户端独立渲染:每个玩家操作的是自己克隆的墙门实例,服务器仅同步该玩家的购买状态,不会影响其他玩家。
  • 逻辑解耦:服务器专注于数据验证和存储,客户端负责UI/交互渲染,避免全局状态污染。
  • 消除重复逻辑:统一所有处理逻辑,避免多脚本冲突导致的状态异常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 11:42:02