2025Unity超详细《坦克大战3D》项目实战案例(上篇)——UI搭建并使用和数据持久化(附资源和源代码)

一、需求分析

1.项目所需具备前提知识

2.核心需求框架

3.详细功能需求

[(一)UI 功能需求](#(一)UI 功能需求)

(二)数据存储功能需求

(三)游戏核心逻辑功能需求

二、场景搭建

1.导入素材

2.个性化搭建

三、开始界面面板

1.通用面板基类:BasePanel

2.核心功能说明

3.开始界面面板:BeginPanel

4.核心功能说明

四、设置面板实现

1.设置面板:SettingPanel

2.功能说明

[3.开始面板完善:BeginPanel 更新](#3.开始面板完善:BeginPanel 更新)

五、音乐数据管理

1.音乐数据类:MusicData

2.说明

3.游戏数据管理器:GameDataMgr

4.核心功能说明

5.设置面板完善:SettingPanel

6.核心更新说明

六、排行榜的UI搭建与使用

1.开始面板的完善

2.排行榜面板(RankPanel)

3.说明

七、排行榜数据系统与面板完善

1.单条排行榜数据:RankInfo类

2.排行榜列表容器:RankList类

3.游戏数据管理器完善扩展:GameDataMgr类

4.核心扩展说明

5.排行榜面板完善:RankPanel

6.功能说明

7.实现流程与原理

八、背景音乐管理系统

1.背景音乐管理类:BKMusic

2.核心功能说明

3.游戏数据管理器更新:GameDataMgr

4.核心更新说明

九、资源以及源代码

1.资源(切勿商用)

2.源代码


演示

一、需求分析

1.项目所需具备前提知识

(1)Unity : 理解 Unity 编辑器操作、场景搭建、游戏对象管理等核心基础。
(2)数据持久化: 能够通过 PlayerPrefs 实现数据的存储与读取。
**(3)UI 开发:**具备使用 GUI 创建游戏界面、添加交互控件的能力。

2.核心需求框架

(1)UI 功能

(2)数据存储功能

(3)游戏核心逻辑功能

**3.**详细功能需求

(一)UI 功能需求

8 个 UI 面板脚本,同时提取 1 个基类脚本封装所有 UI 面板的共同点

**(1)基类脚本:**提取所有 UI 面板的共性逻辑,如面板的显示 / 隐藏控制、通用控件初始化、基础交互事件绑定等,为 7 个功能面板提供基础支撑。

**(2)开始界面面板:**包含 "开始游戏""游戏设置""退出游戏" 三个核心按钮,点击对应按钮可触发进入游戏、打开设置界面、退出游戏(退出时需提示 "此时退出游戏成绩将丢失")的操作,是玩家进入游戏的入口界面。

**(3)游戏界面面板:**实时更新游戏关键信息,包括游戏时间、坦克血量、玩家分数,同时配置退出游戏的关键控件及监听函数,玩家可通过该面板直观掌握游戏进度与自身状态。

**(4)游戏设置面板:**核心功能为控制音视频相关数据,提供音乐、音效的开关控制及音量调节功能,玩家可根据自身需求调整游戏音频输出,提升游戏体验。

**(5)游戏失败界面面板:**当玩家坦克被摧毁时触发显示,界面提示 "很遗憾,您的坦克已被摧毁",同时提供 "重来""返回上一级""再次挑战" 按钮,支持玩家重新开始游戏或返回上级菜单。

**(6)游戏胜利界面面板:**玩家通关时显示,提示 "恭喜通关",并提供用户名输入框与 "确定" 按钮,玩家输入用户名后点击确定,即可将本次通关成绩计入排行榜。

**(7)排行榜面板:**以表格形式展示玩家排名信息,表格包含 "排名""玩家名""分数""通关时间" 四列,默认展示 10 个排名位置,未填充的位置标注 "虚位以待",玩家可在该面板查看历史优秀成绩,同时支持 "返回上一级" 操作。

**(8)功能面板:**整合游戏过程中的辅助功能控件与监听函数,为其他功能面板提供交互支持,确保各界面操作逻辑连贯。

(二)数据存储功能需求

通过设计 "数据管理类" 统一管理游戏数据,实现关键信息的持久化存储,确保玩家设置与游戏成绩不丢失

(1)音效设置信息存储:记录背景音乐、音效的开关状态及音量大小数据,玩家调整音效设置后,数据管理类通过 PlayerPrefs 将设置信息存储,下次启动游戏时自动加载上次设置,无需重复调整。

(2)排行榜信息存储:存储排行榜单条数据(包含玩家名字、分数、通关时间),形成排行榜数据列表。当玩家通关并输入用户名后,数据管理类将新成绩与历史成绩对比排序,更新排行榜并存储,确保排行榜信息实时、准确。

(三)游戏核心逻辑功能需求

(1)坦克类设计:提取所有坦克的共同点,创建 "坦克基类",包含坦克血量、移动速度、开火间隔时间等基础属性,以及移动、开火、受击等通用方法;在此基础上设计 "敌方移动坦克" 子类,处理敌方坦克的简单 AI 逻辑(如自动移动、攻击)与怪物相关行为,区分玩家坦克与敌方坦克的功能差异。

(2)战斗与奖励逻辑

【1】战斗机制:玩家坦克可装备武器,武器存在开火间隔时间限制,发射的子弹命中敌方坦克或障碍物时触发相应碰撞处理;敌方坦克被摧毁后可能生成奖励对象,玩家坦克获取奖励可触发属性加成(如血量奖励)或武器升级(如切换特殊子弹)。

【2】奖励处理:场景中设置可获取的武器奖励对象与属性临时奖励对象,玩家坦克接触奖励对象后触发领取逻辑,实时更新自身属性或武器状态,增强游戏策略性与趣味性。

(3)场景与视角逻辑:配置小地图摄像机,实现摄像机跟随玩家坦克移动的功能,同时在小地图中标记玩家坦克、敌方坦克、奖励对象等关键物体信息,帮助玩家掌握整个战场布局,提升游戏操作体验。

(4)得分与死亡逻辑:玩家摧毁敌方坦克、获取奖励时触发得分逻辑,实时累计并更新分数;当玩家坦克血量为 0 时触发死亡逻辑,跳转至游戏失败界面,结束当前游戏进程。


二、场景搭建

1.导入素材

2.个性化搭建

(1)从导入的资源中,搭建个性化的场地

(2)从导入的GUI预制体中,搭建个性化的UI界面,如上图所示:


三、开始界面面板

1.通用面板基类:BasePanel<T>

实现一个泛型基类 BasePanel<T>:

(1)为所有 UI 面板提供单例管理

(2)显示 / 隐藏基础功能,避免重复编码。

cs 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BasePanel<T> : MonoBehaviour where T:class
{
    //两个关键的静态成员
    //私有的静态成员变量(申明)
    private static T instance;
    //公共的静态成员属性或者方法(获取)
    public static T Instance => instance;

    private void Awake()
    {
        //在Awake中初始化的 原因是
        //我们的面板脚本 在场景上 肯定只会挂载一次
        //那么我们可以在这个脚本的生命周期函数的Awake中
        //直接记录场景上 唯一的这个脚本
        instance = this as T;
    }

    public virtual void ShowMe()
    {
        this.gameObject.SetActive(true);
    }

    public virtual void HideMe()
    {
        this.gameObject.SetActive(false);
    }
}

2.核心功能说明

(1)单例模式实现 :通过静态私有变量 instance 与公开属性 Instance,确保每个面板在场景中只有一个实例,外部可通过 XXXPanel.Instance 直接访问(如后续的 BeginPanel.Instance)。

(2)生命周期初始化 :在 Awake 方法中完成单例赋值,因为 Awake 在脚本生命周期中仅执行一次,且早于 Start,能保证面板实例在使用前完成初始化。

(3)基础显示 / 隐藏接口 :提供 ShowMe()(激活面板)和 HideMe()(隐藏面板)两个虚方法,子类可根据需要重写(如添加动画过渡),当前保持基础功能。

3.开始界面面板:BeginPanel

BeginPanel 继承自 BasePanel<BeginPanel>,是游戏启动后第一个交互界面,负责 "开始游戏""设置""退出""排行榜" 等核心操作入口。

cs 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

public class BeginPanel : BasePanel<BeginPanel>
{
    //首先声明公共的成员变量 来关联各个控件
    public CustomGUIButton btnBegin;
    public CustomGUIButton btnSetting;
    public CustomGUIButton btnQuit;
    public CustomGUIButton btnRank;

    // Start is called before the first frame update
    void Start()
    {
        //监听一次按钮点击过后要做什么
        btnBegin.clickEvent += () =>
        {
            //切换场景
            SceneManager.LoadScene("GameScene");
        };
        btnSetting.clickEvent += () =>
        {
            //打开设置面板
        };
        btnQuit.clickEvent += () =>
        {
            //退出游戏
            Application.Quit();
        };
        btnRank.clickEvent += () =>
        {
            //打开排行榜面板
        };
    }
}

4.核心功能说明

(1)控件关联:声明 4 个 CustomGUIButton 类型的公共变量(btnBegin/btnSetting/btnQuit/btnRank),需在 Unity Inspector 面板中手动将 UI 按钮拖入对应字段,完成脚本与控件的绑定。

(2)按钮点击事件:在 Start 方法中为每个按钮绑定点击逻辑(基于自定义按钮的 clickEvent 事件):

  • 开始游戏(btnBegin):通过 SceneManager.LoadScene("GameScene") 加载游戏主场景(需确保 "GameScene" 已在 Build Settings 中添加);
  • 退出游戏(btnQuit):通过 Application.Quit() 关闭游戏(打包后生效,Editor 模式下需额外处理可参考后续扩展);
  • 设置(btnSetting) 与 排行榜(btnRank):当前预留接口,后续可添加 SettingPanel.Instance.ShowMe()RankPanel.Instance.ShowMe() 实现面板切换。

四、设置面板实现

1.设置面板:SettingPanel

设置面板用于管理游戏中的音乐、音效等配置,继承自通用基类BasePanel<SettingPanel>,实现基本的音视频设置功能。

cs 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SettingPanel : BasePanel<SettingPanel>
{
    //1. 声明成员变量 关联控件
    public CustomGUISlider sliderMusic;    // 音乐音量滑块
    public CustomGUISlider sliderSound;    // 音效音量滑块

    public CustomGUIToggle togMusic;       // 音乐开关
    public CustomGUIToggle togSound;       // 音效开关

    public CustomGUIButton btnClose;        // 关闭按钮

    // Start is called before the first frame update
    void Start()
    {
        //2. 监听对应的事件 处理逻辑
        sliderMusic.changeValue += (value) =>
        {
            // 处理音乐音量变化
        };

        sliderSound.changeValue += (value) =>
        {
            // 处理音效音量变化
        };

        togMusic.changeValue += (value) =>
        {
            // 处理音乐开关状态变化
        };

        togSound.changeValue += (value) =>
        {
            // 处理音效效开关状态变化
        };

        btnClose.clickEvent += () =>
        {
            // 隐藏当前设置面板
            HideMe();
            // 重新显示开始面板
            BeginPanel.Instance.ShowMe();
        };

        // 初始隐藏设置面板
        HideMe();
    }
}

2.功能说明

(1)控件关联:通过公共变量关联 4 个核心控件(2 个滑块、2 个开关、1 个按钮),需在 Unity Inspector 中手动绑定对应 UI 元素。

(2)事件绑定

  • 音量滑块(sliderMusic/sliderSound):通过changeValue事件监听数值变化,预留音乐 / 音效音量调节逻辑接口;
  • 开关控件(togMusic/togSound):通过changeValue事件监听开关状态,预留音乐 / 音效启用 / 禁用逻辑接口;
  • 关闭按钮(btnClose):点击后隐藏当前面板(HideMe()),并重新显示开始面板(BeginPanel.Instance.ShowMe()),实现面板切换。

(3)初始状态 :在Start方法末尾调用HideMe(),确保游戏启动时设置面板默认隐藏。

3.开始面板完善:BeginPanel 更新

cs 复制代码
......
btnSetting.clickEvent += () =>
        {
            // 打开设置面板
            SettingPanel.Instance.ShowMe();
            // 隐藏当前开始面板(避免点击穿透)
            HideMe();
        };
......

btnSetting的点击事件中,通过SettingPanel.Instance.ShowMe()显示设置面板,并调用HideMe()隐藏自身,避免两个面板同时显示导致的交互冲突(如点击穿透)


五、音乐数据管理

1.音乐数据类:MusicData

该类用于定义需要存储的音乐相关配置数据,是数据持久化的基础模型

cs 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 音效数据类 用于存储音乐设置相关的信息
/// </summary>
public class MusicData
{
    //背景音乐是否开启
    public bool isOpenBK;
    //音效是否开启
    public bool isOpenSound;
    //背景音乐音量
    public float bkValue;
    //音效音量
    public float soundValue;

    //加一个是否是第一次加载数据的标识
    public bool notFirst;
}

2.说明

(1)存储了 4 项核心配置:背景音乐开关(isOpenBK)、音效开关(isOpenSound)、背景音乐音量(bkValue)、音效音量(soundValue

(2)notFirst用于标记是否是首次进入游戏,便于初始化默认配置

3.游戏数据管理器:GameDataMgr

单例模式的游戏数据管理类,负责音乐数据的加载、更新和持久化存储

cs 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 这个是游戏数据管理类 是一个单例模式对象
/// </summary>
public class GameDataMgr
{
    private static GameDataMgr instance = new GameDataMgr();
    public static GameDataMgr Instance { get => instance; }

    //音效数据对象
    public MusicData musicData;

    private GameDataMgr()
    {
        //初始化游戏数据
        musicData = PlayerPrefsDataMgr.Instance.LoadData(typeof(MusicData), "Music") as MusicData;
        //如果第一次进入游戏 没有音效数据 则初始化默认值
        if( !musicData.notFirst )
        {
            musicData.notFirst = true;
            musicData.isOpenBK = true;
            musicData.isOpenSound = true;
            musicData.bkValue = 1;
            musicData.soundValue = 1;
            PlayerPrefsDataMgr.Instance.SaveData(musicData, "Music");
        }
    }

    //提供一些API给外部 方便数据的改变存储
    
    //开启或者关闭背景音乐
    public void OpenOrCloseBKMusic(bool isOpen)
    {
        musicData.isOpenBK = isOpen;
        //存储改变后的数据
        PlayerPrefsDataMgr.Instance.SaveData(musicData, "Music");
    }

    //开启或者关闭音效
    public void OpenOrCloseSound(bool isOpen)
    {
        musicData.isOpenSound = isOpen;
        //存储改变后的数据
        PlayerPrefsDataMgr.Instance.SaveData(musicData, "Music");
    }

    //改变背景音乐大小
    public void ChangeBKValue(float value)
    {
        musicData.bkValue = value;
        //存储改变后的数据
        PlayerPrefsDataMgr.Instance.SaveData(musicData, "Music");
    }

    //改变音效大小
    public void ChangeSoundValue(float value)
    {
        musicData.soundValue = value;
        //存储改变后的数据
        PlayerPrefsDataMgr.Instance.SaveData(musicData, "Music");
    }
}

4.核心功能说明

(1)单例模式 :通过私有构造函数和静态Instance属性确保全局唯一实例

(2)数据初始化

  • 在构造函数中通过PlayerPrefsDataMgr加载本地存储的音乐配置
  • 首次进入游戏时自动初始化默认配置(音乐音效开启,音量设为最大 1)

(3)数据操作 API

  • 提供了 4 个方法用于更新音乐配置(开关和音量调节)
  • 每次数据变更后自动调用SaveData方法持久化存储

5.设置面板完善:SettingPanel

实现配置的实时更新和显示同步

cs 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SettingPanel : BasePanel<SettingPanel>
{
    //1声明成员变量 关联控件
    public CustomGUISlider sliderMusic;
    public CustomGUISlider sliderSound;

    public CustomGUIToggle togMusic;
    public CustomGUIToggle togSound;

    public CustomGUIButton btnClose;

    // Start is called before the first frame update
    void Start()
    {
        //2监听对应的事件 处理逻辑
        //处理音乐的变化
        sliderMusic.changeValue += (value) => GameDataMgr.Instance.ChangeBKValue(value);
        //处理音效的变化
        sliderSound.changeValue += (value) => GameDataMgr.Instance.ChangeSoundValue(value);

        //处理音乐开关
        togMusic.changeValue += (value) => GameDataMgr.Instance.OpenOrCloseBKMusic(value);
        //处理音效开关
        togSound.changeValue += (value) => GameDataMgr.Instance.OpenOrCloseSound(value);

        btnClose.clickEvent += () =>
        {
            //隐藏自己
            HideMe();
            //让开始面板重新显示出来
            BeginPanel.Instance.ShowMe();
        };

        HideMe();
    }

    //根据数据 更新面板
    public void UpdatePanelInfo()
    {
        //面板上的信息根据音效数据更新
        MusicData data = GameDataMgr.Instance.musicData;

        //设置面板内容
        sliderMusic.nowValue = data.bkValue;
        sliderSound.nowValue = data.soundValue;
        togMusic.isSel = data.isOpenBK;
        togSound.isSel = data.isOpenSound;
    }

    public override void ShowMe()
    {
        base.ShowMe();
        //每次显示面板时 同步更新面板内容
        UpdatePanelInfo();
    }
}

6.核心更新说明

(1)事件绑定优化 :将滑块和开关的事件直接绑定到GameDataMgr的 API,实现数据的实时更新

(2)面板数据同步

  • 新增UpdatePanelInfo方法,用于根据当前存储的音乐数据更新面板控件状态
  • 重写ShowMe方法,确保每次打开面板时都能显示最新的配置信息

(3)交互逻辑:保持原有面板切换逻辑,关闭按钮点击后仍会隐藏设置面板并显示开始面板

六、排行榜的UI搭建与使用

1.开始面板的完善

cs 复制代码
public class BeginPanel : BasePanel<BeginPanel>
{
   ......
        btnRank.clickEvent += () =>
        {
            //打开排行榜面板
            RankPanel.Instance.ShowMe();
            //避免穿透 隐藏自己
            HideMe();
        };
......
    }
}

排行榜按钮:点击后显示排行榜面板并隐藏自身

2.排行榜面板(RankPanel)

cs 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class RankPanel : BasePanel<RankPanel>
{
    //关联public的 控件对象

    public CustomGUIButton btnClose;

    //因为控件较多 拖的话 工作量太大了 我们直接偷懒 通过代码找
    private List<CustomGUILabel> labPM = new List<CustomGUILabel>();
    private List<CustomGUILabel> labName = new List<CustomGUILabel>();
    private List<CustomGUILabel> labScore = new List<CustomGUILabel>();
    private List<CustomGUILabel> labTime = new List<CustomGUILabel>();

    // Start is called before the first frame update
    void Start()
    {
        for (int i = 1; i <= 10 ; i++)
        {
            //小知识应用 找子对象的子对象 可以通过 斜杠来区分父子关系
            labPM.Add(this.transform.Find("PM/labPM" + i).GetComponent<CustomGUILabel>());
            labName.Add(this.transform.Find("Name/labName" + i).GetComponent<CustomGUILabel>());
            labScore.Add(this.transform.Find("Score/labScore" + i).GetComponent<CustomGUILabel>());
            labTime.Add(this.transform.Find("Time/labTime" + i).GetComponent<CustomGUILabel>());
        }
        //处理事件监听逻辑

        btnClose.clickEvent += () =>
        {
            HideMe();
            BeginPanel.Instance.ShowMe();
        };

        HideMe();
    }

    public override void ShowMe()
    {
        base.ShowMe();
        UpdatePanelInfo();
    }

    public void UpdatePanelInfo()
    {
        //处理根据排行榜数据 更新面板
    }
}

3.说明

(1)控件获取:通过代码查找方式获取 10 条排行榜数据所需的标签控件,分为排名(PM)、名称(Name)、分数(Score)和时间(Time)四个列表

(2)关闭按钮逻辑:点击后隐藏排行榜面板并显示开始面板

(3)面板显示:重写 ShowMe 方法,在显示面板时调用 UpdatePanelInfo 方法更新数据

(4)预留了 UpdatePanelInfo 方法,用于后续根据排行榜数据更新面板显示


七、排行榜数据系统与面板完善

1.单条排行榜数据:RankInfo类

cs 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 排行榜单条数据
/// </summary>
public class RankInfo
{
    public string name;   // 玩家名称
    public int score;     // 玩家分数
    public float time;    // 游戏时间(秒)

    public RankInfo()
    {

    }

    public RankInfo(string name, int score, float time)
    {
        this.name = name;
        this.score = score;
        this.time = time;
    }
}

该类定义了单条排行榜记录应包含的核心信息:玩家名称、分数和游戏时间,提供了默认构造函数和带参构造函数便于数据初始化。

2.排行榜列表容器:RankList类

cs 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 排行榜列表
/// </summary>
public class RankList
{
    public List<RankInfo> list;  // 排行榜数据列表
}

简单的容器类,用于管理多条排行榜数据,便于进行序列化存储和批量处理。

3.游戏数据管理器完善扩展:GameDataMgr类

cs 复制代码
public class GameDataMgr
{
    ......
    // 排行榜数据对象
    public RankList rankData;

    private GameDataMgr()
    {
       ......
        // 初始化排行榜数据
        rankData = PlayerPrefsDataMgr.Instance.LoadData(typeof(RankList), "Rank") as RankList;
    }

    // 新增:添加排行榜数据并排序
    public void AddRankInfo(string name, int score, float time)
    {
        rankData.list.Add(new RankInfo(name, score, time));
        // 按游戏时间升序排序(时间越短排名越前)
        rankData.list.Sort((a, b) => a.time < b.time ? -1 : 1);
        // 只保留前10条数据
        for (int i = rankData.list.Count - 1; i >= 10; i--)
        {
            rankData.list.RemoveAt(i);
        }
        // 保存更新后的数据
        PlayerPrefsDataMgr.Instance.SaveData(rankData, "Rank");
    }
    
   ......
}

4.核心扩展说明

(1)新增数据成员 :添加rankData用于存储排行榜数据列表

(2)初始化逻辑 :在构造函数中通过PlayerPrefsDataMgr加载本地存储的排行榜数据

(3)排行榜管理 API

  • AddRankInfo方法:添加新记录、按游戏时间排序、保留前 10 条数据并自动保存
  • 排序逻辑:使用 Lambda 表达式实现按游戏时间升序排序(时间越短排名越靠前)
  • 数据限制:只保留排名前 10 的记录,超出部分从列表尾部移除

5.排行榜面板完善:RankPanel

cs 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class RankPanel : BasePanel<RankPanel>
{
    // 关闭按钮
    public CustomGUIButton btnClose;

    // 排行榜显示控件列表(通过代码查找获取)
    private List<CustomGUILabel> labName = new List<CustomGUILabel>();
    private List<CustomGUILabel> labScore = new List<CustomGUILabel>();
    private List<CustomGUILabel> labTime = new List<CustomGUILabel>();

    void Start()
    {
        // 初始化控件列表(获取10条记录的显示控件)
        for (int i = 1; i <= 10 ; i++)
        {
            labName.Add(this.transform.Find("Name/labName" + i).GetComponent<CustomGUILabel>());
            labScore.Add(this.transform.Find("Score/labScore" + i).GetComponent<CustomGUILabel>());
            labTime.Add(this.transform.Find("Time/labTime" + i).GetComponent<CustomGUILabel>());
        }

        // 关闭按钮事件
        btnClose.clickEvent += () =>
        {
            HideMe();
            BeginPanel.Instance.ShowMe();
        };

        // 测试代码(可选)
        // GameDataMgr.Instance.AddRankInfo("测试数据", 100, 8432);

        // 初始隐藏面板
        HideMe();
    }

    // 重写显示方法,显示时更新数据
    public override void ShowMe()
    {
        base.ShowMe();
        UpdatePanelInfo();
    }

    // 更新面板数据
    public void UpdatePanelInfo()
    {
        // 获取排行榜数据
        List<RankInfo> list = GameDataMgr.Instance.rankData.list;
        
        // 遍历数据更新UI
        for (int i = 0; i < list.Count; i++)
        {
            // 更新玩家名称
            labName[i].content.text = list[i].name;
            // 更新分数
            labScore[i].content.text = list[i].score.ToString();
            // 格式化时间(秒 -> 时:分:秒)
            int time = (int)list[i].time;
            labTime[i].content.text = "";
            
            // 处理小时
            if (time / 3600 > 0)
            {
                labTime[i].content.text += time / 3600 + "时";
            }
            // 处理分钟
            if (time % 3600 / 60 > 0 || labTime[i].content.text != "")
            {
                labTime[i].content.text += time % 3600 / 60 + "分";
            }
            // 处理秒
            labTime[i].content.text += time % 60 + "秒";
        }
    }
}

6.功能说明

(1)控件初始化:通过代码查找方式获取 10 条记录所需的显示控件(名称、分数、时间),避免手动拖拽绑定的繁琐操作

(2)面板交互:关闭按钮点击后隐藏排行榜面板并显示开始面板,保持面板切换的一致性

(3)数据展示

  • UpdatePanelInfo方法:从GameDataMgr获取排行榜数据并更新到 UI
  • 时间格式化:将秒数转换为 "时:分: 秒" 的易读格式,只显示有意义的时间单位

(4)显示逻辑 :重写ShowMe方法,确保每次打开面板时都能显示最新的排行榜数据

7.实现流程与原理

(1)数据流向

  • 新增记录 → 调用GameDataMgr.AddRankInfo → 数据排序与限制 → 保存到本地
  • 打开排行榜 → 调用UpdatePanelInfo → 从GameDataMgr获取数据 → 更新 UI 显示

(2)UI 结构要求

  • 排行榜面板需包含 Name、Score、Time 三个父节点
  • 每个父节点下需有 10 个子节点(labName1 至 labName10 等),用于显示对应排名的信息

八、背景音乐管理系统

1.背景音乐管理类:BKMusic

该类负责背景音乐的实际播放控制,采用单例模式确保全局唯一的音乐管理实例。

cs 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BKMusic : MonoBehaviour
{
    private static BKMusic instance;

    public static BKMusic Instance => instance;

    private AudioSource audioSource;

    // Start is called before the first frame update
    void Awake()
    {
        instance = this;
        // 得到自己依附的游戏对象上挂载的音频源脚本
        audioSource = this.GetComponent<AudioSource>();
        // 初始化时根据数据设置音量和开关状态
        ChangeValue(GameDataMgr.Instance.musicData.bkValue);
        ChangeOpen(GameDataMgr.Instance.musicData.isOpenBK);
    }

    /// <summary>
    /// 改变背景音乐大小
    /// </summary>
    /// <param name="value"></param>
    public void ChangeValue(float value)
    {
        audioSource.volume = value;
    }

    /// <summary>
    /// 开关背景音乐
    /// </summary>
    /// <param name="isOpen"></param>
    public void ChangeOpen(bool isOpen)
    {
        // 如果开启就是不静音,关闭则静音
        audioSource.mute = !isOpen;
    }
}

2.核心功能说明

(1)单例模式实现 :通过静态Instance属性提供全局访问点,确保音乐控制的唯一性

(2)音频源获取 :在Awake方法中获取组件上的AudioSource组件,用于实际的音频播放控制

(3)初始化配置 :启动时自动从GameDataMgr读取保存的音乐配置,设置初始音量和开关状态

(4)控制接口

  • ChangeValue:调整背景音乐音量
  • ChangeOpen:控制背景音乐开关(通过静音状态实现)

3.游戏数据管理器更新:GameDataMgr

cs 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 游戏数据管理类(单例模式)
/// </summary>
public class GameDataMgr
{
    ......  
    // 背景音乐开关控制(新增实时控制逻辑)
    public void OpenOrCloseBKMusic(bool isOpen)
    {
        musicData.isOpenBK = isOpen;

        // 实时控制场景上的背景音乐开关
        BKMusic.Instance.ChangeOpen(isOpen);

        // 存储改变后的数据
        PlayerPrefsDataMgr.Instance.SaveData(musicData, "Music");
    }

   ......

    // 背景音乐音量控制(新增实时控制逻辑)
    public void ChangeBKValue(float value)
    {
        musicData.bkValue = value;

        // 实时控制场景上的背景音乐大小
        BKMusic.Instance.ChangeValue(value);

        // 存储改变后的数据
        PlayerPrefsDataMgr.Instance.SaveData(musicData, "Music");
    }
......
   
}

4.核心更新说明

(1)背景音乐实时控制

  • OpenOrCloseBKMusic方法中新增BKMusic.Instance.ChangeOpen(isOpen)调用,实现开关状态的实时生效
  • ChangeBKValue方法中新增BKMusic.Instance.ChangeValue(value)调用,实现音量的实时调整

(2)数据与播放同步:保持原有数据持久化逻辑,确保配置修改后既能立即生效,又能保存到本地


九、资源以及源代码

1.资源(切勿商用)

通过网盘分享的文件:资源.zip

链接: https://pan.baidu.com/s/1IOyXuw8z5YLATI9Bwwpn5Q?pwd=1111 提取码: 1111

--来自百度网盘超级会员v5的分享

2.源代码

因为只是UI且篇幅已经较长,所以项目以及源代码放在下篇(游戏场景篇)

相关推荐
曲大家12 小时前
C#生成控笔视频,完整版
c#·绘图
ajassi200013 小时前
开源 C# 快速开发(三)复杂控件
开发语言·开源·c#
WangMing_X13 小时前
C#上位机软件:2.1 .NET项目解决方案的作用
开发语言·c#
Sammyyyyy17 小时前
Go与C# 谁才更能节省内存?
java·golang·c#
Dream achiever17 小时前
7.WPF 的 TextBox 和 TextBlock 控件
开发语言·c#·wpf
爱吃小胖橘17 小时前
Unity-动画子状态机
3d·unity·c#·游戏引擎
SmalBox18 小时前
【光照】[物理模型]中的[BRDF]是什么?
unity·渲染
大飞pkz18 小时前
【设计模式】适配器模式
开发语言·设计模式·c#·适配器模式
大飞pkz18 小时前
【设计模式】外观模式
开发语言·设计模式·c#·外观模式