- Unity 教程
- Unity - 首页
- Unity - 简介
- Unity - 安装和设置
- Unity - 创建精灵
- Unity - 修改精灵
- 变换和对象父子关系
- Unity - 内部资源
- Unity - 保存和加载场景
- Unity - 基本移动脚本
- Unity - 理解碰撞
- Unity - 刚体和物理
- Unity - 自定义碰撞边界
- 理解预制件和实例化
- Unity - 游戏对象销毁
- Unity - 协程
- Unity - 控制台
- Unity - 音频入门
- Unity - UI入门
- Unity - 按钮
- Unity - 文本元素
- Unity - 滑块
- Unity - 材质和着色器
- Unity - 粒子系统
- Unity - 使用资源商店
- Unity 有用资源
- Unity 快速指南
- Unity - 有用资源
- Unity - 讨论
Unity 快速指南
Unity - 简介
Unity 是一个跨平台游戏引擎,最初于 2005 年由Unity Technologies发布。Unity 的重点在于开发 2D 和 3D 游戏以及交互式内容。Unity 目前支持超过20个不同的目标平台进行部署,而其最受欢迎的平台是 PC、Android 和 iOS 系统。
Unity 提供了一个完整的工具包,用于设计和构建游戏,包括图形、音频和关卡构建工具的界面,只需很少使用外部程序即可处理项目。
在本系列中,我们将:
- 学习如何使用 Unity 的各种基础知识
- 了解引擎的工作原理
- 理解游戏设计的基本概念
- 创建和构建实际的示例游戏
- 学习如何将您的项目部署到市场
现在让我们开始吧。
Unity - 安装和设置
要使用 Unity 创建内容,主要要求是下载 Unity 引擎和开发环境。除了核心引擎外,您还可以下载可选的模块以部署到各种不同的平台,以及用于将 Unity 脚本集成到 Visual Studio 的工具。
要安装 Unity,请访问此处访问后,点击:
选择您的 Unity + 下载。
在下一页上,点击个人版下方的立即试用按钮。这是 Unity 的免费版本,包含所有核心功能。在我们开始本系列时,最好先学习如何使用引擎,然后再考虑购买Plus版或Pro版。
在下一页上,向下滚动并点击确认您或您的公司年收入不超过 100,000 美元。如果您超过此金额,则不允许试用 Unity 免费版,但您可以注册免费试用 Pro 版 30 天。
接下来,点击您希望安装 Unity 的平台。在本系列中,我们将使用引擎的Windows版本。也可以在Ubuntu和一些其他 Linux 系统上安装 Unity,更多信息请参见此处
还强烈建议您安装最新版本的Visual Studio,它比 Unity 附带的标准 MonoDevelop IDE 提供了许多有用的工具。
下载安装程序后,按照提示操作,直到您到达一个菜单,可以选择要与 Unity 一起安装的组件。
在这里,选择您需要的组件。在本系列中,我们希望安装图像中显示的组件。此选择包括引擎本身、引擎文档、一个 IDE;Android 的构建工具以及以后可以添加到项目中的一组资源。
点击下一步,按照说明和选项操作,让 Unity 将自身下载并安装到您的计算机上。
打开 Unity,在下一课中,我们将创建我们的第一个项目。
创建您的第一个项目
Unity 同样适用于 2D 和 3D 游戏。所有在 Unity 中制作的游戏都从启动屏幕中的项目开始。
打开您新安装的 Unity 副本;将出现如下所示的屏幕:
您现有的项目将显示在上述图像中的模糊区域中。
在窗口的右上角,您将看到新建图标,如上所示。单击该图标后,您将看到项目设置屏幕。
在这里,您可以为项目命名,设置保存位置,设置项目类型并添加现有资源。
现在,让我们将第一个项目命名为“Hello World!”并将其设置为2D模式。
单击创建项目,让 Unity 设置项目的核心文件。这可能需要一些时间,具体取决于您的计算机速度、预添加的资源和项目类型。
了解引擎
创建新项目并打开 Unity 后,将出现以下屏幕:
让我们快速浏览一下此窗口中可见的内容。目前,我们关注四个主要区域:
在这个窗口中,我们将构建我们的场景。场景是游戏发生的所有内容的关卡。如果您单击小的游戏选项卡,您可以看到游戏对玩家的外观预览窗口。目前,它应该是一个简单的蓝色背景。
此区域是检查器。目前它是空的,因为我们的场景中没有任何对象。我们稍后将看到如何使用检查器。
此窗口是场景层次结构。它列出了您当前打开的场景中的所有对象,以及它们父子层次结构。
最后,此区域是项目资源窗口。当前项目中的所有资源都存储并保存在此处。所有外部导入的资源,例如纹理、字体和声音文件,也保存在此处,然后才能在场景中使用。
在下一课中,我们将讨论 Unity 中游戏的工作流程和工作方式。
Unity 如何工作?
在 Unity 中,所有游戏玩法都在场景中进行。场景是游戏的所有方面(例如游戏关卡、标题屏幕、菜单和过场动画)发生的位置。
默认情况下,Unity 中的新场景将包含场景中名为主摄像机的摄像机对象。可以向场景中添加多个摄像机,但目前我们只处理主摄像机。
主摄像机渲染它在称为视口的特定区域中看到或“捕捉”的所有内容。进入此区域的所有内容都将对玩家可见。
您可以通过将鼠标放在场景视图内并向下滚动以缩小场景视图来看到此视口作为灰色矩形。(您也可以通过按住 Alt 键并右键拖动来实现)。
场景本身是由称为游戏对象的对象组成的。游戏对象可以是任何东西,从玩家模型到屏幕上的 GUI,从按钮和敌人到无形的“管理器”,例如声音源。
游戏对象有一组附加在其上的组件,这些组件描述了它们在场景中的行为方式,以及它们如何对场景中的其他对象做出反应。
事实上,我们现在就可以探索这一点。单击场景层次结构中的主摄像机,然后查看检查器。它现在不会为空;相反,它将包含一系列“模块”。
任何游戏对象最重要的组件都是它的变换组件。场景中存在的任何对象都将有一个变换,它定义了它相对于游戏世界或任何父对象的位置、旋转和缩放。
可以通过单击添加组件并选择所需的组件来将其他组件附加到对象。在我们后续的课程中,我们还将向游戏对象附加脚本,以便我们可以赋予它们编程行为。
现在让我们考虑一些组件的示例:
渲染器 - 负责渲染和使对象可见。
碰撞器 - 定义对象的物理碰撞边界。
刚体 - 为对象提供实时物理属性,例如重量和重力。
音频源 - 为对象提供播放和存储声音的属性。
音频监听器 - 实际“听到”音频并将其输出到玩家扬声器的组件。默认情况下,主摄像机中存在一个。
动画器 - 为对象提供对动画系统的访问权限。
灯光 - 使对象充当光源,具有各种不同的效果。
在此图表中,我们可以看到 Unity 如何通过游戏对象将自身组合成场景。
在下一课中,我们将创建我们的第一个游戏对象并深入研究脚本。
Unity - 创建精灵
精灵是简单的 2D 对象,上面有图形图像(称为纹理)。当引擎处于 2D 模式时,Unity 默认使用精灵。在 3D 空间中查看时,精灵看起来像薄纸一样,因为它们没有 Z 宽度。
除非在 3D 空间中旋转,否则精灵始终以垂直角度面向摄像机。
每当 Unity 创建一个新的精灵时,它都会使用纹理。然后将此纹理应用于新的游戏对象,并附加精灵渲染器组件。这使得我们的游戏对象可以使用我们的纹理可见,并为其提供与屏幕上外观相关的属性。
要在 Unity 中创建精灵,我们必须为引擎提供纹理。
让我们先创建纹理。获取您想要使用的标准图像文件(例如 PNG 或 JPG),将其保存,然后将图像拖到 Unity 的资源区域。
接下来,将图像从资源拖到场景层次结构。您会注意到,一旦松开鼠标按钮,列表中就会出现一个带有纹理名称的新游戏对象。您现在还将在场景视图中看到屏幕中间的图像。
在创建精灵时,让我们考虑以下几点:
通过将外部资源拖到 Unity 中,我们正在添加一个资源。
此资源是一个图像,因此它变成了一个纹理。
通过将此纹理拖入场景层次结构中,我们创建了一个与纹理同名的新游戏对象,并附加了一个Sprite Renderer。
此 Sprite Renderer 使用该纹理在游戏中绘制图像。
我们现在在场景中创建了一个精灵。
在下一课中,我们将了解我们拥有的精灵的一些修改器。
Unity - 修改精灵
我们刚刚导入的精灵也可以通过各种方式进行操作以更改其外观。
如果您查看引擎界面的左上角,您会发现如下所示的工具栏:
让我们讨论一下这些按钮的功能。
手型工具用于在场景中移动而不影响任何对象。
接下来,我们有移动工具。它用于在游戏世界中移动对象。
在中央,我们有旋转工具,用于沿游戏世界(或父对象)的 Z 轴旋转对象。
缩放工具位于上方。此工具允许您沿特定轴修改对象的尺寸(比例)。
最后,我们有矩形工具。此工具的行为类似于移动工具和缩放工具的组合,但容易损失精度。它在排列 UI 元素方面更有用。
随着项目复杂性的增加,这些工具将证明其价值。
Unity - 变换和对象父子关系
在我们刚开始的时候,我们讨论了 gameObject 的变换可以说是其最重要的组件。让我们在本章中详细讨论这个组件。此外,我们还将学习对象父子关系的概念。
变换具有三个可见属性:位置、旋转和缩放。每个属性都有三个值,分别对应三个轴。2D 游戏在定位时通常不关注 Z 轴。Z 轴在 2D 游戏中最常见的用途是创建视差。
旋转属性定义对象相对于游戏世界或父对象围绕该轴旋转的角度(以度为单位)。
对象的缩放比例定义了它与原始大小或本机大小相比的大小。例如,让我们取一个尺寸为 2x2 的正方形。如果这个正方形沿 X 轴缩放 3 倍,沿 Y 轴缩放 2 倍,我们将得到一个 6x4 的正方形。
在我们接下来的章节中,我们将讨论对象父子关系是什么。
什么是对象父子关系?
在 Unity 中,对象遵循层次结构系统。使用此系统,游戏对象可以成为其他游戏对象的“父对象”。
当一个游戏对象具有父对象时,它将相对于另一个游戏对象而不是游戏世界执行所有变换更改。
例如,一个没有父对象的对象放置在 (10, 0, 0) 将距离游戏世界的中心 10 个单位。
但是,一个具有父对象的游戏对象放置在 (10, 0, 0) 将认为父对象的当前位置为中心。
游戏对象可以通过简单的拖放操作将其拖放到所需的父对象上进行父子关系设置。“子”对象在对象列表中用一个小缩进以及父对象旁边的箭头表示。
为游戏对象设置父子关系有很多用途。例如,坦克的所有不同部件都可以是单独的游戏对象,并放在名为“tank”的单个游戏对象下。这样,当这个“tank”父游戏对象移动时,所有部件都会随之移动,因为它们的位置会根据其父对象不断更新。
在我们接下来的课程中,我们将讨论内部资源。我们还将学习如何在项目中创建和管理资源。
Unity - 内部资源
除了从其他程序导入的外部资源(如音频文件、图像、3D 模型等)之外,Unity 还提供内部资源的创建。这些资源是在 Unity 本身创建的,因此不需要任何外部程序来创建或修改。
一些重要的内部资源示例如下所示:
场景 - 它们充当“关卡”。
动画 - 它们包含 gameObject 动画的数据。
材质 - 它们定义光照如何影响对象的外观。
脚本 - 为游戏对象编写的代码。
预制件 - 它们充当游戏对象的“蓝图”,以便可以在运行时生成它们。
其他一些重要的资源是占位符、精灵和模型。当您需要快速占位符时使用它们,以便以后可以替换为合适的图形和模型。
要创建内部资源,请右键单击“资源”文件夹,然后转到创建。
在此示例中,我们将创建一个三角形和一个正方形。
将鼠标悬停在精灵选项上,然后单击三角形。
对正方形重复此过程,您应该有两个新的图形资源。
随着我们的学习进度,我们将探索更多这些内部资源,因为它们对于构建合适的游戏至关重要。
Unity - 保存和加载场景
在一天结束时,当您完成相当多的工作后,您需要保存您的进度。在 Unity 中,按 Ctrl + S 不会直接保存您的项目。
Unity 中的一切都发生在场景中。保存和加载也是如此;您必须将当前工作保存为场景(.unity 扩展名)到您的资源中。
让我们试一试。如果我们按 Ctrl + S 并为我们的场景命名,我们将在我们的资源区域中看到一个新的资源。这就是场景文件。
现在,让我们尝试创建一个新场景。为此,请右键单击“资源”,然后转到“创建”→“场景”。为您的新场景命名并按 Enter。
在编辑器模式下(游戏未运行时),可以通过双击场景将其加载到编辑器中。加载当前场景有未保存更改的场景会提示您保存或放弃更改。
您的第一个脚本
导入图像并让它们在游戏中保持静止并不会让您有所成就。它可能可以做一个漂亮的相框,但不是游戏。
脚本编写对于在 Unity 中制作游戏至关重要。脚本编写是编写代码块的过程,这些代码块像组件一样附加到场景中的游戏对象。脚本编写是您可以使用的最强大的工具之一,它可以成就或毁掉一个优秀的游戏。
Unity 中的脚本编写是通过 C# 或 Unity 的 JavaScript 实现(称为 UnityScript)完成的(但是,随着 2018 周期的到来,UnityScript 现在正处于弃用阶段,因此建议不要使用它)。在本系列中,我们将使用 C#。
要创建新脚本,请右键单击您的资源,然后转到创建→C# 脚本。您也可以使用引擎顶部工具栏中的资源选项卡。
创建新脚本后,应该会显示一个新的资源。目前,请保留其名称不变,然后双击它。您的默认 IDE 应该会打开连同脚本一起打开。让我们看看它到底是什么。
using System.Collections; using System.Collections.Generic; using UnityEngine; public class NewBehaviourScript : MonoBehaviour { // Use this for initialization void Start() { } // Update is called once per frame void Update() { } }
您将看到您的脚本名称作为从MonoBehaviour派生的类。什么是 MonoBehaviour?它是一个庞大的类和方法库。它帮助 Unity 中的所有脚本以某种方式派生出来。您在 Unity 中编写的脚本越多,您就会越意识到 MonoBehaviour 的实际用途。
在继续之前,我们有两个没有返回类型的私有脚本,即Start和Update方法。Start方法在使用此方法的游戏对象在场景中处于活动状态的第一个帧中只运行一次。
Update方法在 Start 方法之后的游戏的每一帧中运行。通常,Unity 中的游戏以 60 FPS 或每秒帧数运行,这意味着在对象处于活动状态时,Update方法每秒调用 60 次。
Unity 脚本允许您利用 MonoBehaviour 类的全部功能,以及核心 C# 功能,例如泛型集合、lambda 表达式和 XML 解析,仅举几例。在下一课中,我们将编写我们的第一段代码!
Unity - 基本移动脚本
在本课中,我们将编写使游戏对象根据用户的输入向上、向下、向左和向右移动的代码。这应该有助于我们更容易地理解 Unity 脚本的工作流程。
请记住,每个游戏对象至少有一个组件:变换。特别之处在于,gameObject 的变换也显示为 Unity 脚本端的变量,因此我们可以通过代码修改它。这也不限于变换;Unity 中的所有组件都具有属性,这些属性可以通过脚本中的变量访问。
让我们从我们的移动脚本开始。创建一个新脚本,并将其命名为“Movement”。
现在,打开脚本,您应该会看到上一课中看到的内容。
让我们创建一个名为speed的公共浮点型变量。在 Unity 中将变量设为公共具有很大的优势:
该变量显示为编辑器内的可修改字段,因此您不必手动调整代码中的值。
public class Movement : MonoBehaviour { public float speed; }
如果我们在不触及其他方法的情况下保存此脚本,它应该会在 Unity 中编译。
(您可以通过右下角的图标看到它何时正在编译。)
接下来,将脚本从资源拖放到游戏对象上。如果操作正确,您应该在游戏对象的属性中看到以下内容:
由于速度值是可调整的,并且不必一直更改代码,因此我们可以使用 update() 方法而不是 start()。
现在让我们考虑 Update 方法的目标:
检查用户输入。
如果有用户输入,请读取输入方向。
根据对象的速度和方向更改对象变换的位置值。为此,我们将添加以下代码:
void Update() { float h = Input.GetAxisRaw(“Horizontal”); float v = Input.GetAxisRaw(“Vertical”); gameObject.transform.position = new Vector2 (transform.position.x + (h * speed), transform.position.y + (v * speed));
现在让我们简要讨论一下代码。
首先,我们创建一个名为h(水平)的浮点型变量,其值由Input.GetAxisRaw方法给出。此方法根据玩家在向上/向下/向左/向右箭头键上按下的键返回 -1、0 或 1。
Input 类负责以按键、鼠标输入、控制器输入等形式获取用户的输入。GetAxisRaw 方法稍微难以理解,因此我们稍后再讨论。
接下来,我们正在将 gameObject 的位置更新为一个新位置,该位置通过创建一个新的Vector2来定义。Vector2 接受 2 个参数,它们分别是x 和 y值。对于 x 值,我们提供对象当前位置及其速度的总和,有效地在按下键的每一帧向其位置添加一些数量。
保存此脚本并返回 Unity。Unity 将在成功编译后自动更新所有脚本,因此您不必一遍又一遍地重新附加脚本。
现在您完成了,请将游戏对象属性中的speed值更改为 0.8。这很重要,因为较高的值会使玩家移动速度过快。
现在,单击播放并查看您的第一个小型游戏!
尝试按下箭头键并四处移动。要停止游戏,只需再次单击播放。您甚至可以实时调整速度,因此不必一直停止和启动它。
下一课我们将学习刚体和碰撞。
Unity - 理解碰撞
Unity中的碰撞与精灵本身是分开的,作为单独的组件附加,并单独计算。现在让我们学习其背后的原因。
游戏中的所有东西都是GameObject。即使构成关卡的单个瓦片本身也是GameObjects。
当我们将每个组件都视为一个GameObject时,我们会意识到场景中可能存在数千个GameObject,它们以某种方式相互作用。您可以想象,如果Unity为每个GameObject都添加碰撞,那么引擎为每个GameObject计算碰撞将是不可行的。
我们将添加一个简单的“墙壁”,我们的玩家角色可以与之碰撞。为此,创建一个另一个精灵并使用矩形工具将其放大。我们还将通过精灵渲染器组件中的颜色属性为其赋予红色。
现在,转到检查器中的添加组件,然后输入“Box Collider 2D”。单击显示的第一个组件,应该会出现一个新组件。
您将在GameObject的周边看到一条亮绿色的线。这就是碰撞边界。它定义了可碰撞对象的实际形状。
对我们的可移动GameObject也重复相同的操作。
当然,Unity中的碰撞不仅仅限于简单的方框。它们可以具有各种形状和大小,并且不一定是对象参数的复制品。
它们也可以采用多边形形状。
开发人员和设计师在碰撞边界中使用近似形状来简化碰撞器并避免引擎进行不必要的计算的情况并不少见。我们很快就会学习如何创建不同形状和大小的碰撞器。
现在我们已经设置了碰撞边界,点击播放并查看其运行效果。
你会注意到我们的可移动物体行为不正常。我们将在下一章讨论物体的行为。
Unity - 刚体和物理
上一章碰撞的主要问题在于代码。我们现在将直接修改GameObject的位置值。如果玩家按下按键,我们只是向位置添加一个值。我们需要一种方法让玩家以正确的方式移动,使其对边界和其他GameObject做出正确的反应。
为此,我们需要了解什么是刚体。刚体是可以对实时物理做出反应的GameObject的组件。这包括对力和重力、质量、阻力和动量的反应。
您可以通过单击添加组件并在搜索字段中输入Rigidbody2D来将Rigidbody附加到您的GameObject。
单击Rigidbody2D会将组件附加到您的GameObject。附加后,您会注意到许多新字段已打开。
使用默认设置,由于重力,GameObject会垂直向下落下。为避免这种情况,请将重力缩放设置为0。
现在,游戏播放不会显示任何可见的差异,因为GameObject尚未与其物理组件有任何关联。
为了解决我们的问题,让我们再次打开我们的代码并重写它。
public class Movement : MonoBehaviour { public float speed; public Rigidbody2D body; // Update is called once per frame void Update() { float h = Input.GetAxisRaw(“Horizontal”); float v = Input.GetAxisRaw(“Vertical”); body.velocity = new Vector2(h * speed, v * speed); } }
我们可以看到我们在声明中创建了对Rigidbody2D的引用,并且我们的更新代码作用于该引用而不是对象的变换。这意味着Rigidbody现在已被赋予移动的责任。
您可能期望body引用会抛出NullReferenceException,因为我们没有为其分配任何内容。如果您按原样编译并运行游戏,您将在编辑器的左下方收到以下错误
为了解决这个问题,让我们考虑一下脚本创建的组件。请记住,公共属性会在Unity中创建它们自己的字段,就像我们对速度变量所做的那样。
将速度调整到更高的值,大约5,然后播放游戏。
您的碰撞现在将正常工作!
Unity - 自定义碰撞边界
在本章中,让我们学习自定义碰撞边界。我们还将学习如何调整碰撞器的大小和形状。
让我们从Box Collider开始。Box Collider (2D) 有4个可调整的边,形状像矩形。在碰撞器的组件中,单击此框 -
您将看到碰撞器上显示4个“手柄”。您可以拖动这些手柄来调整它们的大小。
对于简单的形状,Unity也会检测碰撞器形状的最佳匹配,前提是您选择了正确的形状。例如,在圆形精灵上选择圆形碰撞器会将其与半径匹配。
对于更复杂的形状,Unity将尝试创建最简单但最精细的碰撞器形状。为此,您需要使用Polygon Collider 2D。
尝试单击“编辑碰撞器”按钮并尝试调整碰撞器。
理解预制件和实例化
在游戏过程中,实例化和销毁对象被认为非常重要。实例化仅仅意味着创造。物品出现在游戏中或“生成”,敌人死亡,GUI元素消失,场景不断加载。了解如何正确地摆脱不需要的对象以及如何引入您需要的对象变得更加重要。
让我们首先了解什么是预制件。预制件被认为是理解Unity中实例化工作方式的重要因素。
预制件就像GameObject的蓝图。预制件在某种程度上是GameObject的副本,即使在创建场景时不存在,也可以复制并放入场景中;换句话说,预制件可用于动态生成GameObject。
要创建预制件,您只需将所需的GameObject从场景层次结构拖动到项目资源中。
现在,要实例化一个GameObject,我们在脚本中调用Instantiate()方法。此方法在MonoBehaviour中定义,它接受一个GameObject作为参数,因此它知道要创建/复制哪个GameObject。它还具有各种重载,用于更改新实例化对象的变换以及父对象。
让我们尝试在按下空格键时实例化一个新的六边形。
创建一个名为Instantiator的新脚本并打开它。在Update方法中,键入下面给出的代码。
在这里,我们使用Input类的GetKeyDown方法来检查玩家在上一个帧期间是否按下了特定按钮。由于我们希望它继续检查,因此我们将其放在Update中,该方法每秒运行60次。如果KeyCode枚举(列出了标准键盘上所有可能的键)指定的键在该帧中被按下,则GetKeyDown方法返回true。
public class Instantiator : MonoBehaviour { public GameObject Hexagon; // Update is called once per frame void Update () { if (Input.GetKeyDown(KeyCode.Space)) { Instantiate(Hexagon); } } }
顶部的公共GameObject声明创建了一个类似于我们之前在Rigidbody2D中创建的插槽。但是,此插槽仅接受预制件(在编辑器时间)和游戏对象(在运行时)。
保存脚本并让它编译。完成后,通过转到对象层次结构右键单击菜单并选择创建空对象来创建一个新的空GameObject。
将此对象命名为可识别的名称,例如Instatiator Object,并将我们新创建的脚本附加到它。在GameObject显示的插槽中,拖入我们创建的预制件。
如果我们现在运行游戏,按下空格键将创建一个与我们用来创建预制件的六边形对象相同的新六边形对象。您可以看到在对象层次结构中创建的每个六边形。您看不到它们出现在游戏中的原因是,目前,它们都是精确地一个叠加在一个之上。
在下一课中,我们将了解对象销毁的概念。
Unity - 游戏对象销毁
GameObject的销毁与实例化一样重要。在本章中,我们将学习如何销毁GameObject。
幸运的是,销毁GameObject与创建它们一样容易。您只需要对要销毁的对象进行引用,并使用此引用作为参数调用Destroy()方法。
现在,让我们尝试制作5个六边形,这些六边形将在按下指定的键时自行销毁。
让我们创建一个名为HexagonDestroyer的新脚本,并在Visual Studio中打开它。我们将首先创建一个公共KeyCode变量。KeyCode用于指定标准键盘上的键,其方法中的Input类使用它。通过像之前对Rigidbody和预制件一样将此变量设为公共变量,我们可以通过编辑器访问它。当变量设为公共变量时,我们不需要将“KeyCode.A”等值硬编码到代码中。代码可以根据我们的需要灵活地使用任意数量的对象。
public class HexagonDestroyer : MonoBehaviour { public KeyCode keyToDestroy; // Update is called once per frame void Update () { if (Input.GetKeyDown(keyToDestroy)) { Destroy (gameObject); } } }
观察我们如何在方法中使用名为“gameObject”(小写g,大写O)的变量。这个新的gameObject变量(类型为GameObject)用于引用附加此脚本的gameObject。如果您在多个对象上附加此脚本,则每当涉及此变量时,它们都会以相同的方式做出反应。
但是,不要混淆两者。
带有大写G和O的GameObject是一个类,它包含所有GameObject并提供标准方法,例如Instantiate、Destroy和获取组件的方法。
带有小写g和大写O的gameObject是GameObject的特定实例,用于引用当前附加此脚本的gameObject。
现在让我们编译我们的代码,然后返回Unity。
现在,我们将创建一个新的六边形精灵,并将我们的脚本附加到它。接下来,右键单击层次结构中的gameObject并选择复制。一个新的精灵在层次结构中创建;您应该使用移动工具重新定位它。重复这些步骤以创建类似的六边形。
单击每个六边形并查看其脚本组件。您现在可以设置各个键,以便在按下该键时GameObject自行销毁。例如,让我们创建5个六边形,并设置它们在按下A、S、D、F和G键时销毁。
您可以将相同的键设置为多个六边形,它们都将在按下键时同时销毁自身;这是使用gameObject引用的示例,您可以使用它来引用使用脚本的单个对象,而无需单独设置它们。
您可以将相同的键设置为多个六边形,它们都将在按下键时同时销毁自身;这是使用gameObject引用的示例,您可以使用它来引用使用脚本的单个对象,而无需单独设置它们。
重要的是要理解,销毁一个GameObject并不意味着一个对象会破碎或爆炸。销毁一个对象只会(并且立即)使其在游戏(及其代码)中停止存在。到此对象的链接及其引用现在已断开,尝试访问或使用它们中的任何一个通常会导致错误和崩溃。
Unity - 协程
协程是制作 Unity 游戏时最有效的工具之一。让我们来看下面的代码行,来了解协程到底是什么。
IEnumerator MyCoroutineMethod() { // Your code here… yield return null; }
通常,如果你在 Unity(或者 C#)中调用一个函数,该函数将从头到尾运行。就你的代码而言,这就是你认为的“正常”行为。但是,有时我们想故意减慢函数的速度,或者让它等待的时间超过它运行的几毫秒。协程可以做到这一点:协程是一个能够等待和计时其进程,以及完全暂停其进程的函数。
让我们来看一个例子来理解协程是如何工作的。假设我们想要制作一个正方形,使其颜色在 1 秒的间隔内在红色和蓝色之间变化。
首先,我们创建一个精灵。接下来,创建一个新的脚本,并将其命名为ColorChanger。在这个脚本中,我们获取精灵的Sprite Renderer的引用。但是,我们将使用不同的方法来获取组件。我们不再像以前那样将组件拖放到插槽中,而是让代码自行检测组件。
这是通过GetComponent方法实现的,该方法返回它检测到的第一个匹配组件。由于我们每个对象只使用一个 Sprite Renderer,因此我们可以使用此方法自动检测并获取每次渲染器的引用。
请记住,渲染器负责使精灵实际显示在屏幕上。渲染器具有一个color属性,该属性影响精灵的全局颜色;这是要修改的值。将Color值设置为公共值将允许我们通过操作系统默认的颜色拾取程序在编辑器中选择它们。
private SpriteRenderer sr; public Color color1; public Color color2; void Start () { sr = GetComponent<SpriteRenderer>(); StartCoroutine(ChangeColor()); } IEnumerator ChangeColor() { while (true) { if (sr.color == color1) sr.color = color2; else sr.color = color1; yield return new WaitForSeconds(3); } }
现在,我们将协程函数放入 while 循环中。
要在 C# 中创建协程,我们只需创建一个返回IEnumerator的方法。它还需要一个yield return语句。yield return 语句是特殊的;它实际上告诉 Unity 暂停脚本并在下一帧继续。
有很多方法可以使用 yield return;其中一种是创建一个WaitForSeconds类的实例。这使得协程在继续之前等待一定数量的真实世界秒数。
让我们编译代码并返回 Unity。我们将简单地选择交替颜色,然后点击播放。我们的对象现在应该以 3 秒的间隔在两种颜色之间切换。你也可以将间隔设置为公共变量,并调整颜色变化的频率。
协程广泛用于计时方法,就像我们刚才做的那样。各种WaitForX方法有其自身的用途。协程还用于运行“旁侧”进程,这些进程在游戏同时运行时自行运行。例如,这对于在玩家从一个点开始时加载大型关卡的屏幕外部分非常有用。
Unity - 控制台
控制台是我们读取开发者输出的地方。这些输出可用于快速测试代码片段,而无需为测试添加额外功能。
默认控制台中会出现三种类型的消息。这些消息可能与大多数编译器标准相关——
- 错误
- 警告
- 消息
错误
错误是会阻止代码完全运行的问题或异常。
警告
警告是不会阻止代码运行的问题,但可能会在运行时造成问题。
消息
消息是向用户传达信息的输出;它们通常不会突出显示问题。
我们甚至可以让控制台输出我们自己的消息、警告和错误。为此,我们将使用 Debug 类。Debug类是 MonoBehaviour 的一部分,它为我们提供了向控制台写入消息的方法,这与你在入门程序中创建普通输出消息的方式非常相似。
你可以在 Assets 区域上方的标签页中找到控制台。
控制台的输出对程序员更有用,而不是最终用户或玩家。
让我们尝试向控制台写入一条简单的消息。这将通知我们何时按下空格键。为此,我们将使用Log方法,该方法接收一个Object作为参数,我们将使用字符串。
你可以从一个新的脚本开始,或者修改现有的脚本。
void Update() { if (Input.GetKeyDown(KeyCode.Space)) Debug.Log(“Space key was pressed!”); }
保存、编译并运行此代码(当然,将其附加到 GameObject),尝试按下空格键。
注意——请注意,消息显示在编辑器的底部。
如果你单击控制台选项卡,你将看到打印出的消息。
同样,你也可以使用LogWarning方法输出警告,使用LogError方法输出错误。正如你稍后将看到的那样,这将证明对测试小的代码片段非常有用,而无需实际实现它们。
Unity - 音频入门
游戏强调音频是有原因的;它对于增加游戏的审美价值至关重要。从最早的Pong开始,人们就能听到球交替击中球拍时发出的哔哔声。当时这是一个非常简单的短方波样本,但是对于所有电子游戏的鼻祖来说,你还想要什么呢?
在现实生活中,许多因素会影响你感知声音的方式;物体的速度、它所在的场景类型以及它来自哪个方向。
有很多因素会导致我们的引擎产生不必要的负载。相反,我们尝试创建一个关于我们的声音在游戏中如何工作的想法,并围绕它进行构建。这在 3D 游戏中尤其突出,因为有 3 个轴需要处理。
在 Unity 中,我们有专门用于音频感知和播放的组件。这些组件协同工作,创建了一个对游戏来说感觉自然的可信的声音系统。
Unity 为我们提供了一系列有用的工具和效果,例如混响、多普勒效应、实时混音和效果等等。我们将在后续章节中学习这些内容。
音频组件
在本节中,我们将学习与 Unity 中的音频相关的 3 个主要组件。
AudioSource
AudioSource 组件是主要组件,你将其附加到 GameObject 以使其播放声音。当通过混音器、代码或默认情况下在它唤醒时触发时,它将回放一个AudioClip。
AudioClip 只是一个加载到 AudioSource 中的音频文件。它可以是任何标准音频文件,例如 .mp3、.wav 等等。AudioClip 本身也是一个组件。
AudioListener
AudioListener 是监听场景中所有正在播放的音频并将其传输到计算机扬声器的组件。它就像游戏的耳朵。你听到的所有音频都基于此 AudioListener 的位置。场景中应该只有一个 AudioListener 才能正常工作。默认情况下,主摄像机已将 Listener 附加到它。Listener 没有任何设计师需要关心的公开属性。
音频滤镜
可以使用音频滤镜修改 AudioSource 的输出或 AudioListener 的输入。这些是特定组件,可以更改混响、合唱、滤波等等。每个特定滤镜都作为其自身的组件出现,并具有可调整其声音的公开值。
播放声音
让我们尝试制作一个按钮,当点击它时播放声音。首先,我们将创建一个圆形精灵,并将其设置为红色。
现在,让我们将一个AudioSource附加到此精灵。
为了让对象播放声音,我们必须为它提供一个声音。让我们使用此音效来实现我们的目的。
http://www.orangefreesounds.com/ding-sfx/
下载音效,并将其拖到 Assets 中。
当 Unity 将此资源导入为声音文件时,它会自动转换为AudioClip。因此,你可以将此声音剪辑直接从 Assets 拖到我们精灵的 AudioSource 中的 Audio Clip 插槽。
将声音剪辑从 Assets 直接拖到我们精灵的 AudioSource 中的 Audio Clip 插槽后,请记住取消选中 AudioSource 属性中的“Play on Awake”;否则,声音将在游戏开始时立即播放。
现在,让我们进入我们的代码。创建一个名为“BellSound”的新脚本并打开它。
由于我们的 AudioSource 通过代码控制,因此我们首先要获取对它的引用。我们将像以前一样使用 GetComponent 方法。
public class BellSound : MonoBehaviour { AudioSource mySource; // Use this for initialization void Start () { mySource = GetComponent<AudioSource>(); }
现在,让我们设置方法来检测被点击的对象。MonoBehaviour 为我们提供了所需的方法,名为 OnMouseDown。每当鼠标点击该 gameObject 的碰撞器范围内时,都会调用此方法。
由于我们尚未将碰撞器附加到我们的按钮,因此我们现在这样做。
我们不需要为此使用 Rigidbody;我们也不需要通过代码访问此碰撞器。它只需要存在才能使方法工作。
让我们测试一下该方法,看看它是否有效。在你的脚本中编写以下代码,并将其附加到按钮。
void OnMouseDown() { Debug.Log(“Clicked!”); }
保存脚本并附加后,播放游戏。单击按钮应该会在控制台中生成一条消息。
你现在距离播放声音只有一步之遥。你现在所要做的就是调用 AudioSource 实例中的Play方法。
void OnMouseDown() { mySource.Play(); }
保存你的脚本,并在游戏中运行它。单击按钮,你应该会听到声音播放!
注意——考虑制作一个按钮,每次单击它时音调都会升高。(使用mySource.pitch和一个计数器,看看你是否能解决这个问题。)
Unity - UI入门
在本节中,我们将学习 Unity 中用户界面或 UI 元素的设计过程。这包括基本设置,以及 Unity 附带的常用元素概述。
在 Unity 中设计 UI 的工作流程与我们迄今为止一直在进行的工作流程略有不同。首先,UI 元素不是标准的 GameObject,不能作为 GameObject 使用。UI 元素的设计方式不同;如果设置不正确,在 4:3 分辨率下看起来正确的菜单按钮在 16:9 分辨率下可能会看起来拉伸或变形。
Unity 中的 UI 元素不会直接放置到场景中。它们始终作为名为Canvas的特殊 GameObject 的子项放置。Canvas 就像场景中 UI 的“绘图纸”,所有 UI 元素都将在其中渲染。在没有现有 Canvas 的情况下,从创建上下文菜单创建 UI 元素将自动生成一个。
现在让我们看看 Canvas GameObject,了解其他新组件——
顶部的Rect Transform似乎具有许多标准 GameObject 的 Transform 不具备的新属性。
这是因为,虽然普通 GameObject 的 Transform 描述的是 3D 空间中的一个虚构点,但RectTransform定义的是一个虚构的矩形。这意味着我们需要其他属性来精确定义矩形的位置、大小和方向。
我们可以看到矩形的某些标准属性,例如高度和宽度,以及两个名为Anchors的新属性。锚点是其他实体可以在 Canvas 中“锁定”的点。这意味着,如果 UI 元素(例如按钮)锚定到 Canvas 的右侧,调整 Canvas 的大小将确保按钮始终位于 Canvas 的相对右侧。
默认情况下,你将无法修改画布区域的形状,它将在你的场景周围成为一个相对巨大的矩形。
接下来是Canvas组件。这是主组件,它包含一些关于如何绘制 UI 的通用选项。
我们看到的第一个选项是Render Mode。此属性定义用于将 Canvas 绘制到游戏视图中的方法。
下拉列表中有三个选项。让我们在后续章节中学习这些选项。
屏幕空间 - 叠加
此模式是菜单、HUD 等最标准的模式。它在场景中的所有其他内容之上渲染 UI,完全按照其排列方式进行渲染,绝无例外。当屏幕或游戏窗口大小改变时,它还会很好地缩放 UI。这是 Canvas 中的默认渲染模式。
屏幕空间 - 相机
“屏幕空间 - 相机”创建了一个假想的投影平面,该平面距离相机一定距离,并将所有 UI 投影到该平面。这意味着场景中 UI 的外观很大程度上取决于相机使用的设置;这包括透视、视野等等。
世界空间
在世界空间模式下,UI 元素的行为就像放置在世界中的普通游戏对象一样。但是,它们类似于精灵,因此通常用作游戏世界的一部分,而不是用于玩家,例如游戏中的监视器和显示器。由于这种性质,您可以在此模式下直接修改 Canvas RectTransform 的值。
画布缩放器 (Canvas Scaler) 是一组选项,允许您更明确地调整 UI 元素的比例和外观;它允许您定义当屏幕大小改变时 UI 元素如何调整大小。例如,UI 元素可以保持相同的大小,而不管屏幕大小如何,也可以按屏幕大小的比例进行缩放,或者可以根据参考分辨率进行缩放。
图形射线投射器 (Graphics Raycaster) 主要处理 UI 元素的射线投射([Unity 射线投射文档链接](假设此处应插入Unity射线投射文档链接)),并确保用户发起的事件(如点击和拖动)能够正常工作。
Unity - 按钮
在本章中,我们将学习如何将 UI 元素插入到我们的场景中以及如何操作它们。
让我们从一个按钮开始。要插入按钮,请在场景层次结构中右键单击,然后转到创建 → UI → 按钮。如果您没有现有的画布和事件系统,Unity 将自动为您创建一个,并将按钮也放置在画布中。
请记住,在默认模式“叠加”渲染模式下,画布的大小与相机的大小无关。您可以通过单击游戏选项卡来测试这一点。
如果您播放场景,您会注意到该按钮已经具有一些标准功能,例如检测鼠标是否悬停在其上,以及在按下时更改颜色。
按钮需要功能才能在 UI 中真正有用。此功能可以通过其属性添加。
让我们创建一个新的脚本,并将其命名为ButtonBehaviour。
public class ButtonBehaviour : MonoBehaviour { int n; public void OnButtonPress(){ n++; Debug.Log("Button clicked " + n + " times."); } }
我们创建了一个简单的的方法,记录我们点击按钮的次数。
注意 − 此方法必须是公共的;否则,按钮的功能将不会注意到它。
让我们创建一个空的 GameObject 并将此脚本附加到它。我们这样做是因为按钮本身不会执行任何操作;它只调用其脚本中指定的方法。
现在,进入按钮的属性,找到OnClick()属性。
点击底部选项卡上的“+”图标,列表中应该会显示一个新的条目。
此条目定义按钮按下作用于哪个对象,以及调用该对象脚本的哪个函数。由于按钮按下中使用的事件系统,您可以通过将多个函数添加到列表中来触发它们。
将包含我们创建的ButtonManager脚本的空 GameObject 拖放到无 (对象)槽中。
导航无函数下拉列表,查找我们的OnButtonPress方法。(请记住,它可以命名为任何您想要的名字,OnButtonPress只是一个标准化的命名约定。)您应该在ButtonBehaviour部分找到它。
如果您现在运行游戏,您可以测试按钮,并且控制台会打印出您按下按钮的次数。
Unity - 文本元素
Unity 内置的文本 UI 是学习者入门 UI 设计的一个很好的起点,即使它往往被更强大、更高效的社区构建的资源所掩盖。
对于我们的目的,普通的文本元素已经足够了。
文本作为自身的一个独特的 UI 元素,主要是因为该元素的动态性。例如,将玩家的当前分数打印到屏幕上,需要将分数的数值转换为字符串,通常通过.toString()方法,然后才能显示。
要插入文本 UI 元素,请转到场景层次结构,创建 → UI → 文本。
一个新的文本元素应该出现在您的画布区域。如果我们查看其属性,我们会看到一些非常有用的选项。
然而,最重要的是文本字段。您可以在该字段中键入您希望文本框显示的内容,但我们希望更进一步。
要更改文本的字体,您必须首先将字体文件从您的计算机导入 Unity,作为资源。字体不需要主动附加到场景中的任何东西,可以直接从资源中引用。
文本元素也可以通过脚本访问;这就是动态 UI 的重要性所在。
不是像上一章那样在控制台中输出按钮被按下的次数;让我们实际将其打印到游戏屏幕上。为此,我们将打开上一课中的 ButtonBehaviour 脚本,并对其进行一些更改。
using UnityEngine; using UnityEngine.UI; public class ButtonBehaviour : MonoBehaviour { int n; public Text myText; public void OnButtonPress(){ n++; myText.text = "Button clicked " + n + " times."; } }
我们做的第一个更改是添加一个新的命名空间引用。此引用用于处理 Unity 的 UI 组件,因此我们添加了 using UnityEngine.UI 行。
接下来,我们创建一个公共 Text 变量,我们可以在其中拖放我们的 Text UI 元素。
最后,我们使用myText.text访问此 UI 元素包含的实际文本。
如果我们保存脚本,现在我们将在 ButtonManager 中看到 Text UI 元素的新插槽。只需将包含该 Text 元素的 gameObject 拖放到插槽上,然后点击播放按钮。
Unity - 滑块
在本章中,我们将学习本系列中的最后一个 UI 元素。滑块通常用于设置某个值在最大值和最小值对之间的场景。最常见的用法之一是用于音频音量或屏幕亮度。
要创建滑块,请转到创建 → UI → 滑块。一个新的滑块元素应该出现在您的场景中。
如果您转到此滑块的属性,您会注意到一些用于自定义它的选项。
让我们尝试用这个滑块创建一个音量滑块。为此,打开 ButtonBehaviour 脚本(您可以重命名 ButtonManager GameObject,因为它肯定不仅仅是管理一个按钮了),并添加对滑块的引用。我们还将再次更改代码。
public class ButtonBehaviour : MonoBehaviour { int n; public Text myText; public Slider mySlider; void Update() { myText.text = "Current Volume: " + mySlider.value; } }
了解我们如何使用 Update 方法不断更新 myText.text 的值。
在滑块属性中,让我们选中“整数”框,并将最大值设置为 100。
我们将通过其属性设置文本的颜色,以获得更醒目的颜色。
让我们按照相同的步骤将滑块 GameObject 拖放到新插槽上,然后点击播放。
强烈建议您也探索和试验其他 UI 控件,以了解哪些控件以何种方式工作。
在我们接下来的部分中,我们将学习关于光照、材质和着色器的内容。
Unity - 材质和着色器
在本章中,我们将简要了解材质和着色器。为了更好地理解,我们将创建一个新的3D 项目,而不是我们当前的 2D 项目。这将帮助我们看到各种变化。
创建新项目后,转到层次结构并右键单击,然后转到3D 对象 → 立方体。这将在场景中间创建一个新的立方体。您可以通过按住右键并拖动场景视图中的鼠标来环绕立方体查看。您还可以使用滚轮放大和缩小。
现在,单击立方体,然后查看其属性。
最底部的属性似乎具有默认材质和标准着色器。
什么是材质?
在 Unity(以及许多 3D 建模方面),材质是一个包含有关使用该材质的对象的光照信息的文件。请注意,灰色的球体表示材质,上面有一些光线照射。
现在,不要混淆名称;材质与质量、碰撞或一般的物理特性无关。材质用于定义光照如何影响具有该材质的对象。
让我们尝试创建我们自己的材质。在资源区域中右键单击,转到创建 → 材质,并为其命名,例如“我的材质”。
这些属性不像我们目前为止学习过的任何属性。这是因为这些属性是在着色器中编程的,而不是在材质中。
材质首先使您的对象可见。事实上,即使在 2D 中,我们也使用不需要光照的特殊材质。当然,Unity 为我们生成并应用它到所有东西,所以我们甚至没有注意到它在那里。
什么是着色器?
着色器是一个程序,它定义了如何在屏幕上绘制每个像素。着色器不是用 C# 甚至面向对象语言编程的。它们是用一种称为 GLSL 的类似 C 语言的语言编程的,它可以向 GPU 直接发出指令以进行快速处理。
Unity - 粒子系统
粒子系统有助于以高效的方式生成大量具有短生命周期的粒子。这些系统经历一个单独的渲染过程;即使有数百或数千个对象,它们也可以实例化粒子。
现在,粒子在粒子系统中是一个模糊的术语;粒子是粒子系统生成的任何单个纹理、材质实例或实体。它们不一定是漂浮在太空中的点(尽管它们可以是!),并且可以用于大量不同的场景。
一个 GameObject 通过附加的粒子系统组件来管理粒子系统;粒子系统不需要任何资源来设置,尽管它们可能需要不同的材质,具体取决于您想要的效果。
要创建粒子系统,可以通过“添加组件”设置添加粒子系统组件,或者进入层级结构,选择创建 → 效果 → 粒子系统。这将生成一个附加了粒子系统的新的游戏对象。
如果你查看粒子系统的属性,你会发现它包含许多模块。默认情况下,只有三个模块处于活动状态:发射、形状和渲染器。可以通过点击模块名称旁边的圆圈来激活其他模块。
在某些值的右侧,你可能会注意到一个小黑箭头。这允许你更好地控制每个粒子的值。例如,你可以将起始大小设置为两个常量之间的随机值,以指示粒子系统渲染不同大小的随机粒子,就像水管喷水一样。
Unity - 使用资源商店
资源商店是Unity在游戏引擎市场上最强大的功能之一;它包含大量的资源、工具、脚本,甚至完整的现成项目供你下载。
要使用资源商店,你需要一个有效的Unity ID。如果你没有,你可以在Unity网站上创建一个。
创建Unity ID后,点击与场景视图位于同一行的资源商店选项卡。
登录后,你应该能够在右上角看到你的用户名。
在这个例子中,我们将导入生存射击教程项目。为此,我们将在选项卡中搜索它,然后点击Unity发布的资源。
我们将点击下载,并等待其完成。完成后,“下载”按钮将变为“导入”;再次点击它以将你的新资源导入当前打开的项目。
(注意——在本例中,我们导入的是一个完整的项目;如果Unity警告你,请创建一个新项目或覆盖现有项目(如果你愿意)。两种方法都可以。)
将弹出一个新窗口,列出你刚刚导入的新资源的所有内容。根据你下载的内容,这可能是一个文件、一堆文件或带有文件夹和文件层次结构的完整树。默认情况下,当你点击导入时,Unity 将导入所有资源组件,这就是我们想要的。现在,让我们点击导入让Unity 完成它的工作。
尝试下载未付费的资源是非法的,并且始终可能存在病毒、错误或缺乏更新。