You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity C#:如何访问Scriptable Object中的List及子List数据

解决Scriptable Object嵌套List数据访问问题

问题根源分析

你遇到的核心问题来自三个方面:

  1. 冗余的Scriptable Object引用:MenuManager中错误声明了currentCategory和currentRound两个独立的CurrentGameData引用,这两个字段应该是currentGameData实例下的属性,而非单独的对象。
  2. 不安全的列表访问:直接通过索引[1]访问列表元素,未判断列表是否为空或索引是否越界,容易触发空引用或索引越界异常。
  3. 未初始化/赋值的数据:如果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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 03:29:55