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

如何在Roblox游戏中创建可累加的总游戏时长追踪变量?

完整实现方案:Roblox游戏停留时长追踪+累加+徽章自动发放

1. 数据存储配置(跨会话保存时长)

所有时长数据必须存在服务器端的DataStore里,客户端无法篡改。在ServerScriptService新建脚本,核心逻辑如下:

  • 用DataStoreService获取专属数据存储,命名建议带游戏ID避免冲突
  • 玩家加入时加载历史时长,玩家离开/服务器关闭时保存最新时长
local DataStoreService = game:GetService("DataStoreService")
local BadgeService = game:GetService("BadgeService")
local Players = game:GetService("Players")

-- 初始化数据存储,替换为你的游戏专属标识
local playTimeStore = DataStoreService:GetDataStore("PlayerPlayTime_YourGameID")

-- 玩家数据结构:{TotalPlayTime = 0}
local function loadPlayerData(player)
    local success, data = pcall(function()
        return playTimeStore:GetAsync("Player_" .. player.UserId)
    end)
    if success then
        return data or {TotalPlayTime = 0}
    else
        warn("加载玩家" .. player.Name .. "数据失败:" .. data)
        return {TotalPlayTime = 0}
    end
end

local function savePlayerData(player, data)
    local success, err = pcall(function()
        playTimeStore:SetAsync("Player_" .. player.UserId, data)
    end)
    if not success then
        warn("保存玩家" .. player.Name .. "数据失败:" .. err)
    end
end

2. 实时时长追踪与累加逻辑

玩家进入游戏后,记录当前会话的开始时间,每1秒更新总时长,避免用repeat wait()的低效写法,改用RunService实现定时更新:

local RunService = game:GetService("RunService")

Players.PlayerAdded:Connect(function(player)
    local playerData = loadPlayerData(player)
    local sessionStartTime = tick()
    local isTracking = true

    -- 每1秒更新一次时长
    local updateConnection = RunService.Heartbeat:Connect(function()
        if not isTracking then return end
        local sessionElapsed = tick() - sessionStartTime
        -- 累加至总时长(保留整数秒即可)
        playerData.TotalPlayTime = math.floor(playerData.TotalPlayTime + sessionElapsed)
        sessionStartTime = tick() -- 重置会话开始时间

        -- 可选:把时长同步到客户端显示
        local leaderstats = player:FindFirstChild("leaderstats") or Instance.new("Folder")
        leaderstats.Name = "leaderstats"
        leaderstats.Parent = player
        local playTimeValue = leaderstats:FindFirstChild("PlayTime") or Instance.new("IntValue")
        playTimeValue.Name = "PlayTime"
        playTimeValue.Value = playerData.TotalPlayTime
        playTimeValue.Parent = leaderstats

        -- 检查徽章发放条件
        checkAndAwardBadges(player, playerData.TotalPlayTime)
    end)

    -- 玩家离开时停止追踪并保存数据
    player.AncestryChanged:Connect(function(_, parent)
        if parent == nil then
            isTracking = false
            updateConnection:Disconnect()
            savePlayerData(player, playerData)
        end
    end)
end)

3. 徽章自动发放实现

先在Roblox开发者后台创建好徽章并获取徽章ID,然后编写发放逻辑——必须在服务器端执行,且先检查玩家是否已拥有徽章:

-- 定义徽章ID和对应时长阈值,替换为你的实际徽章ID
local badgeConfigs = {
    {BadgeId = 123456789, RequiredTime = 3600}, -- 1小时徽章
    {BadgeId = 987654321, RequiredTime = 10800} -- 3小时徽章
}

local function checkAndAwardBadges(player, totalTime)
    for _, config in ipairs(badgeConfigs) do
        -- 先检查玩家是否已拥有该徽章
        local hasBadgeResult, err = pcall(function()
            return BadgeService:UserHasBadgeAsync(player.UserId, config.BadgeId)
        end)
        if hasBadgeResult then
            -- 时长达标且未拥有徽章,发放
            if totalTime >= config.RequiredTime and not hasBadgeResult then
                local success, awardErr = pcall(function()
                    BadgeService:AwardBadge(player.UserId, config.BadgeId)
                end)
                if not success then
                    warn("给玩家" .. player.Name .. "发放徽章" .. config.BadgeId .. "失败:" .. awardErr)
                end
            end
        else
            warn("检查玩家" .. player.Name .. "徽章拥有状态失败:" .. err)
        end
    end
end

关键注意事项

  • 防作弊:所有时长计算和徽章发放必须在服务器端执行,绝不信任客户端传来的时长数据
  • DataStore容错:必须用pcall包裹DataStore操作,避免请求失败导致脚本崩溃
  • 测试建议:在Studio中开启DataStore模拟器(设置>游戏设置>安全>允许DataStore),避免测试时消耗真实DataStore配额
  • 性能优化:也可以改为玩家离开时一次性计算会话总时长(用tick()记录进入和离开的时间差),减少DataStore请求次数

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 09:02:36