下载 App
分析排行榜技术
09/0451 浏览开发交流
排行榜这个坑我以前遇到过,ai也会遇到,下面贴出代码,直接让ai严格按代码配置,保准成功。
修仙榜 · 代码与配置提取
本文件将「修仙排行榜」功能相关的全部代码与配置从项目各 lua 文件中单独提取汇总,便于集中查看与维护。 原始来源:scripts/LeaderboardSystem.lua(核心系统)、scripts/main.lua(提交集成)、scripts/Views/CultivationView.lua(UI 弹窗)、scripts/GameState.lua(数据字段)。
注意:本文件为代码摘录副本,运行时仍以 scripts/ 下的源文件为准。


一、配置项总览
| 配置项 | 值 | 说明 | 来源 |
|---|---|---|---|
| 综合分数键 SCORE_KEY | "cult_rank_score" | 排行榜主排序键 | LeaderboardSystem.lua |
| 境界ID附加键 REALM_KEY | "cult_realm_id" | 展示用附加字段 | LeaderboardSystem.lua |
| 境界小阶附加键 STAGE_KEY | "cult_realm_stage" | 展示用附加字段 | LeaderboardSystem.lua |
| 提交冷却 SUBMIT_COOLDOWN | 30(秒) | 最低 30 秒提交一次,避免频繁写入 | LeaderboardSystem.lua |
| 主循环定期提交间隔 | 300(秒 = 5 分钟) | main.lua 计时器 | main.lua |
| 分数公式 | score = realm * 100000000 + spirit | realm(1-10) 主排序,spirit 次排序;上限约 1.025B,适配 32 位整数 | LeaderboardSystem.lua |
| 前三名称号 | #1 仙尊 / #2 仙帝 / #3 仙王 | 由排名返回称号 | LeaderboardSystem.lua |
| 昵称默认值 | "隐世修士" | 昵称查询失败时使用 | LeaderboardSystem.lua |
| 排行榜拉取条数 | 50(默认 topN) | GetRankList(50, ...) | CultivationView.lua |


二、核心系统源码 — scripts/LeaderboardSystem.lua
lua-- ============================================================================ -- LeaderboardSystem.lua - 官方排行榜系统 (clientCloud) -- ---------------------------------------------------------------------------- -- 职责: -- 1. 提交综合分数 (境界 × 100M + 修为) -- 2. 查询排行榜列表 (带昵称) -- 3. 查询我的排名 -- 4. 查询排行榜总人数 -- -- 分数公式: score = realm * 100000000 + spirit -- - realm (1-10) 为主排序键, 确保境界高的玩家始终排在前面 -- - spirit 为次排序键, 同境界内修为高的排在前面 -- - 最大值约 10*100M + 25M = 1.025B, 适配 32 位整数 -- -- 前三名称号: -- #1 仙尊 #2 仙帝 #3 仙王 -- ============================================================================ local LeaderboardSystem = {} -- 排行榜 iscore key local SCORE_KEY = "cult_rank_score" -- 综合分数 (主排序) local STAGE_KEY = "cult_realm_stage" -- 境界小阶 (附加字段, 用于显示) local REALM_KEY = "cult_realm_id" -- 境界ID (附加字段, 用于显示) -- 提交节流 (避免频繁写入, 最低 30 秒一次) local lastSubmitTime_ = 0 local SUBMIT_COOLDOWN = 30 -- ============================================================================ -- 1. 计算综合分数 -- ============================================================================ function LeaderboardSystem.CalculateScore() local GameState = require("scripts.GameState") local realm = GameState.data.realm or 1 local spirit = GameState.data.spirit or 0 return realm * 100000000 + spirit end -- ============================================================================ -- 2. 提交分数 -- ============================================================================ -- 提交综合分数到云端 (带节流, 默认 30 秒冷却) -- @param force boolean 强制提交 (忽略冷却) function LeaderboardSystem.SubmitScore(force) -- 节流检查 local now = os.time() if not force and (now - lastSubmitTime_) < SUBMIT_COOLDOWN then return end lastSubmitTime_ = now local GameState = require("scripts.GameState") local score = LeaderboardSystem.CalculateScore() local realm = GameState.data.realm or 1 local stage = GameState.data.realmStage or 1 -- 批量写入: 综合分数 + 境界ID + 境界小阶 clientCloud:BatchSet() :SetInt(SCORE_KEY, score) :SetInt(REALM_KEY, realm) :SetInt(STAGE_KEY, stage) :Save("提交修仙排行", { ok = function() print("[LeaderboardSystem] 提交分数成功: score=" .. score .. " realm=" .. realm .. " stage=" .. stage) end, error = function(code, reason) print("[LeaderboardSystem] 提交分数失败: code=" .. tostring(code) .. " reason=" .. tostring(reason)) end }) end -- ============================================================================ -- 3. 查询排行榜列表 -- ============================================================================ -- 获取排行榜列表 (带昵称) -- @param topN number 获取前 N 名 (默认 50) -- @param callback function 回调, 参数: leaderboard (数组), total (总人数) -- leaderboard[i] = { rank, userId, nickname, score, realm, stage, isMe } function LeaderboardSystem.GetRankList(topN, callback) topN = topN or 50 clientCloud:GetRankList(SCORE_KEY, 0, topN, { ok = function(rankList) local leaderboard = {} local userIds = {} for i, item in ipairs(rankList) do local score = item.iscore[SCORE_KEY] or 0 local realm = item.iscore[REALM_KEY] or 1 local stage = item.iscore[STAGE_KEY] or 1 leaderboard[#leaderboard + 1] = { rank = i, userId = item.userId, nickname = nil, score = score, realm = realm, stage = stage, isMe = item.userId == clientCloud.userId, } userIds[#userIds + 1] = item.userId end -- 无数据, 直接回调 if #userIds == 0 then if callback then callback(leaderboard, 0) end return end -- 查询总人数 LeaderboardSystem.GetRankTotal(function(total) -- 查询昵称 GetUserNickname({ userIds = userIds, onSuccess = function(nicknames) local map = {} for _, info in ipairs(nicknames) do map[info.userId] = info.nickname or "" end for _, entry in ipairs(leaderboard) do entry.nickname = map[entry.userId] or "隐世修士" end if callback then callback(leaderboard, total or 0) end end, onError = function(errorCode) -- 昵称查询失败, 仍返回排行榜 (昵称用默认值) for _, entry in ipairs(leaderboard) do entry.nickname = "隐世修士" end if callback then callback(leaderboard, total or 0) end end }) end) end, error = function(code, reason) print("[LeaderboardSystem] 获取排行榜失败: code=" .. tostring(code) .. " reason=" .. tostring(reason)) if callback then callback({}, 0) end end }, STAGE_KEY, REALM_KEY) -- 附加字段: 同时获取 stage 和 realm end -- ============================================================================ -- 4. 查询我的排名 -- ============================================================================ function LeaderboardSystem.GetMyRank(callback) clientCloud:GetUserRank(clientCloud.userId, SCORE_KEY, { ok = function(rank, scoreValue) if callback then callback(rank, scoreValue or 0) end end, error = function(code, reason) print("[LeaderboardSystem] 获取我的排名失败: code=" .. tostring(code) .. " reason=" .. tostring(reason)) if callback then callback(nil, 0) end end }) end -- ============================================================================ -- 5. 查询排行榜总人数 -- ============================================================================ function LeaderboardSystem.GetRankTotal(callback) clientCloud:GetRankTotal(SCORE_KEY, { ok = function(total) if callback then callback(total or 0) end end, error = function(code, reason) print("[LeaderboardSystem] 获取总人数失败: code=" .. tostring(code) .. " reason=" .. tostring(reason)) if callback then callback(0) end end }) end -- ============================================================================ -- 6. 前三名称号 -- ============================================================================ local TOP_TITLES = { [1] = "仙尊", [2] = "仙帝", [3] = "仙王", } function LeaderboardSystem.GetTitle(rank) return TOP_TITLES[rank] end -- ============================================================================ -- 7. 格式化分数为显示文本 -- ============================================================================ -- 从综合分数中解析出境界ID和修为 function LeaderboardSystem.ParseScore(score) if not score or score <= 0 then return 1, 0 end local realm = math.floor(score / 100000000) local spirit = score % 100000000 return realm, spirit end return LeaderboardSystem


三、依赖的数据字段 — scripts/GameState.lua
排行榜分数依赖以下玩家存档字段:
lua-- 修仙榜相关字段 (GameState.data) realm = 1, -- 当前境界 ID (1-10), 决定分数主排序 realmStage = 1, -- 境界小阶 (1-4), 展示用附加字段 spirit = 0, -- 修为, 决定分数次排序 (上限约 2500万)
境界名称展示通过 Config.GetRealmName(realmId, stage)(scripts/GameConfig.lua)转换。


四、主循环集成 — scripts/main.lua
1) 提交计时器声明(第 49 行)
lualocal leaderboardSubmitTimer_ = 0 -- 排行榜提交计时器
2) 初始化完成后首次提交(第 349-351 行)
lua-- 初始化完成后提交一次排行榜分数 local LeaderboardSystem = require("scripts.LeaderboardSystem") LeaderboardSystem.SubmitScore(true)
3) 小阶提升后强制提交(第 495-498 行)
lua-- 小阶提升后提交排行榜分数 local LeaderboardSystem = require("scripts.LeaderboardSystem") LeaderboardSystem.SubmitScore(true)
4) 定期提交(每 5 分钟,第 500-506 行)
lua-- 排行榜定期提交 (每 5 分钟) leaderboardSubmitTimer_ = leaderboardSubmitTimer_ + timeStep if leaderboardSubmitTimer_ >= 300 then leaderboardSubmitTimer_ = 0 local LeaderboardSystem = require("scripts.LeaderboardSystem") LeaderboardSystem.SubmitScore() end


五、UI 实现 — scripts/Views/CultivationView.lua
1) 境界区「修仙排行榜」入口按钮(第 81-92 行)
lua-- 排行榜入口 (境界名称后) UI.Button { text = "修仙排行榜", variant = "secondary", fontSize = 12, height = 28, borderRadius = 14, fontColor = C.accentGold, onClick = function(self) CultivationView.ShowLeaderboard() end, },
2) 排行榜弹窗布局(第 379-491 行)
lua-- 排行榜弹窗 UI.Panel { id = "leaderboardModal", position = "absolute", top = 0, left = 0, right = 0, bottom = 0, justifyContent = "center", alignItems = "center", backgroundColor = { 0, 0, 0, 160 }, visible = false, children = { UI.Panel { width = "92%", maxWidth = 400, maxHeight = "85%", flexDirection = "column", gap = 8, padding = 16, backgroundColor = C.bgPanel, borderRadius = 16, borderWidth = 2, borderColor = C.accentGold, children = { -- 标题栏 UI.Panel { width = "100%", flexDirection = "row", justifyContent = "space-between", alignItems = "center", children = { UI.Label { text = "修仙排行榜", fontSize = 18, fontWeight = "bold", fontColor = C.accentGold, }, UI.Button { text = "✕", variant = "secondary", fontSize = 16, width = 36, height = 36, borderRadius = 18, fontColor = C.textGray, onClick = function(self) CultivationView.HideLeaderboard() end, }, } }, -- 副标题: 按境界与修为综合排名 UI.Label { text = "综合境界与修为, 前三名获赐称号", fontSize = 11, fontColor = C.textDim, textAlign = "center", width = "100%", }, -- 排行榜列表 (ScrollView) UI.ScrollView { id = "leaderboardList", width = "100%", flexGrow = 1, flexBasis = 0, gap = 4, scrollY = true, children = { UI.Label { text = "加载中...", fontSize = 13, fontColor = C.textGray, textAlign = "center", width = "100%", marginTop = 20, }, } }, -- 我的排名 UI.Panel { id = "myRankRow", width = "100%", padding = 10, backgroundColor = C.bgDark, borderRadius = 8, borderWidth = 1, borderColor = C.accentGold, visible = false, children = { UI.Label { id = "myRankLabel", text = "", fontSize = 13, fontWeight = "bold", fontColor = C.accentGold, textAlign = "center", width = "100%", }, } }, -- 刷新按钮 UI.Button { text = "刷新排行榜", variant = "primary", fontSize = 13, height = 36, borderRadius = 18, onClick = function(self) CultivationView.RefreshLeaderboard() end, }, } } } },
3) 弹窗控制与数据刷新(第 977-1028 行)
lua-- 显示排行榜弹窗 function CultivationView.ShowLeaderboard() AudioManager.PlaySFX("click") local modal = Shared.uiRoot:FindById("leaderboardModal") if modal then modal:SetVisible(true) end CultivationView.RefreshLeaderboard() end -- 隐藏排行榜弹窗 function CultivationView.HideLeaderboard() local modal = Shared.uiRoot:FindById("leaderboardModal") if modal then modal:SetVisible(false) end end -- 刷新排行榜数据 function CultivationView.RefreshLeaderboard() local LeaderboardSystem = require("scripts.LeaderboardSystem") local C = Shared.C -- 显示加载中 local list = Shared.uiRoot:FindById("leaderboardList") local myRankRow = Shared.uiRoot:FindById("myRankRow") if list then list:ClearChildren() list:AddChild(UI.Label { text = "加载中...", fontSize = 13, fontColor = C.textGray, textAlign = "center", width = "100%", marginTop = 20, }) end if myRankRow then myRankRow:SetVisible(false) end -- 先提交一次分数 (确保自己上榜) LeaderboardSystem.SubmitScore(true) -- 拉取排行榜 LeaderboardSystem.GetRankList(50, function(leaderboard, total) CultivationView.RenderLeaderboard(leaderboard, total) end) -- 拉取我的排名 LeaderboardSystem.GetMyRank(function(rank, scoreValue) if myRankRow and rank then myRankRow:SetVisible(true) local myRankLabel = Shared.uiRoot:FindById("myRankLabel") if myRankLabel then local realmName = Config.GetRealmName(GameState.data.realm, GameState.data.realmStage) myRankLabel:SetText(string.format("我的排名: #%d %s 修为:%d", rank, realmName, GameState.data.spirit)) end end end) end
4) 列表渲染(第 1030-1122 行)
lua-- 渲染排行榜列表 function CultivationView.RenderLeaderboard(leaderboard, total) local C = Shared.C local Config = require("scripts.GameConfig") local LeaderboardSystem = require("scripts.LeaderboardSystem") local list = Shared.uiRoot:FindById("leaderboardList") if not list then return end list:ClearChildren() if #leaderboard == 0 then list:AddChild(UI.Label { text = "暂无上榜修士\n快来成为第一人!", fontSize = 14, fontColor = C.textGray, textAlign = "center", width = "100%", marginTop = 30, }) return end -- 总人数提示 list:AddChild(UI.Label { text = "共 " .. total .. " 名修士上榜", fontSize = 11, fontColor = C.textDim, textAlign = "center", width = "100%", marginBottom = 4, }) -- 前三名称号颜色 local titleColors = { [1] = { 255, 215, 0, 255 }, -- 金 [2] = { 192, 192, 192, 255 }, -- 银 [3] = { 205, 127, 50, 255 }, -- 铜 } for _, entry in ipairs(leaderboard) do local realmName = Config.GetRealmName(entry.realm, entry.stage) local title = LeaderboardSystem.GetTitle(entry.rank) local rankColor = titleColors[entry.rank] or C.textWhite local isMe = entry.isMe -- 排名 + 称号 (前三名) local rankText = "#" .. entry.rank if title then rankText = "#" .. entry.rank .. " " .. title end list:AddChild(UI.Panel { width = "100%", flexDirection = "row", alignItems = "center", padding = 8, gap = 8, backgroundColor = isMe and { 60, 50, 30, 120 } or { 30, 28, 24, 80 }, borderRadius = 8, borderWidth = isMe and 1 or 0, borderColor = isMe and C.accentGold or nil, children = { -- 排名 UI.Label { text = rankText, fontSize = 12, fontWeight = "bold", fontColor = rankColor, width = 60, flexShrink = 0, }, -- 昵称 UI.Label { text = entry.nickname or "隐世修士", fontSize = 13, fontColor = isMe and C.accentGold or C.textWhite, flexGrow = 1, flexShrink = 1, minWidth = 0, overflow = "hidden", }, -- 境界 UI.Label { text = realmName, fontSize = 11, fontColor = C.accentQing, flexShrink = 0, }, -- 修为 UI.Label { text = tostring(entry.score % 100000000), fontSize = 11, fontColor = C.textGray, flexShrink = 0, }, } }) end end


六、调用关系图(文字)
main.lua (Start/Update 主循环)
└─ LeaderboardSystem.SubmitScore(force)
├─ CalculateScore() → realm*100000000 + spirit
└─ clientCloud:BatchSet() → 写入 cult_rank_score / cult_realm_id / cult_realm_stage
CultivationView.lua
├─ 入口按钮 "修仙排行榜" → ShowLeaderboard()
├─ ShowLeaderboard() → RefreshLeaderboard()
├─ RefreshLeaderboard() → SubmitScore(true) + GetRankList(50,...) + GetMyRank(...)
└─ RenderLeaderboard() → 渲染列表 (称号/境界/修为)
LeaderboardSystem.lua (clientCloud 云榜)
├─ CalculateScore()
├─ SubmitScore(force)
├─ GetRankList(topN, cb) → GetRankTotal + GetUserNickname
├─ GetMyRank(cb) / GetRankTotal(cb)
└─ GetTitle(rank) / ParseScore(score)

提取时间:2026-07-24 | 源文件以 scripts/ 下为准












