unity游戏开发-5-更有序地学习unity:理解对象、组件、脚本

前言

  • 在前面的文章中,我简单写了一个在输入框写文字,点击按钮将文字显示在日志的功能:
    运行界面

  • 虽然功能非常简单,但却涉及了 unity editor 界面中的诸多操作,以及脚本与 GameObject、组件 之间的配合。

  • 如果不能很好地理解这些“元素”之间的关系,那么开发丰富的游戏内容就会变成一场痛苦的“死记硬背”。

  • 因此本篇文章旨在介绍 unity 中,对象、组件、脚本 之间的关系,以便在后续的游戏开发中能够自己设计并实现预期功能。

题外话
得益于大语言模型的发展,我们可以借助 AI 来学习 unity,甚至写代码。
但是,如果没有打好基础、理解原理,我们就没法当好 AI 的 “老板”,保障产品的质量了。
因此,为了让 AI 成为一个 好助手、好员工,系统性地学习仍然十分有必要。

1. 前置知识

本文需要的前置知识主要包括:类、对象、派生、聚合、组合、UML 图、访问控制。
这里仅作简单介绍。

    • 对一类事物的抽象。
    • 举例:假设在一个简单场景里,我们需要计算顾客购买的苹果的花费,这里的苹果就是一个类,在这个场景里可以简单抽象出其属性:重量kg、单价 Yuan/kg。当然 重量是每个苹果都具有的属性,单价是某一类苹果公共的属性。具体的类如何设计还要视实际需要而定。
  • 对象

    • 实际的“物品”,也称:实例
    • 举例:假设我们有了一个 “苹果” 类,它只是抽象的概念,在程序的世界里,我们可以用这个 类 来 实例化出一个对象,也就是一个真正的 “苹果”(在内存中的一段参与运算的数据)。
  • 派生

    • 描述的是 父类(也称基类)与子类(也称派生类)之间的关系,是 is-a 关系。
    • 举例:界门纲目科属种 就是一套典型的类的派生,就好比 植物界 就是基类,某个具体种类的苹果树 就是最末端的 派生类。
  • 聚合、组合

    • 这是类之间的另外两种关系,是 has-a 关系
    • 举例1:茶壶和杯子可以聚合成茶具套装,茶具套装与茶壶之间是整体与局部的关系。在这个关系中,脱离茶具套装,茶壶还是有意义的个体。
    • 举例2:冰箱柜与冰箱门组合成冰箱,冰箱与冰箱门之间是整体与局部的关系。在这个关系中,脱离冰箱,冰箱门不再是一个有意义的个体。
  • UML 图

    • UML 图是程序设计中的重要工具,可以用来表示程序中类之间的关系,比如前面介绍的 派生、聚合、组合,在 UML 图中都有各自的表示方法。
  • 访问控制

    • 访问控制是指,控制 类/对象 内部的 属性(成员变量)、方法(成员函数)可以被谁访问。
    • private 修饰的,仅能被本类的对象访问;
    • public 修饰的可以被所有对象访问;
    • protected 修饰的可以被 本类以及子类对象 访问,不能被其他类型的对象访问

2. 游戏对象、组件、脚本 的关系 ★

2.1 要点

  • GameObject
    GameObject 是用于承载各类功能的基础对象;

  • GameObject & 组件

    • 通过组装各种 组件 到 某个具体的 GameObject 上,可以实现具有复杂功能的游戏对象,这种关系是一种组合关系(组件必须依附在 GameObject 上);
    • 一个 GameObject 下,同类的 组件 只能有一个;
  • 子 GameObject

    • GameObject 下面可以包含另一个 GameObject,一般称为子对象。不过这里并非“派生”,而是聚合关系;
    • 子对象 的 位置(Transform组件中的属性),是相对于父对象的;
  • 组件 的 派生

    • 组件多种多样,以 Component 作为基类,派生出:MonoBehaviour (脚本基类)、Transform (位置、旋转、缩放)、Collider (碰撞检测)、Rigidbody (物理行为)、AudioSource (音频播放)、Animator (动画控制)、Canvas (UI管理) 等等 大量子类;
  • 组件 & 脚本
    脚本用来实现游戏对象的某些行为。通过 MonoBehaviour 组件,将脚本附加到 GameObject 上,可以说:脚本聚合到 MonoBehaviour 组件上,进一步聚合到 GameObject 中。

2.2 图解

下图是一个 GameObject、组件、脚本 关系的示例:
GameObject、组件、脚本 关系示例

3. 以上一篇文章涉及的对象、组件、脚本为例,加深理解

3.1 GameObjects 结构

Hierarchy 界面 看到的是 GameObject 的结构
输入、按钮 UI 的 GameObject

3.2 组件

  • Inspector 界面 看到的是组件
  • 下图是 Button 这个 GameObject 中的前几个组件。
    可以看到其中有一个名为 Button 的组件。
    这个对象所包含的组件 是在创建这个对象时自动提供的“套装”。
    组件示例
  • 下图是 Canvas 这个 GameObject 中的组件
    与上面一个例子类似,创建 Canvas 对象的时候,已经带有了基础的几个组件(脚本组件是后来通过“拖拽”这个动作添加的)
    在这里插入图片描述

3.3 脚本以及成员变量的显示

  • 将脚本“拖拽”到 GameObject 上,会自动给对象添加脚本组件
  • 在 Inspector 界面 显示成员变量
    • 脚本中的类,其 公有成员变量 将在 Inspector 界面中显示
    • [SerializeField]:用于将private或protected变量暴露到Inspector中。
    • [HideInInspector]:用于隐藏public变量,使其不在Inspector中显示。
  • 举例
    • 代码
using UnityEngine;
using UnityEngine.UI;
using TMPro; // 引入TextMeshPro命名空间

public class InputHandler : MonoBehaviour
{
    public TMP_InputField inputField; // 拖拽 InputField 到这个变量
    public Button submitButton;   // 拖拽 Button 到这个变量
    public int testIntParam = 0;
    [HideInInspector] public int testHideParam = 1;
    private int testPrvParam = 2;
    [SerializeField] private int testViewablePrvParam = 3;

    void Start()
    {
        // 添加按钮点击事件
        submitButton.onClick.AddListener(OnSubmit);
    }

    void OnSubmit()
    {
        // 获取输入框的内容
        string inputText = inputField.text;

        // 打印到 Debug 日志
        Debug.Log("Input: " + inputText);
    }
}
  • Inspector 界面 效果
    成员变量在 Inspector 界面显示

附录1 - 常见的组件以及作用

在Unity中,组件(Component)是附加到GameObject上的功能模块,用于扩展其行为和属性。以下是一些常见的组件及其作用:

1. Transform

  • 作用:定义GameObject的位置、旋转和缩放。
  • 属性
    • position:对象在世界空间中的位置。
    • rotation:对象的旋转,通常用Quaternion表示。
    • scale:对象的缩放。
  • 使用场景:几乎所有需要位置、旋转和缩放的对象都需要Transform组件。

2. Rigidbody

  • 作用:为GameObject提供物理行为,如运动、重力、碰撞等。
  • 属性
    • mass:物体的质量。
    • velocity:物体的速度。
    • drag:物体的阻力。
  • 使用场景:需要物理交互的对象,如角色、道具等。

3. Collider

  • 作用:定义GameObject的碰撞检测。
  • 类型
    • BoxCollider:立方体碰撞器。
    • SphereCollider:球体碰撞器。
    • CapsuleCollider:胶囊碰撞器。
    • MeshCollider:基于网格的碰撞器。
  • 属性
    • isTrigger:是否作为触发器。
  • 使用场景:需要碰撞检测的对象,如墙壁、地面、角色等。

4. Renderer

  • 作用:定义GameObject的渲染属性,如材质、阴影等。
  • 属性
    • material:对象的材质。
    • enabled:是否启用渲染。
  • 使用场景:需要渲染的对象,如模型、UI元素等。

5. Animator

  • 作用:控制GameObject的动画。
  • 属性
    • runtimeAnimatorController:动画控制器。
  • 使用场景:需要动画的对象,如角色、UI元素等。

6. AudioSource

  • 作用:为GameObject提供音频播放功能。
  • 属性
    • clip:音频剪辑。
    • volume:音量。
    • loop:是否循环播放。
  • 使用场景:需要播放音频的对象,如背景音乐、音效等。

7. Canvas

  • 作用:管理UI元素,是UI系统的根节点。
  • 属性
    • renderMode:渲染模式(屏幕空间、世界空间等)。
  • 使用场景:需要管理UI元素的场景。

8. RectTransform

  • 作用:定义UI元素的布局和位置。
  • 属性
    • anchoredPosition:锚点位置。
    • sizeDelta:大小偏移。
  • 使用场景:需要布局的UI元素。

9. Button

  • 作用:创建按钮,提供交互逻辑。
  • 属性
    • onClick:点击事件。
  • 使用场景:需要用户交互的UI元素,如菜单按钮。

10. InputField

  • 作用:创建文本输入框,允许用户输入文本。
  • 属性
    • text:输入的文本。
    • placeholder:占位符文本。
  • 使用场景:需要用户输入的UI元素,如表单、对话框。

11. Image

  • 作用:显示图像。
  • 属性
    • sprite:图像资源。
    • color:图像颜色。
  • 使用场景:需要显示图像的UI元素。

12. Text

  • 作用:显示文本。
  • 属性
    • text:显示的文本。
    • fontSize:字体大小。
  • 使用场景:需要显示文本的UI元素。

13. ScriptableObject

  • 作用:用于创建可持久化存储数据的脚本对象,通常用于配置和管理数据。
  • 属性
    • 自定义属性,根据需要定义。
  • 使用场景:需要管理配置数据的场景。

14. MonoBehaviour

  • 作用:所有附加到GameObject的脚本的基类,提供生命周期方法(如StartUpdate等)。
  • 属性
    • 自定义属性,根据需要定义。
  • 使用场景:需要实现游戏逻辑的GameObject

总结

  • Transform:定义位置、旋转和缩放。
  • Rigidbody:提供物理行为。
  • Collider:提供碰撞检测。
  • Renderer:定义渲染属性。
  • Animator:控制动画。
  • AudioSource:提供音频播放功能。
  • Canvas:管理UI元素。
  • RectTransform:定义UI元素的布局和位置。
  • Button:创建按钮,提供交互逻辑。
  • InputField:创建文本输入框,允许用户输入文本。
  • Image:显示图像。
  • Text:显示文本。
  • ScriptableObject:用于管理配置数据。
  • MonoBehaviour:提供生命周期方法。

通过合理使用这些组件,你可以实现复杂的游戏逻辑和功能。

附录2 - MonoBehaviour 常用接口

MonoBehaviour 是 Unity 中所有附加到 GameObject 的脚本的基类,它提供了一系列的生命周期方法和事件回调。这些方法和回调通常被称为“接口”,虽然它们在 C# 的严格意义上并不是接口(interface),但它们的作用类似于接口,提供了特定的功能和行为。

以下是一些常用的 MonoBehaviour 生命周期方法及其作用:

1. Awake

  • 作用:在脚本实例被加载时调用一次。
  • 特点
    • Start 之前调用。
    • 即使脚本被禁用,Awake 也会被调用。
  • 使用场景
    • 初始化静态变量。
    • 设置全局引用。
    • 注册事件监听器。
  • 示例
    void Awake()
    {
        Debug.Log("Awake is called when the script is loaded.");
    }
    

2. Start

  • 作用:在脚本实例被激活后调用一次。
  • 特点
    • 在第一次调用 Update 之前被调用。
    • 如果脚本被禁用,则不会调用 Start
  • 使用场景
    • 初始化变量。
    • 设置初始状态。
    • 执行一次性的逻辑。
  • 示例
    void Start()
    {
        Debug.Log("Start is called when the script is activated.");
    }
    

3. Update

  • 作用:每帧调用一次。
  • 特点
    • 在每一帧的渲染之前被调用。
    • 适合处理需要每帧更新的逻辑。
  • 使用场景
    • 处理用户输入(如键盘、鼠标、触摸等)。
    • 更新游戏逻辑(如移动物体、碰撞检测等)。
    • 动态更新 UI。
  • 示例
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            Debug.Log("Space bar is pressed.");
        }
    }
    

4. FixedUpdate

  • 作用:在固定的物理更新周期调用。
  • 特点
    • 与物理系统同步,适合处理物理相关的逻辑。
    • 调用频率与 Update 不同,通常用于处理物理碰撞、刚体运动等。
  • 使用场景
    • 更新物理组件(如 Rigidbody)。
    • 处理物理碰撞。
  • 示例
    void FixedUpdate()
    {
        Debug.Log("FixedUpdate is called at a fixed interval.");
    }
    

5. LateUpdate

  • 作用:在所有 Update 方法调用后调用。
  • 特点
    • 每帧调用一次,但比 Update 稍晚。
    • 适合处理需要在所有 Update 方法之后执行的逻辑。
  • 使用场景
    • 跟随相机或其他需要在所有更新后处理的对象。
  • 示例
    void LateUpdate()
    {
        Debug.Log("LateUpdate is called after all Update methods.");
    }
    

6. OnEnable

  • 作用:在脚本实例被激活时调用。
  • 特点
    • 每次脚本被启用时都会调用。
    • 如果脚本被禁用后再次启用,也会调用。
  • 使用场景
    • 设置脚本被启用时的初始状态。
    • 注册事件监听器。
  • 示例
    void OnEnable()
    {
        Debug.Log("OnEnable is called when the script is enabled.");
    }
    

7. OnDisable

  • 作用:在脚本实例被禁用时调用。
  • 特点
    • 每次脚本被禁用时都会调用。
    • 如果脚本被销毁,也会调用。
  • 使用场景
    • 清理资源。
    • 注销事件监听器。
  • 示例
    void OnDisable()
    {
        Debug.Log("OnDisable is called when the script is disabled.");
    }
    

8. OnDestroy

  • 作用:在脚本实例被销毁时调用。
  • 特点
    • 只在脚本被销毁时调用一次。
  • 使用场景
    • 清理资源。
    • 释放内存。
  • 示例
    void OnDestroy()
    {
        Debug.Log("OnDestroy is called when the script is destroyed.");
    }
    

9. OnApplicationQuit

  • 作用:在应用程序退出时调用。
  • 特点
    • 在 Unity 编辑器中,只有在播放模式下退出时才会调用。
    • 在构建的游戏中,会在关闭游戏时调用。
  • 使用场景
    • 保存数据。
    • 清理资源。
  • 示例
    void OnApplicationQuit()
    {
        Debug.Log("OnApplicationQuit is called when the application is about to quit.");
    }
    

10. OnTriggerEnter / OnTriggerExit / OnTriggerStay

  • 作用:处理碰撞器的触发事件。
  • 特点
    • OnTriggerEnter:当其他碰撞器进入触发器时调用。
    • OnTriggerExit:当其他碰撞器离开触发器时调用。
    • OnTriggerStay:当其他碰撞器在触发器内时每帧调用。
  • 使用场景
    • 实现触发区域的逻辑(如进入区域时播放音效、显示提示等)。
  • 示例
    void OnTriggerEnter(Collider other)
    {
        Debug.Log("Trigger entered by " + other.gameObject.name);
    }
    
    void OnTriggerExit(Collider other)
    {
        Debug.Log("Trigger exited by " + other.gameObject.name);
    }
    
    void OnTriggerStay(Collider other)
    {
        Debug.Log("Trigger stayed by " + other.gameObject.name);
    }
    

11. OnCollisionEnter / OnCollisionExit / OnCollisionStay

  • 作用:处理碰撞事件。
  • 特点
    • OnCollisionEnter:当发生碰撞时调用。
    • OnCollisionExit:当碰撞结束时调用。
    • OnCollisionStay:当碰撞持续时每帧调用。
  • 使用场景
    • 实现碰撞逻辑(如反弹、播放音效等)。
  • 示例
    void OnCollisionEnter(Collision collision)
    {
        Debug.Log("Collision detected with " + collision.gameObject.name);
    }
    
    void OnCollisionExit(Collision collision)
    {
        Debug.Log("Collision ended with " + collision.gameObject.name);
    }
    
    void OnCollisionStay(Collision collision)
    {
        Debug.Log("Collision staying with " + collision.gameObject.name);
    }
    

总结

MonoBehaviour 提供了一系列的生命周期方法和事件回调,这些方法在特定的时刻被 Unity 自动调用。通过合理使用这些方法,你可以实现复杂的游戏逻辑和行为。以下是一些常用的生命周期方法及其作用:

  • Awake:初始化静态变量和全局引用。
  • Start:初始化变量和设置初始状态。
  • Update:每帧更新游戏逻辑。
  • FixedUpdate:处理物理更新。
  • LateUpdate:在所有 Update 方法之后处理。
  • OnEnable:脚本被启用时调用。
  • OnDisable:脚本被禁用时调用。
  • OnDestroy:脚本被销毁时调用。
  • OnApplicationQuit:应用程序退出时调用。
  • OnTriggerEnter/Exit/Stay:处理触发器事件。
  • OnCollisionEnter/Exit/Stay:处理碰撞事件。

通过理解这些方法的作用和调用顺序,你可以更好地组织和管理你的脚本逻辑。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值