RankManager.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  1. using UnityEngine;
  2. using UnityEngine.UI;
  3. using System.Collections.Generic;
  4. using System.Collections;
  5. using UnityEngine.Networking;
  6. using Newtonsoft.Json.Linq;
  7. namespace LocalRank
  8. {
  9. [RequireComponent(typeof(RankUpAnimator))]
  10. public class RankManager : MonoBehaviour
  11. {
  12. public Transform content; // Content节点
  13. public GameObject itemPrefab; // 排行榜Item预制体
  14. public InputField inputField;
  15. private RankUpAnimator rankUpAnimator; // RankUpAnimator脚本引用
  16. private List<RankItemData> rankDataList = new List<RankItemData>();
  17. private int userId;
  18. private void Awake()
  19. {
  20. rankUpAnimator = GetComponent<RankUpAnimator>();
  21. if (rankUpAnimator == null)
  22. {
  23. Debug.LogError("RankUpAnimator 组件未找到!");
  24. }
  25. if (inputField != null)
  26. {
  27. inputField.onEndEdit.AddListener(InputScore);
  28. }
  29. }
  30. private void Start()
  31. {
  32. rankUpAnimator.onAnimationStart = () =>
  33. {
  34. HideAllRankIcons(); // 滚动前隐藏图标和名次数字
  35. };
  36. rankUpAnimator.onAnimationComplete = () =>
  37. {
  38. RefreshRankIcons(); // 滚动后恢复图标或数字
  39. };
  40. StartSync();
  41. CreateUserId();
  42. }
  43. // 你可以在这里定义游戏 ID 或者动态获取
  44. private int gameId = 1;
  45. /// <summary>
  46. /// 创建本地排行榜,进入场景时候直接创建。比如游戏场景
  47. /// 不同的游戏ID对应不同的排行榜
  48. /// </summary>
  49. private void StartSync(int CurrentGameId = 1)
  50. {
  51. gameId = CurrentGameId;
  52. // 判断是否已创建表
  53. if (RankService.IsTableExists(gameId))
  54. {
  55. Debug.Log($"rank_game_{gameId} 表已存在!");
  56. }
  57. else
  58. {
  59. Debug.Log($"rank_game_{gameId} 表不存在,正在创建...");
  60. RankService.CreateTableIfNotExists(gameId);
  61. }
  62. }
  63. /// <summary>
  64. /// 创建一个userId,每个userId都是一条排行榜数据。
  65. /// 后续上传分数时候使用
  66. /// </summary>
  67. private void CreateUserId()
  68. {
  69. //获取一个时间戳值
  70. userId = RankService.GenerateLocalUserId();
  71. }
  72. /// <summary>
  73. /// 通过Input End 上传分数
  74. /// </summary>
  75. /// <param name="score"></param>
  76. void InputScore(string score)
  77. {
  78. int parsedScore;
  79. if (!int.TryParse(score, out parsedScore))
  80. {
  81. Debug.LogError("输入的分数无效!");
  82. return;
  83. }
  84. RankService.UploadScore(gameId, userId, "Player_" + userId, "", parsedScore);
  85. Debug.Log($"InputScore rank_game_{gameId} 上传 Id:{userId}分数!");
  86. GetLocalRankList();
  87. }
  88. /// <summary>
  89. /// 上传一个随机分数
  90. /// </summary>
  91. public void UploadScore()
  92. {
  93. int seed = 0;
  94. var random = seed == 0 ? new System.Random() : new System.Random(seed);
  95. int score = random.Next(0, 1000); // 0~999 分之间
  96. RankService.UploadScore(gameId, userId, "Player_" + userId, "", score);
  97. Debug.Log($"UploadScore rank_game_{gameId} 上传 Id:{userId}分数!");
  98. GetLocalRankList();
  99. }
  100. /// <summary>
  101. /// 上传一个新用户id分数
  102. /// </summary>
  103. public void UploadNewIdScore()
  104. {
  105. int newUserId = RankService.GenerateLocalUserId();
  106. int seed = 0;
  107. var random = seed == 0 ? new System.Random() : new System.Random(seed);
  108. int score = random.Next(0, 1000); // 0~999 分之间
  109. RankService.UploadScore(gameId, newUserId, "Player_" + newUserId, "", score);
  110. Debug.Log($"UploadNewIdScore rank_game_{gameId} 上传 新Id:{newUserId}分数!");
  111. GetLocalRankList();
  112. }
  113. #region 测试数据操作
  114. /// <summary>
  115. /// 测试数据调用
  116. /// </summary>
  117. public void TestData()
  118. {
  119. InsertTestData();
  120. }
  121. /// <summary>
  122. /// 按钮调用,重新填充排行榜数据
  123. /// </summary>
  124. // public void AnimatorRunButton()
  125. // {
  126. // PopulateRankList(true); // 重新填充排行榜数据
  127. // }
  128. /// <summary>
  129. /// 插入测试数据
  130. /// </summary>
  131. /// <param name="seed"></param>
  132. private void InsertTestData(int seed = 0)
  133. {
  134. var random = seed == 0 ? new System.Random() : new System.Random(seed);
  135. for (int i = 1; i <= 20; i++)
  136. {
  137. int userId = 1000 + i;
  138. string nickname = $"玩家{i:D2}";
  139. string avatarUrl = ""; // 可以填测试图片链接或留空
  140. int score = random.Next(0, 10000); // 0~9999 分之间
  141. RankService.UploadScore(gameId, userId, nickname, avatarUrl, score);
  142. }
  143. Debug.Log($"已为 gameId={gameId} 插入 20 条测试数据");
  144. }
  145. #endregion
  146. /// <summary>
  147. /// 获取本地排行榜
  148. /// </summary>
  149. void GetLocalRankList()
  150. {
  151. rankDataList.Clear();
  152. RankItemData selfItem = null;
  153. const int MaxRankDisplayCount = 9; // 展示排行榜上限,最后一位预留给自己
  154. int maxRank = 0;//记录最大的max
  155. int index = 0;//当前下标
  156. var (ranks, selfRank, total) = RankService.GetRankList(gameId: gameId, userId: userId);
  157. foreach (var r in ranks)
  158. {
  159. Debug.Log($"[{r.Rank}] {r.Nickname} - {r.Score} {(r.IsSelf ? "(你)" : "")}");
  160. var data = new RankItemData
  161. {
  162. Rank = r.Rank,
  163. UserName = r.Nickname,
  164. Score = r.Score,
  165. AvatarUrl = r.AvatarUrl,
  166. IsSelf = r.IsSelf,
  167. RankIndex = index++
  168. };
  169. // Debug.Log($"成功解析data {data} 排行榜数据");
  170. if (data.IsSelf)
  171. selfItem = data;
  172. else
  173. rankDataList.Add(data);
  174. maxRank = Mathf.Max(maxRank, r.Rank);//记录最大值
  175. }
  176. //Debug.Log($"成功解析 {rankDataList.Count} 条排行榜数据");
  177. // 如果总数量不足 MaxRankDisplayCount,补充假数据
  178. int currentCount = rankDataList.Count;
  179. int otherIndex = 0;
  180. for (int i = currentCount; i < MaxRankDisplayCount; i++)
  181. {
  182. ++otherIndex;
  183. var fakeData = new RankItemData
  184. {
  185. Rank = maxRank + otherIndex,
  186. UserName = $"游客玩家 {i + 1}",
  187. Score = 0,
  188. AvatarUrl = "", // 可替换为默认头像路径
  189. IsSelf = false,
  190. RankIndex = index++
  191. };
  192. rankDataList.Add(fakeData);
  193. }
  194. // 最后添加自己(先放在最后一位)
  195. if (selfItem != null)
  196. {
  197. rankDataList.Add(selfItem);
  198. }
  199. // Debug.Log($"最终排行榜数据条数: {rankDataList.Count}");
  200. PopulateRankList(true);
  201. }
  202. /// <summary>
  203. /// 获取远程排行榜数据
  204. /// 暂时不使用
  205. /// </summary>
  206. /// <returns></returns>
  207. private IEnumerator FetchRankDataFromServer()
  208. {
  209. string url = "http://192.168.0.108:11432/SmartBowBusinessServer/SmartBowSDK/UploadScoreAndGetSeasonCenterRankList" +
  210. "?token=3421-1746758375311-5786b88c34343d142bea1dd31a1cb315" +
  211. "&rankArea=2&rankGameType=1001&score=9937" +
  212. "&countryCode=中国&stateCode=广东省&cityCode=清远市";
  213. UnityWebRequest request = UnityWebRequest.Post(url, "");
  214. yield return request.SendWebRequest();
  215. if (request.result != UnityWebRequest.Result.Success)
  216. {
  217. Debug.LogError("Request Failed: " + request.error);
  218. yield break;
  219. }
  220. string json = request.downloadHandler.text;
  221. ParseRankJson(json);
  222. }
  223. /// <summary>
  224. /// 解析远程排行榜数据
  225. /// </summary>
  226. /// <param name="json"></param>
  227. private void ParseRankJson(string json)
  228. {
  229. rankDataList.Clear();
  230. JObject root = JObject.Parse(json);
  231. JArray rankArray = (JArray)root["data"][0];
  232. RankItemData selfItem = null;
  233. int index = 0;
  234. foreach (JObject item in rankArray)
  235. {
  236. var data = new RankItemData
  237. {
  238. Rank = item["rankNum"] != null ? (int)item["rankNum"] : 0,
  239. UserName = item["nickname"]?.ToString() ?? "Unknown",
  240. Score = item["score"] != null ? (int)item["score"] : 0,
  241. AvatarUrl = item["avatarUrl"]?.ToString() ?? "",
  242. IsSelf = item["isSelf"]?.ToObject<bool>() ?? false,
  243. RankIndex = index++
  244. };
  245. Debug.Log($"成功解析data {data} 排行榜数据");
  246. if (data.IsSelf)
  247. selfItem = data;
  248. else
  249. rankDataList.Add(data);
  250. }
  251. // 最后添加自己(先放在最后一位)
  252. if (selfItem != null)
  253. {
  254. rankDataList.Add(selfItem);
  255. }
  256. Debug.Log($"成功解析 {rankDataList.Count} 条排行榜数据");
  257. PopulateRankList();
  258. }
  259. /// <summary>
  260. /// 开始跑循环分数时候调用
  261. /// </summary>
  262. public void HideAllRankIcons()
  263. {
  264. for (int i = 0; i < content.childCount; i++)
  265. {
  266. var item = content.GetChild(i);
  267. var itemUI = item.GetComponent<RankItemUI>();
  268. if (itemUI != null)
  269. {
  270. //itemUI.HideIconAndText();
  271. itemUI.StartRunUI();
  272. }
  273. }
  274. }
  275. /// <summary>
  276. /// 结束循环分数时候调用
  277. /// </summary>
  278. public void RefreshRankIcons()
  279. {
  280. for (int i = 0; i < content.childCount; i++)
  281. {
  282. var item = content.GetChild(i);
  283. var itemUI = item.GetComponent<RankItemUI>();
  284. if (itemUI != null)
  285. {
  286. itemUI.StopRunUI();
  287. }
  288. }
  289. }
  290. /// <summary>
  291. /// 填充排行榜列表,生成排行榜预制item
  292. /// </summary>
  293. /// <param name="isStartup"></param>
  294. private void PopulateRankList(bool isStartup = false)
  295. {
  296. foreach (Transform child in content)
  297. Destroy(child.gameObject);
  298. for (int i = 0; i < rankDataList.Count; i++)
  299. {
  300. var data = rankDataList[i];
  301. GameObject itemObj = Instantiate(itemPrefab, content);
  302. itemObj.name = $"RankItem_{data.Rank}";
  303. RankItemUI itemUI = itemObj.GetComponent<RankItemUI>();
  304. if (itemUI != null)
  305. {
  306. itemUI.SetData(data);
  307. if (!string.IsNullOrEmpty(data.AvatarUrl))
  308. StartCoroutine(LoadAvatarImage(data.AvatarUrl, itemUI.avatarImage));
  309. }
  310. else
  311. {
  312. Debug.LogWarning("预制体上缺少 RankItemUI 脚本");
  313. }
  314. }
  315. if (isStartup)
  316. StartCoroutine(DelayedStartAnimation());
  317. }
  318. /// <summary>
  319. /// 加载头像图片
  320. /// </summary>
  321. /// <param name="url"></param>
  322. /// <param name="targetImage"></param>
  323. /// <returns></returns>
  324. private IEnumerator LoadAvatarImage(string url, Image targetImage)
  325. {
  326. UnityWebRequest request = UnityWebRequestTexture.GetTexture(url);
  327. yield return request.SendWebRequest();
  328. if (request.result != UnityWebRequest.Result.Success)
  329. {
  330. Debug.LogWarning("头像加载失败: " + request.error);
  331. yield break;
  332. }
  333. Texture2D texture = DownloadHandlerTexture.GetContent(request);
  334. Sprite avatarSprite = Sprite.Create(texture,
  335. new Rect(0, 0, texture.width, texture.height),
  336. new Vector2(0.5f, 0.5f));
  337. targetImage.sprite = avatarSprite;
  338. }
  339. /// <summary>
  340. /// 延迟开始动画
  341. /// </summary>
  342. /// <returns></returns>
  343. IEnumerator DelayedStartAnimation()
  344. {
  345. yield return null;
  346. // TestRankUpFromTo(10, 1);
  347. //查找“自己”的索引和目标真实排名索引
  348. int startIndex = rankDataList.Count;
  349. RankItemData selfData = rankDataList.Find(d => d.IsSelf);
  350. int targetIndex = selfData.RankIndex + 1; // 目标名次是自己在列表中的索引 + 1
  351. // for (int i = 0; i < rankDataList.Count; i++)
  352. // {
  353. // var data = rankDataList[i];
  354. // Debug.Log($"[{data.Rank}] {data.UserName} - {data.Score} {(data.IsSelf ? "(你)" : "")} ,RankIndex: {data.RankIndex}");
  355. // }
  356. // Debug.Log($"startIndex:{startIndex} targetIndex:{targetIndex}");
  357. if (startIndex != targetIndex)
  358. {
  359. RankUpFromTo(startIndex, targetIndex);
  360. }
  361. }
  362. /// <summary>
  363. /// 测试冲榜动画:从 startIndex 冲到 targetRankIndex
  364. /// </summary>
  365. void RankUpFromTo(int startIndex, int targetRankIndex)
  366. {
  367. if (startIndex <= 0 || startIndex > content.childCount)
  368. {
  369. Debug.LogError("无效的起始排名!");
  370. return;
  371. }
  372. if (targetRankIndex <= 0 || targetRankIndex > content.childCount)
  373. {
  374. Debug.LogError("无效的目标排名!");
  375. return;
  376. }
  377. // 获取起始 item
  378. Transform startItem = content.GetChild(startIndex - 1);
  379. // 设置 animator 的目标元素和目标名次
  380. rankUpAnimator.targetItem = startItem.GetComponent<RectTransform>();
  381. rankUpAnimator.targetRankIndex = targetRankIndex;
  382. // 调用冲榜动画
  383. rankUpAnimator.StartRankUpAnimation(startIndex, targetRankIndex);
  384. }
  385. }
  386. }