Unity创建排行榜触发ArgumentNullException,求解决方法
Unity排行榜加载时ArgumentNullException错误排查与解决
错误核心原因
报错ArgumentNullException: Value cannot be null. Parameter name: source的直接原因是**loadedUserData.ListPlayer为null**,导致Linq排序操作没有可操作的数据源。
具体问题点与修复方案
1. 序列化/反序列化工具不匹配
你保存数据用的是Newtonsoft的JsonSerializer,但加载时用的是Unity自带的JsonUtility,两者序列化格式不兼容,导致反序列化后ListPlayer字段为null。
修复:统一使用Newtonsoft.Json进行序列化和反序列化:
- 确保Unity已导入Newtonsoft.Json包(Package Manager中搜索
Newtonsoft Json安装) - 同步修改保存和加载逻辑,用
JsonConvert替代原有实现
2. ListPlayer未初始化
ListPlayerModel中的ListPlayer字段没有默认初始化,即使序列化正常,空对象也会导致后续操作报错。
修复:给ListPlayerModel添加构造函数初始化列表:
public class UserModel : IDataModel { public int Score; public string Day; public string Name; } [Serializable] public class ListPlayerModel : IDataModel { public List<UserModel> ListPlayer; public ListPlayerModel() { ListPlayer = new List<UserModel>(); } }
3. 保存逻辑优化
将原SaveDataToFile改为更简洁的Newtonsoft实现:
public void SaveDataToFile() { string json = JsonConvert.SerializeObject(this.Model); string filePath = Application.persistentDataPath + "/dataUser.json"; File.WriteAllText(filePath, json); Debug.Log("Save success"); }
4. 加载逻辑彻底修复
原加载逻辑存在3个严重问题:嵌套循环重复实例化、错误访问字符串索引(std.Name[i]取的是字符而非完整名称)、未处理文件不存在的情况。修复后的代码:
public void LoadDataToFile() { string filePath = Application.persistentDataPath + "/dataUser.json"; ListPlayerModel loadedUserData; // 处理文件不存在的情况 if (!File.Exists(filePath)) { loadedUserData = new ListPlayerModel(); File.WriteAllText(filePath, JsonConvert.SerializeObject(loadedUserData)); Debug.Log("No data file found, created empty one"); return; } // 用Newtonsoft反序列化,匹配保存逻辑 string outputJson = File.ReadAllText(filePath); loadedUserData = JsonConvert.DeserializeObject<ListPlayerModel>(outputJson); // 双重保险:确保列表不为null if (loadedUserData.ListPlayer == null) { loadedUserData.ListPlayer = new List<UserModel>(); } // 按分数降序排序,取前3名 var topPlayers = loadedUserData.ListPlayer.OrderByDescending(p => p.Score).Take(3).ToList(); // 实例化每个排行榜项 for (int i = 0; i < topPlayers.Count; i++) { var currentPlayer = topPlayers[i]; // 实例化预制体并设置父物体(根据你的UI容器调整) GameObject newRankItem = UnityEngine.Object.Instantiate(ListPlayer, transform); // 调整位置(示例为垂直排列,每个项间隔100像素,可按需修改) RectTransform itemRect = newRankItem.GetComponent<RectTransform>(); itemRect.anchoredPosition = new Vector2(0, -i * 100); // 找到预制体内的文本组件并赋值 Text nameText = newRankItem.transform.Find("ListPlayerName").GetComponent<Text>(); Text scoreText = newRankItem.transform.Find("ListPlayerScore").GetComponent<Text>(); Text dayText = newRankItem.transform.Find("ListPlayerDay").GetComponent<Text>(); nameText.text = currentPlayer.Name; scoreText.text = currentPlayer.Score.ToString(); dayText.text = currentPlayer.Day; } }
5. 额外注意事项
- 确保排行榜预制体
ListPlayer内存在名为ListPlayerName、ListPlayerScore、ListPlayerDay的Text组件 - 排序用
OrderByDescending才能取最高分,原代码的OrderBy是升序,会获取最低分 - 实例化时务必设置父物体,避免UI元素位置错乱
- 检查
UserView中的SaveUserData方法,确保this.Model.ListPlayer已初始化(通过ListPlayerModel的构造函数已解决)
内容的提问来源于stack exchange,提问作者Ngọc Phát
相关产品推荐
相关产品推荐

