云变量和排行榜怎么接入 —— 从存分数到显示排行榜的完整指南

05/0967 浏览开发心得
做好了一个游戏,玩家打了高分,退出再进来就没了——这体验太差了。
更让人郁闷的是:玩家辛辛苦苦打了 9999 分,却没法看到自己在全服排第几。
云变量和排行榜就是解决这两个问题的:
云变量:把数据存到云端,下次打开游戏还在。金币、分数、等级、装备配置……什么都能存。
排行榜:基于云端数据自动排序,玩家可以看到自己在全服的排名。
引擎已经把最难的部分(网络通信、数据持久化、排序算法)全做好了。你只需要调几个 API,告诉嗒啦啦:"帮我存一下分数"、"帮我拉一下排行榜"。
难度:入门级,会写基础游戏代码就行
————————————————

第一章:先搞懂两个核心概念

云变量有两个"抽屉"来存数据:
【values —— 存"任意类型"数据】
用 Set() 方法写入。可以存字符串、表、布尔值……什么类型都行。
适合存游戏配置、装备列表、存档数据。
但是 values 里的数据不能排行榜排序。
【iscores —— 存"整数"数据】
用 SetInt() 或 Add() 方法写入。只能存整数。
适合存分数、金币、等级、击杀数。
重点:iscores 里的数据可以排行榜排序。
一句话记住:要排行榜 → 用 SetInt/Add 存到 iscores;不需要排行榜 → 用 Set 存到 values。
读的时候,回调函数会同时给你两个参数:第一个是 values,第二个是 iscores。
————————————————

第二章:存一个分数(最简单的用法)

游戏结束时把分数存到云端,用 clientCloud:SetInt() 方法。第一个参数是 key 名字(比如 "high_score"),第二个参数是分数值,第三个参数是回调。回调里 ok 表示成功,error 表示失败。
读出来用 clientCloud:Get() 方法。Get 的回调函数有两个参数,第一个是 values,第二个是 iscores。用 SetInt 写进去的数据在 iscores 里(第二个参数),用 Set 写的在 values 里(第一个参数)。
比如存分数:clientCloud:SetInt("high_score", 9999, { ok = function() ... end })
读分数:clientCloud:Get("high_score", { ok = function(values, iscores) local score = iscores.high_score or 0 end })
注意 iscores.high_score 后面加 or 0 做兜底,防止没存过的时候返回 nil。
————————————————

第三章:整数增量 —— 加金币、加经验

有些数据不是"覆盖"式的,而是"累加"式的。比如金币每局打完加 100,用 clientCloud:Add()。
clientCloud:Add("gold", 100) 加 100 金币,clientCloud:Add("gold", -50) 扣 50 金币(买道具)。
Add 是原子操作——多个请求同时到达也不会出错。
记住:设置固定值用 SetInt,累加用 Add。
————————————————

第四章:存复杂数据 —— 配置、装备、存档

存表、字符串等复杂数据用 clientCloud:Set()。比如 clientCloud:Set("game_config", { difficulty = "hard", music = true, sfx = true }),整个表结构会完整保存到云端。
读出来的时候,表结构会完整保留。在回调的第一个参数 values 里取:local config = values.game_config,然后就可以直接访问 config.difficulty、config.music 这些字段。
————————————————

第五章:批量操作 —— 一次存多个、读多个

一个一个调 API 太慢,用批量操作一次搞定。
【批量写入】
用 clientCloud:BatchSet() 开始,然后链式调用 :Set()、:SetInt()、:Add(),最后调 :Save() 提交。一次请求可以同时存任意类型数据、设整数、加整数。Save 的第一个参数是备注名(随便写),第二个参数是回调。
【批量读取】
用 clientCloud:BatchGet() 开始,链式调用 :Key("gold"):Key("exp"):Key("level") 列出要读的 key,最后调 :Fetch() 拉取。回调里一次拿到所有数据。
推荐:游戏启动时用 BatchGet 一次性加载,游戏结束时用 BatchSet 一次性保存。
————————————————

第六章:排行榜 —— 让玩家看到自己排第几

只要你用 SetInt 或 Add 存了整数数据,就可以直接拉排行榜——不需要额外配置。
【拉取排行榜前 10 名】
用 clientCloud:GetRankList("high_score", 0, 10, { ok = function(rankList) ... end })。
参数依次是:排序用的 key 名、起始位置(0 表示第一名)、取多少人、回调。
回调里的 rankList 是一个数组,用 ipairs 遍历。每个 item 里:
item.userId 是玩家 ID。
item.iscore.high_score 是该玩家的分数(SetInt/Add 存的整数数据)。
item.userId == clientCloud.userId 可以判断是不是自己。
默认降序(分数高的排前面)。想升序排(比如用时最短排前面),在回调参数前面加一个 true:clientCloud:GetRankList("best_time", 0, 10, true, { ok = function(rankList) ... end })。
【查我自己排第几】
用 clientCloud:GetUserRank(clientCloud.userId, "high_score", { ok = function(rank, scoreValue) ... end })。rank 是排名数字,nil 表示还没上榜。
【查总共多少人上榜】
用 clientCloud:GetRankTotal("high_score", { ok = function(total) ... end })。
【附加字段:排行榜里同时显示多个数据】
如果排行榜想同时显示分数和游戏场次,可以在 GetRankList 的回调参数后面追加 key 名。比如 clientCloud:GetRankList("high_score", 0, 10, { ok = function(rankList) ... end }, "play_count"),最后的 "play_count" 就是附加字段。
拿到之后用 item.iscore.play_count 读取。附加字段可以写多个,逗号隔开就行。
————————————————

第七章:排行榜显示昵称 —— 两种方案按需选

排行榜拉出来只有 userId,没有昵称。根据游戏类型不同,有两种方案:
方案 A:TapTap 平台昵称。适合休闲游戏、不需要改名的场景。昵称来自 TapTap 账号系统,零成本,不用自己存。
方案 B:游戏内自定义昵称。适合 RPG、社交游戏、需要改名的场景。昵称存到云变量里,玩家自由取名。
一句话判断:玩家需不需要自己起名字?不需要 → 方案 A;需要 → 方案 B。
—— 方案 A:使用 TapTap 平台昵称(推荐大多数游戏)——
玩家注册 TapTap 时已经有昵称了,用全局函数 GetUserNickname() 查询。
流程是这样的:先拉排行榜拿到 rankList,收集所有 item.userId 放到一个数组里。然后调用 GetUserNickname({ userIds = 那个数组, onSuccess = function(nicknames) ... end })。返回的 nicknames 数组里每个元素有 userId 和 nickname 两个字段。把它们做成一个 userId → nickname 的映射表,最后遍历排行榜的时候查这个映射表就能显示昵称了。
需要两次异步调用(先拉排行榜,再查昵称),但不需要自己存任何数据。
—— 方案 B:使用游戏内自定义昵称 ——
如果你的游戏有"输入昵称"环节(RPG 开局取名等),昵称需要自己存到云变量里。
第一步,玩家起名时存到云变量:clientCloud:Set("nickname", nickname)。
第二步,排行榜里通过附加字段直接带出来。调用 GetRankList 时在最后加上 "nickname":clientCloud:GetRankList("high_score", 0, 10, { ok = function(rankList) ... end }, "nickname")。
拿到之后用 item.score.nickname 读取(注意是 item.score 不是 item.iscore,因为 Set 存的数据在 score 里)。
方案 B 的好处:一次请求搞定排行榜加昵称,不用嵌套回调。
记得游戏启动时用 clientCloud:Get("nickname") 检查玩家有没有设过昵称,没设过就弹出取名界面。
两种方案也可以混用:排行榜附加字段先取自定义昵称,对于没取名的玩家再用 GetUserNickname() 查平台昵称兜底。跟嗒啦啦说"帮我做排行榜,优先显示游戏内自定义昵称,没有的回退到 TapTap 平台昵称"就行。
————————————————

第八章:一个完整的实战流程

把前面的串起来,做一个"启动加载 → 结算保存 → 排行榜"流程:
【启动加载】游戏启动时用 BatchGet 一次性读取 gold、high_score、play_count 三个 key。在回调里把数据存到本地变量,iscores 里取整数数据,values 里取配置数据。
【结算保存】游戏结束时用 BatchSet 一次性保存:Add("gold", 本局金币) 累加金币,Add("play_count", 1) 累加场次,SetInt("last_score", 本局分数) 覆盖最后一次分数。Save 之后在回调里再检查是否需要更新最高分。
【更新最高分】先 Get("high_score") 读出当前最高分,如果新分数更高就 SetInt("high_score", 新分数) 覆盖。
【显示排行榜】GetRankList 拉前 10 名,附加字段带上 play_count。拿到后收集所有 userId,调 GetUserNickname 查平台昵称。两个回调嵌套完成后,把排名、昵称、分数、场次拼在一起显示。
————————————————

第九章:回调结构和错误处理

所有 API 都用同样的回调结构:ok 表示成功,error 表示失败(有错误码和原因),timeout 表示超时(可选)。
建议至少加上 error,不然出了问题你都不知道。
常见错误码:-429 表示请求太频繁,超过限频(读写各 300 次/分钟)。
————————————————

常见坑

坑 1:用 Set 存分数,发现不能排行榜
排行榜只能基于 iscores 排序。要排行榜 → 必须用 SetInt() 或 Add()。
坑 2:昵称方案没选对
不需要自定义昵称 → 直接用 GetUserNickname() 查平台昵称。需要自定义昵称 → 用 clientCloud:Set("nickname", name) 存云变量,排行榜附加字段取出来。见第七章。
坑 3:回调里 values 和 iscores 搞反
Set() 存的 → values(第一个参数),SetInt()/Add() 存的 → iscores(第二个参数)。
坑 4:Get 只返回你请求的那个 key
想同时读多个 key → 用 BatchGet。
坑 5:排行榜 item 里找不到昵称
排行榜不返回昵称,需要额外查询。见第七章。
坑 6:Add 传了小数
Add 只能传整数。需要存小数用 Set(),但不能排行榜。
坑 7:每帧都读写导致限频
读写各 300 次/分钟。正确做法:开始时读一次,结束时写一次,中间在内存处理。
————————————————

提示词·直接复制给嗒啦啦

【模板 1:给游戏加云端存档】
「帮我给游戏加上云端存档功能:游戏启动时用 BatchGet 加载玩家数据(金币、最高分、游戏场次),游戏结束时用 BatchSet 批量保存(加金币、加场次、存本局分数),如果本局分数超过最高分就更新。加上 error 回调处理失败。」
【模板 2:加排行榜(平台昵称)】
「帮我做一个排行榜:显示 high_score 前 10 名,每行显示排名、玩家昵称、分数,高亮自己那一行,底部显示我的排名和总人数。昵称用 GetUserNickname 查询 TapTap 平台昵称。用 UI 组件做界面。」
【模板 3:加排行榜(自定义昵称)】
「帮我做排行榜,游戏支持自定义昵称。玩家首次进入弹出取名界面,昵称用 clientCloud:Set("nickname", name) 存云端。排行榜用附加字段同时获取 nickname 直接显示。」
————————————————

总结

云变量和排行榜的核心就三件事:
第一,搞清楚存哪儿。
数字想排行榜 → SetInt/Add 存到 iscores。复杂数据不排行榜 → Set 存到 values。
第二,批量操作更高效。
游戏开始用 BatchGet 一次性加载,游戏结束用 BatchSet 一次性保存。不要一个一个调。
第三,昵称看需求选方案。
不需要改名 → GetUserNickname() 查平台昵称,零成本。需要改名 → 云变量自己存,排行榜附加字段带出来。
最后一个建议:不要每帧读写云变量。开始时读一次,结束时写一次,中间在内存里处理。
云变量和排行榜虽然简单,但对玩家体验的提升是巨大的。有了云存档不怕数据丢失,有了排行榜有了竞争动力。试试看吧。
4
5