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

Roblox合并ServerScriptService两主脚本实现投票后启动对战逻辑

Roblox 服务端双脚本时序冲突整合方案

问题梳理

两个独立部署在ServerScriptService下的脚本生命周期完全独立,无统一状态调度,是时序冲突的核心原因:

  • 地图投票脚本存在重复变量声明、双层嵌套死循环问题,易出现逻辑重复执行、内存泄漏
  • 对战模式脚本使用旧版Roblox API,独立运行时无法感知投票、地图加载、玩家传送的进度,易出现投票未结束就启动对局、玩家未传送就触发死亡判定的异常

整合后严格时序规则

全流程由单顶层循环统一调度,无并行逻辑抢占执行权,执行顺序固定为:

  • 脚本启动完成基础服务初始化、玩家加入监听配置
  • 单轮对局启动,优先执行地图投票全流程:从Lighting.Maps随机抽取3张候选地图、开启投票会话、统计票数选出获胜地图
  • 克隆获胜地图至Workspace,等待地图所有实例加载完成后,将所有在线玩家传送至地图出生点,校验所有玩家传送到位
  • 正式启动对战逻辑:清空玩家背包、开启死亡判定、击杀计数、存活玩家检测
  • 实时监听对局结束触发条件:对战时长耗尽 / 场上存活玩家仅剩1名,满足任意条件立即触发结算
  • 完成奖励发放、对局结果公示、玩家状态重置后,将所有玩家传送回大厅出生点,销毁临时克隆的对战地图
  • 回到投票流程起点,开启下一轮对局循环

核心实现代码

-- 服务引用
local Players = game:GetService("Players")
local Lighting = game:GetService("Lighting")
local Workspace = game:GetService("Workspace")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Teams = game:GetService("Teams")

-- 全局配置(收敛所有原脚本重复声明的变量)
local CONFIG = {
    VoteDuration = 20, -- 投票时长(秒)
    BattleDuration = 300, -- 对战时长(秒)
    LobbySpawn = Vector3.new(0, 5, 0), -- 大厅出生点坐标
    KillReward = 50, -- 单次击杀奖励
    WinReward = 200, -- 对局胜利奖励
    MinPlayersToStart = 2 -- 最低开局人数
}

-- 全局状态标记
local gameState = {
    InVote = false,
    InBattle = false,
    CurrentMap = nil,
    AlivePlayers = {}
}

-- 玩家加入初始化leaderstats
Players.PlayerAdded:Connect(function(player)
    local leaderstats = Instance.new("Folder")
    leaderstats.Name = "leaderstats"
    leaderstats.Parent = player

    local KOs = Instance.new("IntValue")
    KOs.Name = "KOs"
    KOs.Value = 0
    KOs.Parent = leaderstats

    local Wipeouts = Instance.new("IntValue")
    Wipeouts.Name = "Wipeouts"
    Wipeouts.Value = 0
    Wipeouts.Parent = leaderstats

    local Coins = Instance.new("IntValue")
    Coins.Name = "Coins"
    Coins.Value = 0
    Coins.Parent = leaderstats

    -- 对战阶段死亡判定
    player.CharacterAdded:Connect(function(char)
        local humanoid = char:WaitForChild("Humanoid")
        humanoid.Died:Connect(function()
            if not gameState.InBattle then return end
            Wipeouts.Value += 1
            -- 击杀者计数逻辑
            local creatorTag = humanoid:FindFirstChild("creator")
            if creatorTag and creatorTag.Value and creatorTag.Value:IsA("Player") then
                local killerStats = creatorTag.Value:FindFirstChild("leaderstats")
                if killerStats then
                    killerStats.KOs.Value += 1
                    killerStats.Coins.Value += CONFIG.KillReward
                end
            end
            -- 从存活列表移除
            gameState.AlivePlayers[player.UserId] = nil
        end)
    end)
end)

-- 投票流程函数
local function startMapVote()
    gameState.InVote = true
    gameState.InBattle = false
    local allMaps = Lighting.Maps:GetChildren()
    -- 随机抽3张不重复的候选地图
    local candidateMaps = {}
    local pickedIds = {}
    while #candidateMaps < math.min(3, #allMaps) do
        local randomIdx = math.random(1, #allMaps)
        if not pickedIds[randomIdx] then
            pickedIds[randomIdx] = true
            table.insert(candidateMaps, allMaps[randomIdx])
        end
        task.wait()
    end
    -- 此处可插入投票UI推送逻辑
    task.wait(CONFIG.VoteDuration)
    -- 简化版计票逻辑,可替换为实际票选统计
    local winningMap = candidateMaps[math.random(1, #candidateMaps)]
    gameState.InVote = false
    return winningMap
end

-- 地图加载与玩家传送
local function loadMapAndTeleport(mapTemplate)
    -- 清理旧地图
    if gameState.CurrentMap then
        gameState.CurrentMap:Destroy()
    end
    local newMap = mapTemplate:Clone()
    newMap.Parent = Workspace
    gameState.CurrentMap = newMap
    -- 等待地图出生点加载
    local mapSpawn = newMap:WaitForChild("SpawnPoint", 10)
    if not mapSpawn then error("地图缺少出生点配置") end
    -- 传送所有在线玩家
    for _, player in Players:GetPlayers() do
        player:LoadCharacter()
        local char = player.Character or player.CharacterAdded:Wait()
        char:PivotTo(CFrame.new(mapSpawn.Position + Vector3.new(math.random(-5,5), 3, math.random(-5,5))))
    end
    -- 等待所有玩家传送完成
    task.wait(3)
    return true
end

-- 对战流程
local function startBattle()
    gameState.InBattle = true
    -- 清空所有玩家背包
    for _, player in Players:GetPlayers() do
        player.Backpack:ClearAllChildren()
        if player.Character then
            for _, tool in player.Character:GetChildren() do
                if tool:IsA("Tool") then tool:Destroy() end
            end
        end
        -- 初始化存活列表
        gameState.AlivePlayers[player.UserId] = player
    end
    -- 对局计时+结束条件检测
    local battleTimer = 0
    local battleEnd = false
    while gameState.InBattle and not battleEnd do
        task.wait(1)
        battleTimer += 1
        -- 检测存活人数
        local aliveCount = 0
        for _ in pairs(gameState.AlivePlayers) do aliveCount +=1 end
        -- 结束条件触发
        if battleTimer >= CONFIG.BattleDuration or aliveCount <= 1 then
            battleEnd = true
            -- 结算奖励
            for userId, player in pairs(gameState.AlivePlayers) do
                local stats = player:FindFirstChild("leaderstats")
                if stats then stats.Coins.Value += CONFIG.WinReward end
            end
            -- 此处插入结果公示逻辑
        end
    end
end

-- 对局重置回大厅
local function resetToLobby()
    gameState.InBattle = false
    -- 传送所有玩家回大厅
    for _, player in Players:GetPlayers() do
        player:LoadCharacter()
        local char = player.Character or player.CharacterAdded:Wait()
        char:PivotTo(CFrame.new(CONFIG.LobbySpawn + Vector3.new(math.random(-5,5), 3, math.random(-5,5))))
        -- 重置玩家状态
        player:SetAttribute("Ready", false)
    end
    -- 销毁对战地图
    if gameState.CurrentMap then
        gameState.CurrentMap:Destroy()
        gameState.CurrentMap = nil
    end
    task.wait(5)
end

-- 顶层单循环(替换原双脚本的双层死循环,从根源控制时序)
while true do
    -- 等待满足最低开局人数
    while #Players:GetPlayers() < CONFIG.MinPlayersToStart do
        task.wait(1)
    end
    local winMap = startMapVote()
    loadMapAndTeleport(winMap)
    startBattle()
    resetToLobby()
end

关键修复点说明

  • 移除原脚本双层嵌套死循环,所有流程由顶层单while true循环串行调度,不存在并行逻辑抢占,彻底解决时序冲突
  • 所有变量统一收敛到CONFIG和gameState表中,修复原脚本重复变量声明的问题
  • 全量替换旧版API:废弃的全局wait()全部替换为task.wait(),角色加载、道具清理逻辑全部使用当前版本支持的官方API,避免兼容性问题
  • 每个流程节点添加完成校验:地图加载后等待出生点、玩家传送后预留加载时间、对局阶段实时检测存活人数和计时,不会出现跳阶段执行的问题
  • 所有事件回调增加状态位判断,比如玩家死亡逻辑仅在对战阶段生效,避免投票、大厅阶段的误触发

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 15:48:15