UNITY 3D – 游戏编程 – 第 5 部分






4.94/5 (16投票s)
本系列文章的第五篇,旨在讨论Unity 3D以及如何开始自己的3D项目。
引言
在本系列的第五部分,我们将整合迄今为止所学到的所有知识,并将其提升一个档次!我们将学习如何将3D模型导入游戏引擎,并利用迄今为止学到的方法,将其应用于游戏世界中的移动。
如果您还没有阅读,请花点时间阅读
-
Unity 3D – 游戏编程 – 第 5 部分
Unity 3D 网络文章
Unity 3D Leap Motion 和 Oculus Rift 文章
在本系列的第一部分,我们从Unity 3D环境的最基本知识开始。我们了解了IDE以及将在整个项目中使用的不同部分。我们还学习了如何使用设计器中的工具对选定的游戏对象应用不同的变换:定位、旋转和缩放。最后,我们学习了如何创建第一个脚本,并使用脚本在立方体的Y轴上应用旋转变换。
在本系列的第二部分中,我们更多地研究了通过编码对给定对象进行变换。我们还研究了如何创建对场景中对象的渲染至关重要的光源。
在本系列的第三部分中,我们研究了如何通过键盘处理用户输入,并根据键码采取特定操作。
在本系列的第四部分,我们研究了创建简单的用户界面。我们开发的界面为用户提供了反馈机制,也为用户提供了输入到我们的游戏或模拟中的另一种方法。
在第5部分,我们将利用迄今为止学到的所有知识来创建一个有趣的环境,玩家角色可以在其中移动和发现对象。此外,本文的关键点将是导入3D模型到游戏引擎的方法。
Windows Phone 8.x 演示
我提供了一个免费的手机应用程序,您可以下载并在您的Windows Phone上预览演示。要下载移动应用程序,请访问链接:CodeProjectArticleSample
文章代码和视觉效果的实时预览
实时预览链接:http://www.noorcon.com/CodeProject/CodeProjectArticlePreview.html
背景
注意:对于本篇文章,我将使用 SketchUp 创建一些简单的积木,并将其导入 Unity!我不是 3D 建模师或设计师,所以请耐心等待并原谅我的不足!
假定本文读者熟悉通用的编程概念。也假定读者对 C# 语言有了解和经验。还建议本文读者熟悉面向对象编程和设计概念。我们将在文章中根据需要简要介绍它们,但不会深入细节,因为它们是完全独立的主题。我们还假定您热衷于学习 3D 编程,并具备 3D 图形和矢量数学的基本理论概念。
最后,本文使用 Unity 3D 版本 4.6.1,这是初始发布日期时的最新公共版本。本系列讨论的大多数主题都与游戏引擎的旧版本兼容,甚至可能也与今年应该发布的那个新版本兼容。然而,有一个主题与旧版游戏引擎相比,在当前的 4.6.1 版本中存在显著差异,那就是 UI(用户界面)管道。这是因为引擎中新的 UI 架构远远优于此版本发布之前的架构。我个人对新的 UI 架构非常满意。
使用代码
下载文章系列的工程/源代码:下载源代码。
随着后续文章的提交,项目/源代码也将随之扩展。新的项目文件和源文件将包含系列中较旧的部分。
注意:要获取最新代码,请转到本系列最新发布的部分并下载代码。
想法
因此,就像宇宙中的任何其他伟大成就和项目一样,第一步是提出一个想法,一种愿景。所以,出于本文以及本系列迄今为止所做一切的需要,我们想提出以下内容!
简要
创建一个类似迷宫的关卡。关卡应足够大,以便玩家可以自由移动并发现关卡及其所有相关元素。关卡将是有限的,并由墙壁定义边界。假设我们希望关卡总共由 100 平方米组成,因此我们的主关卡将是一个 10 米 x 10 米的方形区域。玩家的目标是在给定时间内尽可能多地收集硬币。
注意:如果您在任何时候想为本系列的任何部分做出贡献,请务必这样做,并与我联系,以便我们将您的想法和内容纳入本系列!
让我们开始勾画我们的环境,我不是像你们中的大多数人那样有创意,所以我提出了以下内容
注意:这不是一个 SketchUp 或 3D 建模文章,所以我不会讲解该应用程序的使用。
图 1 - 我的关卡的 SketchUp 模型
我使用 SketchUp 创建了这个演示所需的迷宫。它对我来说很容易使用,因为我不是 3D 设计师/建模师。您可以使用任何您熟悉的工具。所以步骤如下
-
在 3D 建模工具中设计您满意的关卡。
-
将您的模型导出为 .OBJ 文件。
注意:在您的 3D 建模软件中,您可以为模型附加纹理。我没有为我的模型附加任何纹理。此时我只对模型的结构感兴趣。
注意:导出模型时,请确保选择了以下选项:勾选“三角化所有面”,勾选“导出双面”,并勾选“导出边”。如果您为模型附加了任何纹理,请同时勾选“导出纹理贴图”选项。
您需要注意的另一个重要点是您的建模软件配置的实际单位。由于 Unity 3D 使用公制单位,最好将您的 3D 建模软件配置为在建模时使用公制单位。
注意:导出模型时,请确保您以公制单位导出。如果您的模型已经是公制单位,请选择“模型单位”。
现在让我们看看如何轻松地将我们的模型导入 Unity 3D。
导入模型
我首先要做的是创建一个易于在项目中导航的目录结构。我保持它非常简单。我为我将在项目中使用的每种不同类型的资源都创建了一个文件夹。因此,一个给定的项目至少在 Assets 文件夹下有以下文件夹
-
Models – 我所有的模型都放在这个文件夹里。
-
Prefabs – 我所有的预制件都放在这个文件夹里。
-
Scenes – 我所有的场景都保存在这个文件夹里。
-
Scripts – 我所有的脚本都保存在这个文件夹里。
-
Sprites – 我所有的精灵(2D 纹理)都保存在这个文件夹里。
您可以直接在 Unity 3D 中创建此目录结构,或在项目存储的文件系统级别创建。要在 IDE 中创建,您需要在“项目”窗口中右键单击,然后从上下文菜单中选择“创建->文件夹”。
图 2 - 演示的项目结构
要导入模型,只需将 .OBJ 文件复制并粘贴到“模型”文件夹中即可。这可以通过您的系统文件浏览器复制和粘贴任何其他文件来完成。将 .OBJ 文件复制到所需文件夹后,返回 Unity 3D,引擎将自动为您的 .OBJ 文件或文件应用正确的内部标记。
图 3 - 将 .OBJ 文件放入模型文件夹后的项目视图
|
导入模型后,从项目窗口导航到您的模型并选择它。 现在您的检查器窗口应该会显示您模型的所有属性。 如图 4 所示,有很多属性,并且每个属性都很重要。有些默认情况下已勾选。根据您的模型和模型的使用目的,您可以根据需要打开或关闭这些属性。您需要自行进一步阅读。 但就我们而言,在大多数情况下,默认设置就足够了。我们唯一需要做的更改是启用生成碰撞器功能,如图 4 所示,项目编号 2。 碰撞器是模型的重要组成部分,我们将在稍后详细介绍。碰撞器用于碰撞检测。 接下来的两个重要属性是法线和切线,它们会自动导入并由引擎自动计算,如图 4 中的数字 3。 在检查器窗口中,一直滚动到底部,然后单击应用按钮。现在 Unity 将对您的模型应用所有必要的更改。 |
创建关卡
让我们通过创建一个新场景来开始我们的新关卡。您可以通过从主菜单中选择文件->新建场景来完成此操作。
注意:如果当前场景未保存,系统会提示您保存。
现在将您的模型从“项目窗口”拖放到“场景设计器视图”中。
图 5 - 导入的模型在设计视图中
我调整了我的视图和摄像机,以便更好地了解关卡在 3D 空间中的样子。另外,我将关卡模型放置在以下坐标:<5,0,5>;这实际上是将模型居中到原点,因为我的模型是 10x10。我还将一个光源放在原点,为场景提供一些光照以便更好地观看。
就将模型放置在场景中而言,您只需要做到这些。所以我们完成了这一阶段。现在让我们看看游戏的下一步。
创建和放置玩家角色
由于我们的游戏是一个迷宫,玩家在游戏中需要在给定时间内收集尽可能多的硬币,所以我决定将我的玩家建模为一个球体。所以我们将使用一个球体原始体作为我们主要角色的占位符。为此,在层级窗口中右键单击并选择3D对象->球体。
图 6 - 创建球体作为玩家角色的占位符
注意:创建球体对象时,使用设计定位工具将其正确放置在关卡模型上方并内部。
|
现在您的检查器窗口应该会显示您模型的所有属性。 我首先要做的是将 GameObject 的名称从Sphere更改为CharacterPlaceHolder_Part_5。 我们需要对这个 GameObject 所做的第二个也是更重要的更改是为其添加一个Rigidbody组件。 要做到这一点,您需要单击检查器窗口中的添加组件按钮,然后选择物理,再选择Rigidbody。 向对象添加Rigidbody组件将使其运动受 Unity 物理引擎的控制。即使不添加任何代码,Rigidbody 对象也会因重力而向下坠落,并且如果存在正确的 Collider 组件,它还会与进入的对象发生碰撞。 这足以模拟运动以及与场景中其他对象的碰撞。 注意:我已将 X 和 Z 轴上的旋转设置为冻结。 这是为了阻止对象基于环境中产生的力在这些轴上旋转。例如,我们不希望我们的对象在与另一个对象碰撞时在 X 和 Z 轴上发生旋转。
|
接下来我想做的是能够移动场景中的角色玩家(CP)。为了做到这一点,我们需要创建一个脚本并将其附加到代表我们CP的对象上。我将该脚本称为playerInput.cs,这是它的代码:
using UnityEngine; using System.Collections; public class playerInput : MonoBehaviour { // Use this for initialization void Start () { } // Update is called once per frame void Update () { // code for the movement of player (CP) forward if(Input.GetKey(KeyCode.UpArrow)){ this.transform.Translate(Vector3.forward * Time.deltaTime); } // code for the movement of player (CP) backward if(Input.GetKey(KeyCode.DownArrow)){ this.transform.Translate(Vector3.back * Time.deltaTime); } // code for the movement of player (CP) left if(Input.GetKey(KeyCode.LeftArrow)){ this.transform.Rotate(Vector3.up, -5); } // code for the movement of player (CP) right if(Input.GetKey(KeyCode.RightArrow)){ this.transform.Rotate(Vector3.up, 5); } } }
我们的 CP 移动非常简单。我们将使用键盘箭头键来定义 CP 的移动。向上和向下键盘箭头键将分别向前和向后移动 CP,左右键盘箭头键将分别向左或向右旋转 CP。
最后一件我想为我们的场景(关卡)做的事情是让摄像机跟随 CP。这也非常容易实现!
在场景设计器中,使用鼠标将您的视图定位在 CP(球体)后面的场景设计器中。
图 8 - 游戏视图显示游戏期间的实际摄像机视图
当您对场景窗口中的内容满意后,选择主摄像机对象。从主菜单中,选择GameObject->与视图对齐。这将移动主摄像机 GameObject,或当前选定的 GameObject,以使其与场景中的视图对齐!
您现在可以运行程序并在设计的关卡中移动了
创建硬币
好的,现在我们已经完成了主关卡设计,并且CP也已到位且可移动,我们可以开始处理硬币了!为了设计硬币,我使用了胶囊体原始体。所以要创建硬币,在您的层级窗口中,右键单击以获取上下文菜单,然后选择3D对象->胶囊体。
图 9 - 修改后的胶囊体原始体
这将在场景中放置一个胶囊体原始体。我们需要修改我们的胶囊体游戏对象的属性,使其看起来像一枚硬币,为此,我们将需要使用检查器窗口。
|
现在您的检查器窗口应该会显示您模型的所有属性。 我首先要做的是将 GameObject 的名称从Capsule更改为coin。 我想要做的第二个更改是更改 GameObject 的缩放属性。我想让它看起来更像一枚硬币,所以为此,我需要将缩放值更改为以下值:相对于每个轴为<0.25, 0.05, 0.25>。另外,将 GameObject 绕 Z 轴旋转 90 度。旋转应为:<0,0,90>。 第三个重要的属性更改是将在胶囊体碰撞器上设置Is Trigger属性为True。这对于引擎中的碰撞检测很重要。它允许在当此 GameObject 的碰撞器撞击/进入另一个 GameObject 的碰撞器时引发一个事件。 最后,我想让硬币在场景中旋转,以增加一些动态效果和额外效果。因此,我们将一个名为rotateCoin.cs的旋转脚本附加到coin GameObject。 注意:由于我必须将原始胶囊体原始体绕 Z 轴旋转 90 度,因此硬币对象的旋转轴不再是 Y 轴。根据我的设计,新的旋转轴将是 X 轴。您的可能不同! |
注意:我还创建了一个名为 coin 的新标签。
标签元素是另一个可用于识别 GameObject 用户定义类型的属性。默认情况下,您只有以下标签
-
未标记
-
重生
-
结束
-
仅限编辑器
-
主摄像机
-
玩家
-
游戏控制器
要添加新标签,您需要选择添加标签…并在列表元素中输入您的标签。在这种情况下,我创建了一个名为coin的新标签,并在检查器窗口中将标签属性从未标记更改为coin。
现在您可以运行程序来测试新实现。您的硬币应该会在其位置旋转,就像在世界中定位的那样。
旋转硬币的代码列表
using UnityEngine; using System.Collections; public class rotateCoin : MonoBehaviour { // Use this for initialization void Start () { } // Update is called once per frame void Update () { this.transform.Rotate (Vector3.right, 3); } }
检测碰撞
我们游戏的目标是收集硬币。为了做到这一点,我们需要CP能够检测它是否与硬币对象发生碰撞/相交。为此,我们需要扩展附加到CP的脚本来处理此事件。
由于这是一个简单的游戏,交互和逻辑不多,我们可以使用处理玩家输入的同一脚本来处理碰撞检测。所以我们可以将我们的playerInput.cs脚本扩展到以下内容:
using UnityEngine; using System.Collections; public class playerInput : MonoBehaviour { // Use this for initialization void Start () { } // Update is called once per frame void Update () { // code for the movement of player (CP) forward if(Input.GetKey(KeyCode.UpArrow)){ this.transform.Translate(Vector3.forward * Time.deltaTime); } // code for the movement of player (CP) backward if(Input.GetKey(KeyCode.DownArrow)){ this.transform.Translate(Vector3.back * Time.deltaTime); } // code for the movement of player (CP) left if(Input.GetKey(KeyCode.LeftArrow)){ this.transform.Rotate(Vector3.up, -5); } // code for the movement of player (CP) right if(Input.GetKey(KeyCode.RightArrow)){ this.transform.Rotate(Vector3.up, 5); } } // This event will be raised by object that have their Is Trigger attributed enabled. // In our case, the coin GameObject has Is Trigger set to true on its collider. void OnTriggerEnter(Collider c){ if(c.tag.Equals("coin")){ Debug.Log("Got the Coin!"); } } }
处理触发器事件的主要函数是OnTrigeerEnter(Collider c)。此函数将获取进入CP对象碰撞器的对象的碰撞器。在碰撞器中,我们检查我们碰撞的对象是什么类型的,为此,我们使用附加到我们碰撞的 GameObject 的标签元素!
在这种情况下,每次我们与硬币对象的实例发生碰撞时,我们都会在控制台窗口中打印“Got the Coin!”。
关注点
我们在第 5 部分涵盖了大量的想法和概念。我们简要讨论了如何从构思和愿景开始,在开始任何设计和编码之前,在纸上设定游戏规则和界限。我们研究了如何将 3D 模型导入 Unity 3D。我们研究了如何创建角色玩家(CP)以及如何对其应用移动。
我们还讨论了如何向给定的GameObject添加组件。例如,对于CP,我们必须添加一个Rigidbody组件。这使我们能够使用物理引擎开始在世界中创建更逼真的交互。
我们还研究了coin GameObject 的碰撞器组件中的Is Trigger属性。
总而言之,本文涵盖了大量信息,而且非常简短和简化。目的是让您深入挖掘并自己熟悉它们。
在下一部分,我们将继续我们的游戏,开始引入更多交互,并开始为其开发一些 GUI(图形用户界面)。
历史
这是我将缓慢贡献给 Code Project 社区的系列文章中的第五篇。
-
Unity 3D – 游戏编程 – 第 5 部分
Unity 3D 网络文章
Unity 3D Leap Motion 和 Oculus Rift 文章