Unity C#:如何访问Scriptable Object中的List及子List数据
解决Scriptable Object嵌套List数据访问问题
问题根源分析
你遇到的核心问题来自三个方面:
- 冗余的Scriptable Object引用:MenuManager中错误声明了
currentCategory和currentRound两个独立的CurrentGameData引用,这两个字段应该是currentGameData实例下的属性,而非单独的对象。 - 不安全的列表访问:直接通过索引
[1]访问列表元素,未判断列表是否为空或索引是否越界,容易触发空引用或索引越界异常。 - 未初始化/赋值的数据:如果
currentGameData未在Inspector中赋值,或者teamRoundData列表没有预先添加元素,访问时必然报错。
分步修复方案
1. 修正MenuManager的引用错误
删除冗余的currentCategory和currentRound字段,统一通过currentGameData访问所有属性:
public class MenuManager : MonoBehaviour { public GameObject categoryDropdownGO; public GameObject primaryDropdownGO; public GameObject secondaryDropdownGO; public GameObject openDropDownGO; [SerializeField] private CurrentGameData currentGameData; // 仅保留这一个核心引用 void Awake() { var catDD = categoryDropdownGO.GetComponent<TMP_Dropdown>(); var rndDD = primaryDropdownGO.GetComponent<TMP_Dropdown>(); // 修正之前重复获取同一下拉的错误 // 安全访问列表数据 if (currentGameData != null && currentGameData.teamRoundData != null && currentGameData.teamRoundData.Count > 1) { Debug.Log(currentGameData.teamRoundData[1].TeamName); } else { Debug.LogWarning("currentGameData未赋值,或teamRoundData为空/元素数量不足"); } if (currentGameData != null && currentGameData.currentCategory == "primary") { CategoryMenuSwitch(1); catDD.value = 1; catDD.RefreshShownValue(); } } private void CategoryMenuSwitch(int index) { // 你的菜单切换逻辑实现 } }
2. 确保List数据的初始化与赋值
方式一:Inspector手动初始化
在Unity编辑器中,创建并选中你的CurrentGameData实例,在Inspector面板中:
- 点击
teamRoundData旁的+按钮添加TeamDataContainer元素 - 对每个
TeamDataContainer,点击roundDataContainer旁的+按钮添加RoundDataContainer元素 - 填写对应的队伍和轮次数据
方式二:代码自动初始化(可选,用于测试)
在CurrentGameData中添加OnEnable方法,自动生成测试数据(仅在编辑器环境生效):
[CreateAssetMenu] public class CurrentGameData : ScriptableObject { // 原有字段和嵌套类保持不变 [SerializeField] public List<TeamDataContainer> teamRoundData = new(); private void OnEnable() { if (teamRoundData == null) { teamRoundData = new List<TeamDataContainer>(); } #if UNITY_EDITOR // 编辑器环境下自动添加测试数据 if (teamRoundData.Count == 0) { var testTeam = new TeamDataContainer { TeamName = "测试队伍1", TeamID = 1, TeamSchool = "测试学校", TeamCatID = "primary" }; testTeam.roundDataContainer.Add(new RoundDataContainer { RoundID = "Round1", GrossScore = 100, Touches = 0, Time = 60, NetScore = 100 }); teamRoundData.Add(testTeam); } #endif } }
3. 安全访问嵌套List数据
无论访问外层的teamRoundData还是内层的roundDataContainer,都要先做非空和长度判断:
// 示例:访问第一个队伍的最新轮次数据 if (currentGameData != null && currentGameData.teamRoundData.Count > 0) { var firstTeam = currentGameData.teamRoundData[0]; if (firstTeam.roundDataContainer.Count > 0) { var latestRound = firstTeam.roundDataContainer.Last(); Debug.Log($"队伍[{firstTeam.TeamName}]最新轮次净分:{latestRound.NetScore}"); } }
轮次数据传递与排行榜更新示例
从轮次脚本提交分数到CurrentGameData
在裁判打分的轮次脚本中,通过CurrentGameData更新对应队伍的轮次数据:
public class RoundScoreManager : MonoBehaviour { [SerializeField] private CurrentGameData currentGameData; public int targetTeamID; // 当前打分的队伍ID public string targetRoundID; // 当前轮次ID // 裁判完成打分后调用此方法 public void SubmitScore(int grossScore, int touches, int time, int netScore) { // 查找目标队伍 var targetTeam = currentGameData.teamRoundData.FirstOrDefault(t => t.TeamID == targetTeamID); if (targetTeam == null) return; // 检查该轮次是否已存在,存在则更新,不存在则添加 var targetRound = targetTeam.roundDataContainer.FirstOrDefault(r => r.RoundID == targetRoundID); if (targetRound != null) { targetRound.GrossScore = grossScore; targetRound.Touches = touches; targetRound.Time = time; targetRound.NetScore = netScore; } else { var newRound = new CurrentGameData.RoundDataContainer { RoundID = targetRoundID, GrossScore = grossScore, Touches = touches, Time = time, NetScore = netScore }; targetTeam.roundDataContainer.Add(newRound); } // 触发排行榜刷新 FindObjectOfType<LeaderboardManager>()?.RefreshLeaderboard(); } }
用TeamTableData生成排行榜条目
在排行榜脚本中,遍历CurrentGameData的数据,生成并更新UI条目:
public class LeaderboardManager : MonoBehaviour { [SerializeField] private CurrentGameData currentGameData; [SerializeField] private TeamTableData teamTablePrefab; [SerializeField] private Transform teamListParent; public void RefreshLeaderboard() { // 清空现有UI条目 foreach (Transform child in teamListParent) { Destroy(child.gameObject); } if (currentGameData == null || currentGameData.teamRoundData.Count == 0) return; // 遍历所有队伍生成UI foreach (var team in currentGameData.teamRoundData) { var latestRound = team.roundDataContainer.LastOrDefault(); if (latestRound == null) continue; var tableEntry = Instantiate(teamTablePrefab, teamListParent); tableEntry.teamIDText.text = team.TeamID.ToString(); tableEntry.teamNameText.text = team.TeamName; tableEntry.teamSchoolText.text = team.TeamSchool; tableEntry.teamCatText.text = team.TeamCatID; tableEntry.roundIDText.text = latestRound.RoundID; tableEntry.grossScoreText.text = latestRound.GrossScore.ToString(); tableEntry.touchText.text = latestRound.Touches.ToString(); tableEntry.grossTimeText.text = latestRound.Time.ToString(); tableEntry.netScoreText.text = latestRound.NetScore.ToString(); // 保存UI引用到队伍数据,方便后续单独更新 team.tableTeamLine = tableEntry.gameObject; } } }
内容的提问来源于stack exchange,提问作者Br Barry
相关产品推荐
相关产品推荐

