【XR开发系列】UI 入门 - 创建一个简单的分数显示

在 Unity 游戏开发中,UI(用户界面)是连接玩家与游戏核心玩法的重要桥梁。无论是显示分数、生命值,还是提供按钮交互、关卡提示,UI 系统都是让游戏 "可交互、可反馈" 的关键。此前我们实现了小球移动与碰撞收集功能,但分数仅能在控制台打印,玩家无法直观感知游戏进度 ------ 本次将以「创建分数显示 UI」为核心,从零讲解 Unity UGUI 系统的基础用法,带你完成从 "控制台调试" 到 "可视化 UI 展示" 的进阶,掌握 UI 创建、锚点适配、脚本联动等核心知识点,让收集类小游戏具备完整的视觉反馈。

一、前置准备:基于收集玩法搭建基础项目

本次 UI 开发基于《碰撞检测之谜 - 当小球遇到收集物》的项目扩展,如果你是从零开始,可按照以下步骤快速搭建可运行的基础场景,保证 UI 开发有完整的交互逻辑支撑。推荐使用Unity 2022 LTS版本,UGUI 系统稳定,适配主流教学案例。

1. 快速创建核心游戏对象

打开 Unity Hub,新建 3D Core 项目,命名为UI_ScoreDisplay,路径确保纯英文、无空格。进入编辑器后,完成核心对象创建:

  1. 地面(Ground) :右键创建 3D 物体→平面,位置(0,0,0),作为小球移动载体;
  2. 玩家小球(PlayerBall) :右键创建 3D 物体→球体,位置(0,0.5,0),添加Rigidbody(取消 Use Gravity、冻结全部旋转),挂载移动脚本实现 WASD 操控;
  3. 收集物(Collectible) :右键创建 3D 物体→立方体,缩放(0.5,0.5,0.5),位置(2,0.25,2),勾选碰撞体 Is Trigger,挂载收集触发脚本;
  4. 游戏管理器(GameManager):新建空物体,挂载单例脚本,负责全局分数管理。

2. 核心功能代码复用(保证交互逻辑完整)

为了聚焦 UI 开发,这里直接复用核心功能代码,确保小球可移动、收集物可触发、分数可累加:

(1)小球移动脚本(BallPlayerController.cs)

C#

复制代码
using UnityEngine;
public class BallPlayerController : MonoBehaviour
{
    public float moveSpeed = 5f;
    void Update()
    {
        float horizontal = Input.GetAxis("Horizontal");
        float vertical = Input.GetAxis("Vertical");
        Vector3 moveDir = new Vector3(horizontal, 0, vertical).normalized;
        transform.Translate(moveDir * moveSpeed * Time.deltaTime, Space.World);
        
        // 边界限制
        Vector3 curPos = transform.position;
        curPos.x = Mathf.Clamp(curPos.x, -4.5f, 4.5f);
        curPos.z = Mathf.Clamp(curPos.z, -4.5f, 4.5f);
        transform.position = curPos;
    }
}
(2)收集物触发脚本(CollectibleItem.cs)

C#

复制代码
using UnityEngine;
public class CollectibleItem : MonoBehaviour
{
    public int scoreValue = 1;
    void OnTriggerEnter(Collider other)
    {
        if (other.CompareTag("Player"))
        {
            GameManager.Instance.AddScore(scoreValue);
            Destroy(gameObject);
        }
    }
}
(3)游戏管理器脚本(GameManager.cs)

C#

复制代码
using UnityEngine;
public class GameManager : MonoBehaviour
{
    public static GameManager Instance;
    public int totalScore = 0;

    void Awake()
    {
        if (Instance == null) Instance = this;
        else Destroy(gameObject);
    }

    public void AddScore(int value)
    {
        totalScore += value;
        Debug.Log($"当前分数:{totalScore}"); // 临时控制台输出
    }
}

3. 测试基础功能

将小球标签设为Player,复制多个收集物布置在场景中,运行游戏:

  • 确认 WASD 可控制小球移动;
  • 确认小球触碰收集物后,收集物消失,控制台打印分数累加;
  • 基础功能无问题后,开始 UI 开发环节。

二、UGUI 核心认知:新手必须理清的基础概念

Unity 的 UI 系统基于 UGUI(Unity Graphic UI)构建,是目前最主流的 UI 解决方案。在创建分数显示前,先理清 3 个核心概念,避免新手常见的 "UI 变形、适配异常、显示错位" 问题。

1. Canvas(画布):所有 UI 的 "容器"

Canvas 是 UGUI 的核心载体,所有 UI 元素必须放在 Canvas 下才能显示。Unity 中创建任何 UI 元素时,会自动生成 Canvas 和 EventSystem(事件系统,处理 UI 交互):

  • Canvas 渲染模式 :默认使用Screen Space - Overlay(屏幕空间 - 覆盖),UI 直接绘制在屏幕最上层,不受 3D 场景影响,适合分数、血条等 HUD 界面;
  • Canvas Scaler :画布缩放器,核心作用是让 UI 在不同分辨率屏幕下自适应,新手默认使用Scale With Screen Size即可,后续会详细配置。

2. Rect Transform(矩形变换):UI 的 "位置与尺寸" 控制器

普通 3D 物体使用Transform组件控制位置,而 UI 元素使用Rect Transform,包含以下核心属性:

  • 锚点(Anchors):UI 的定位基准,决定 UI 如何跟随屏幕缩放 / 移动。比如将锚点设为 "右上角",UI 会始终固定在屏幕右上角;
  • 锚定位置(Anchored Position):基于锚点的偏移量,调整 UI 的具体位置;
  • 尺寸(Width/Height):UI 元素的宽高,单位为像素。

3. Text/TextMeshPro:文字显示的两种核心组件

Unity 提供两种文字显示组件:

  • Text:传统文字组件,操作简单,但对中文支持一般,适合快速开发;
  • TextMeshPro (TMP) :升级版文字组件,支持富文本、多语言、高清字体,是 Unity 官方推荐的首选方案。本次教程使用TextMeshPro,兼顾实用性与行业主流用法。

三、实战开发:创建分数显示 UI(从布局到适配)

接下来进入核心实操环节,我们将分步完成 "创建 Canvas→添加分数文本→配置锚点适配→脚本联动更新分数" 的完整流程。

1. 创建 Canvas 与分数文本 UI

步骤 1:创建 Canvas 和 TextMeshPro 文本
  1. 在层级面板右键→UI→Text - TextMeshPro,Unity 会自动生成CanvasEventSystemText (TMP)三个对象;
  2. Text (TMP)重命名为ScoreText,方便识别和脚本调用;
  3. 首次使用 TextMeshPro 会提示 "Import TMP Essential Resources",点击导入,这是 TMP 的核心资源包,包含字体、材质等基础文件。
步骤 2:基础文本样式配置(Inspector 面板)

选中ScoreText,在 Inspector 面板调整以下属性,让文字清晰易读:

  • Text Input:输入 "分数:0",作为初始显示内容;
  • Font Size:设置为 48,保证文字大小适中;
  • Color:选择白色(或深蓝色),对比鲜明;
  • Alignment:设置为 "Center"(居中),让文字在文本框内居中显示;
  • Width/Height:设置为 200×80,适配文字大小,避免文本框过大 / 过小。

2. 锚点配置:让 UI 适配不同分辨率

新手最容易遇到的问题是 "在自己电脑上 UI 位置正常,换分辨率就错位",核心原因是锚点配置不当。我们要让分数文本固定在屏幕右上角,适配所有分辨率:

步骤 1:调整 Rect Transform 锚点
  1. 选中ScoreText,找到 Rect Transform 组件,点击锚点(Anchors)右侧的方形图标(预设锚点按钮);
  2. 在弹出的锚点预设面板中,点击右上角的预设图标(第一行最后一个);
  3. 此时锚点会固定在屏幕右上角,Anchored Position的 X/Y 值变为相对于右上角的偏移量。
步骤 2:调整偏移量,优化显示位置

锚点固定后,调整Anchored Position的数值:

  • X:-50(向右上角左侧偏移 50 像素,避免贴边);
  • Y:-50(向右上角下方偏移 50 像素,避免贴边);调整后,无论屏幕分辨率如何变化,分数文本都会固定在右上角,不会错位。
步骤 3:配置 Canvas Scaler(全局适配)

选中Canvas,找到 Canvas Scaler 组件,配置以下属性:

  • UI Scale Mode :选择Scale With Screen Size
  • Reference Resolution :设置为1920×1080(主流屏幕分辨率,作为适配基准);
  • Screen Match Mode :选择Match Width Or Height,Match 值设为 0.5(兼顾宽高适配)。这样配置后,UI 会根据屏幕分辨率自动缩放,在手机、电脑等不同设备上都能正常显示。

3. 脚本联动:实现分数实时更新

现在 UI 布局和样式已完成,接下来通过脚本让分数文本跟随游戏内的分数实时更新。

步骤 1:修改 GameManager 脚本,关联 UI 文本

我们需要在 GameManager 中获取 ScoreText 组件,并在分数变化时更新文本内容:

C#

复制代码
using UnityEngine;
using TMPro; // 引入TMP命名空间,必须添加!

public class GameManager : MonoBehaviour
{
    public static GameManager Instance;
    public int totalScore = 0;
    public TextMeshProUGUI scoreText; // 关联分数文本UI

    void Awake()
    {
        if (Instance == null) Instance = this;
        else Destroy(gameObject);
        
        // 初始化分数显示
        UpdateScoreText();
    }

    // 累加分数的方法
    public void AddScore(int value)
    {
        totalScore += value;
        UpdateScoreText(); // 分数变化时更新UI
    }

    // 单独封装更新UI的方法,便于复用
    private void UpdateScoreText()
    {
        if (scoreText != null)
        {
            scoreText.text = $"分数:{totalScore}"; // 更新文本内容
        }
        else
        {
            Debug.LogError("未关联ScoreText组件!");
        }
    }
}
关键说明:
  • 必须添加using TMPro;命名空间,否则无法识别TextMeshProUGUI组件;
  • 将更新 UI 的逻辑封装为UpdateScoreText()方法,既保证代码整洁,也方便后续扩展(比如胜利时重置分数);
  • 添加空值判断,避免因未关联组件导致报错,提升代码健壮性。
步骤 2:关联 UI 组件到脚本
  1. 在层级面板选中GameManager对象;
  2. 在 Inspector 面板找到GameManager脚本的Score Text字段;
  3. 将层级面板中的ScoreText拖拽到该字段中,完成组件关联。

4. 完整测试:验证分数显示功能

  1. 在场景中复制多个收集物,布置在不同位置;
  2. 点击 Unity 播放按钮,运行游戏;
  3. 操控小球触碰收集物,观察屏幕右上角的分数文本:
    • 初始显示 "分数:0";
    • 每收集一个道具,分数数字实时增加,文本同步更新;
    • 切换不同游戏窗口分辨率(比如将 Game 视图从 16:9 改为 4:3),分数文本仍固定在右上角,无错位、变形。

四、新手常见问题排查:解决 UI 显示异常

开发过程中,新手容易遇到各种 UI 显示问题,以下是最常见的故障及解决方案:

1. 分数文本看不见 / 显示不全

  • 原因 1:TextMeshPro 资源未导入→重新导入 TMP Essential Resources;
  • 原因 2:文本颜色与背景色一致→修改 Text Color 为对比色(如白色);
  • 原因 3:文本框尺寸过小,文字被截断→增大 Rect Transform 的 Width/Height;
  • 原因 4 :Canvas 渲染模式错误→确认 Canvas 为Screen Space - Overlay

2. 分数不更新 / 显示始终为 0

  • 原因 1:未关联 ScoreText 组件→检查 GameManager 的 Score Text 字段是否拖拽了 ScoreText 对象;
  • 原因 2 :缺少using TMPro;命名空间→脚本中添加该命名空间,重新编译;
  • 原因 3:分数累加逻辑异常→先检查控制台是否打印分数,确认收集功能正常;
  • 原因 4:脚本未挂载到 GameManager 对象→确认 GameManager 对象上有 GameManager 脚本组件。

3. UI 在不同分辨率下错位

  • 原因:锚点配置错误→重新设置锚点为右上角预设,调整 Anchored Position 偏移量;
  • 补充:避免直接修改 Position 值,UI 必须通过锚点 + 锚定位置控制。

4. 运行时提示 "NullReferenceException"

  • 原因:访问了未初始化的组件(如 scoreText 为 null)→检查组件关联、脚本挂载、命名空间是否正确;
  • 解决方案 :添加空值判断(如代码中的if (scoreText != null)),快速定位问题。

五、功能进阶:让分数 UI 更美观、更实用

基础分数显示实现后,我们可以通过简单扩展,让 UI 更贴近商业游戏的效果,同时学习更多 UGUI 技巧。

1. 添加分数背景框(提升视觉层次)

  1. 在层级面板选中ScoreText,右键→UI→Image,创建一个 Image 对象作为子对象,重命名为ScoreBG
  2. 调整ScoreBG的 Rect Transform:
    • Width 设为 220,Height 设为 90,覆盖分数文本;
    • Anchored Position 设为 (0,0),与文本居中对齐;
  3. 在 Image 组件中:
    • Color 选择半透明深蓝色(RGBA:0,0,0.5,0.3);
    • 点击Source ImageCreateSpriteSquare,创建一个纯色背景;
  4. ScoreBG的层级调整为低于 ScoreText(在 Inspector 面板拖动顺序),避免遮挡文字。

2. 分数变化动画(提升反馈感)

添加简单的缩放动画,让分数更新时产生 "跳动" 效果,增强交互反馈:

C#

复制代码
// 在GameManager的UpdateScoreText方法中添加动画逻辑
private void UpdateScoreText()
{
    if (scoreText != null)
    {
        scoreText.text = $"分数:{totalScore}";
        // 缩放动画:先放大再恢复原尺寸
        StartCoroutine(ScoreBounceAnimation());
    }
}

// 协程实现动画
private IEnumerator ScoreBounceAnimation()
{
    scoreText.transform.localScale = Vector3.one * 1.2f; // 放大1.2倍
    yield return new WaitForSeconds(0.1f); // 等待0.1秒
    scoreText.transform.localScale = Vector3.one; // 恢复原尺寸
}

注意 :需要在 GameManager 脚本顶部添加using System.Collections;,才能使用协程(IEnumerator)。

3. 添加最高分显示(扩展数据管理)

记录并显示最高分,需要将最高分保存到本地(PlayerPrefs),即使重启游戏也不会丢失:

C#

复制代码
// 在GameManager中添加最高分逻辑
public int highScore;

void Awake()
{
    if (Instance == null) Instance = this;
    else Destroy(gameObject);
    
    // 读取本地保存的最高分
    highScore = PlayerPrefs.GetInt("HighScore", 0);
    UpdateScoreText();
}

public void AddScore(int value)
{
    totalScore += value;
    // 更新最高分
    if (totalScore > highScore)
    {
        highScore = totalScore;
        PlayerPrefs.SetInt("HighScore", highScore); // 保存到本地
        PlayerPrefs.Save();
    }
    UpdateScoreText();
}

private void UpdateScoreText()
{
    if (scoreText != null)
    {
        // 显示当前分数+最高分
        scoreText.text = $"分数:{totalScore}\n最高分:{highScore}";
    }
}

4. 调整文字换行与排版

修改 ScoreText 的属性,适配多行显示:

  • Line Spacing:设置为 1.2,增加行间距;
  • Alignment:改为 "Top Center",让多行文字顶部居中;
  • Width:调整为 250,适配两行文字宽度。

六、知识点总结与后续学习方向

通过 "创建分数显示 UI" 这个实战案例,我们系统掌握了 UGUI 的基础开发流程,这些知识点是后续开发复杂 UI 界面的核心基础。

1. 核心知识点回顾

  • UGUI 基础架构:Canvas 是 UI 的容器,Rect Transform 控制 UI 位置与适配,TextMeshPro 是文字显示的首选组件;
  • 适配关键:锚点(Anchors)决定 UI 的定位基准,Canvas Scaler 保证不同分辨率下的适配;
  • 脚本联动:通过获取 UI 组件引用,在逻辑层更新 UI 显示,核心是 "数据变化→调用 UI 更新方法";
  • 健壮性设计:添加空值判断、错误日志,避免组件未关联导致的报错;
  • 用户体验:通过背景、动画提升 UI 的视觉反馈,让交互更生动。

2. 后续进阶学习方向

掌握基础分数显示后,可以沿着以下方向深入学习 UGUI,构建完整的 UI 系统:

  1. 按钮与交互:学习 Button 组件,实现 "重新开始""暂停游戏" 等按钮功能;
  2. 进度条与血条:学习 Slider 组件,实现生命值、能量值的可视化显示;
  3. 弹窗系统:学习 Panel、Image、Text 组合,实现胜利 / 失败弹窗、提示框;
  4. UI 布局:学习 Horizontal Layout Group、Vertical Layout Group,快速排版多个 UI 元素;
  5. 多语言适配:基于 TextMeshPro 实现多语言切换,适配不同地区玩家;
  6. UI 优化:学习图集打包、合批渲染,提升 UI 渲染性能。

七、结语

UI 开发是 Unity 游戏开发中 "贴近玩家体验" 的核心环节,看似简单的分数显示,背后涵盖了 "布局、适配、脚本联动、用户体验" 等多个维度的思考。从控制台打印分数到屏幕可视化显示,你已经完成了从 "功能实现" 到 "玩家体验" 的关键跨越。

本次教程的核心逻辑 ------"数据层(GameManager)管理分数,UI 层(ScoreText)展示分数,逻辑层联动更新",是游戏 UI 开发的通用模式。无论是分数、血条、任务提示,还是复杂的商城界面、设置面板,都遵循 "数据与 UI 分离,通过脚本联动" 的核心思想。

接下来,不妨尝试给你的小游戏添加 "重新开始按钮""胜利弹窗",将所学的 UI 知识串联起来,逐步构建完整的游戏界面系统。记住,优秀的游戏 UI 不仅要 "显示信息",更要 "贴合玩家习惯、提升交互体验",这需要在实践中不断调试和优化。

相关推荐
工控小龙人1 天前
核电行业HMI:核岛设备的安全监控与操作界面
ui·人机交互·制造·用户界面
XiaoLeisj1 天前
Android 文件与数据存储实战:SharedPreferences、SQLite 与 Room 的渐进式实现
android·java·数据库·ui·sqlite·room·sp
互联网散修1 天前
鸿蒙应用开发UI基础第十八节:表单交互核心组件Button、Radio、Toggle示例演示
ui·交互·harmonyos
●VON1 天前
2G 内存云服务器部署 Spring Boot + MySQL 实战:从踩坑到上线
服务器·开发语言·spring boot·mysql·ui·von
Swift社区2 天前
ArkUI 如何设计 AI 原生 UI?
人工智能·ui
新缸中之脑2 天前
Claude生成式UI的逆向与利用
ui
ayaya_mana3 天前
快速安装Nginx-UI:让Nginx管理可视化的高效方案
运维·nginx·ui
zzz84153 天前
Spring Boot 3.x 引入springdoc-openapi (内置Swagger UI、webmvc-api)
spring boot·后端·ui
乘风破浪的小太阳3 天前
Python之Playwright+AI UI自动化测试框架搭建与实战
人工智能·python·ui