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






4.99/5 (39投票s)
本系列文章的第二篇,旨在讨论Unity 3D以及如何开始创建自己的3D项目。
引言
在本系列文章的第二篇中,我们将继续扩展我们对环境的认识,并开始演示更多的代码和脚本编写。如果您还没有这样做,请花点时间read
-
Unity 3D – 游戏编程 – 第 2 部分
Unity 3D 网络文章
Unity 3D Leap Motion 和 Oculus Rift 文章
在第一部分中,我们从Unity 3D环境的最基础知识开始。熟悉IDE以及您将在项目中使用的不同部分。我们还涵盖了如何使用设计器中的工具对选定的游戏对象应用不同的变换:定位、旋转和缩放。最后,我们研究了如何创建第一个脚本,并使用该脚本在立方体的Y轴上应用旋转变换。
在本系列文章的第二部分中,我们将通过代码研究给定对象的更多变换。我们还将研究如何创建对渲染场景中的对象至关重要的光源。没有照明,您的场景或关卡将不会那么吸引人。
现在,理解这些概念非常重要,这样您才能更好地控制想要创建的环境。
Windows Phone 8.x 演示
我提供了一个免费的手机应用程序,您可以下载并在Windows Phone上预览演示。要下载移动应用程序,请访问链接: CodeProjectArticleSample
文章代码和视觉效果的实时预览
实时预览链接:http://www.noorcon.com/CodeProject/CodeProjectArticlePreview.html
背景
如果您还没有这样做,请花点时间阅读 Unity 3D – 游戏编程入门 – 第一部分。
假定本文的读者普遍熟悉编程概念。还假定读者对C#语言有理解和经验。还建议本文的读者熟悉面向对象编程和设计概念。我们将在文章中根据需要简要介绍它们,但不会深入细节,因为它们是完全独立的主题。我们还假定您热衷于学习3D编程,并具备3D图形和向量数学的基本理论概念。
最后,本文使用了Unity 3D 4.6.1版本,这是初始发布日期时最新的公开版本。系列中讨论的大多数主题将与游戏引擎的旧版本兼容,甚至可能也与今年某个时候发布的新版本兼容。但是,有一个主题与旧版本游戏引擎相比,在当前的4.6.1版本中存在显著差异,那就是UI(用户界面)管道。这是因为引擎中引入了新的UI架构,它远优于我们在此版本发布之前使用的。我个人对新的UI架构非常满意。
使用项目文件
下载文章系列的下载/源代码: 下载源代码.
随着后续文章的提交,项目/源代码也将随之扩展。新的项目文件和源文件将包含系列中较旧的部分。
对象的定位、旋转和缩放
场景/关卡中的每个游戏对象都有一个Transform。此组件用于存储和操作给定对象的位置、旋转和缩放。作为一名游戏开发者,您需要熟悉Transform及其通过API提供的许多操作和属性。
注意:要熟悉Transform的所有不同变量、函数等…,您需要查阅Unity 3D提供的脚本API文档。我不会在这里列出它们,因为那没有意义。
如前所述,理解Transform对于您按照需要操纵场景/关卡至关重要。
假设我们要创建三个立方体原语,方法如下:cube1将位于原点(0,0,0),cube2将创建在(3,0,0),cube3将创建在(-3,0,0)。此时,我们将使用设计器来创建我们的原语。稍后我将演示如何通过代码动态创建原语。
使用菜单工具栏,选择GameObject->3D Object->Cube。您需要执行此操作三次,每次都会创建一个立方体并将其放置在(0,0,0)。使用Hierarchy窗口,选择第一个立方体。Inspector窗口将加载所选立方体的所有属性。将其名称更改为Cube1,并确保Position向量为(0,0,0),即(X,Y,Z)。
注意:如果您在此处迷失了,请参考本系列的第1部分。我们在第1部分中涵盖了基础知识。
重复相同的过程,使Cube2位于(3,0,0),Cube3位于(-3,0,0)。您的场景应如下所示
图1-三个立方体原语
注意:根据您的视角位置和方向,您可能需要进行调整,使其看起来与图1中的内容一致。
现在,假设我们要让三个立方体分别绕不同的轴旋转,顺序如下:Cube1绕其Y轴旋转,Cube2绕其X轴旋转,Cube3绕其Z轴旋转。为了实现这一点,我们需要创建三个脚本,每个脚本代表一个游戏对象的旋转。
在您的Project窗口中,右键单击以弹出上下文菜单。选择Create->C# Script。这将为您创建一个C#文件。将其命名为cube1Rotate.cs。对Cube2和Cube3重复相同的过程。您应该得到三个名为cube1Rotate.cs、cube2Rotate.cs和cube3Rotate.cs的脚本。
双击cube1Rotate.cs文件,在Mono Develop编辑器或您选择的编辑器中打开它。在Update()函数中,我们需要实现将应用于Y轴旋转的代码。
Cube1 Y轴旋转
using UnityEngine; using System.Collections; public class cube1Rotate : MonoBehaviour { // Use this for initialization void Start () { } // Update is called once per frame void Update () { // Rotate our game object around it's y-axis this.transform.Rotate (new Vector3 (0, 1, 0), 1); } }
为了正确应用Cube2和Cube3的旋转,我们需要打开相应的脚本文件,并使用相同的Transform函数,称为Rotate(),但传入不同的Vector3对象。因此,对于X轴旋转,它将是Vector3(1,0,0),对于Z轴旋转,它将是Vector3(0,0,1)。
Cube2 X轴旋转
using UnityEngine; using System.Collections; public class cube2Rotate : MonoBehaviour { // Use this for initialization void Start () { } // Update is called once per frame void Update () { // Rotate our game object around it's x-axis this.transform.Rotate (new Vector3 (1, 0, 0), 1); } }
Cube3 Z轴旋转
using UnityEngine; using System.Collections; public class cube3Rotate : MonoBehaviour { // Use this for initialization void Start () { } // Update is called once per frame void Update () { // Rotate our game object around it's z-axis this.transform.Rotate (new Vector3 (0, 0, 1), 1); } }
注意:不要忘记将每个C#脚本与其相应的GameObject关联起来。
现在,到目前为止,如果您是那些更具进取心的开发人员之一,您可能会自言自语,我们是否真的需要为每个GameObject创建一个单独的脚本文件?如果我的场景/关卡中有数百个GameObject怎么办?我该怎么办?
那么,您问题的简短答案是,不,您不需要为每个GameObject创建一个单独的脚本文件。
让我们看一个更高级的场景。让我们创建一个脚本,该脚本将创建三个额外的立方体原语,并将它们放置在现有立方体上方两单位处。也就是说,我们将创建一个位于(0,2,0)的Cube1',一个位于(3,2,0)的Cube2',以及一个位于(-3,2,0)的Cube3'。
using UnityEngine; using System.Collections; public class dynamicPrimitives : MonoBehaviour { private GameObject cube1; // represents our Cube1' private GameObject cube2; // represents our Cube2' private GameObject cube3; // represents our Cube3' // Use this for initialization void Start () { // initialize our Cube1 primitive and place it at location (0,2,0) this.cube1 = GameObject.CreatePrimitive (PrimitiveType.Cube); this.cube1.transform.localPosition = new Vector3 (0, 2, 0); // initialize our Cube2 primitive and place it at location (3,2,0) this.cube2 = GameObject.CreatePrimitive (PrimitiveType.Cube); this.cube2.transform.localPosition = new Vector3 (3, 2, 0); // initialize our Cube3 primitive and place it at location (-3,2,0) this.cube3 = GameObject.CreatePrimitive (PrimitiveType.Cube); this.cube3.transform.localPosition = new Vector3 (-3, 2, 0); } // Update is called once per frame void Update () { // apply the y-axis transform to Cube1' this.cube1.transform.Rotate(new Vector3(0,1,0), 1); // apply the x-axis transform to Cube2' this.cube2.transform.Rotate(new Vector3(1,0,0), 1); // apply the z-axis transform to Cube3' this.cube3.transform.Rotate(new Vector3(0,0,1), 1); } }
上面显示的列表中发生了一些事情。首先,您会注意到我们声明了三个私有的GameObject变量,它们将代表我们的立方体。接下来,您会注意到我们使用了Start()函数来执行我们的初始化。所以,让我们来讨论其中一个立方体,同样的概念将适用于其余的。
我们的Cube1'由cube1 GameObject定义。默认情况下,它是null。如果您还记得第一部分,Start()函数将在您的游戏或模拟开始时只调用一次,因此我们希望在Start()函数中初始化/实例化我们的原语。我们可以通过使用GameObject.CreatePrimitive(PrimitiveType.Cube)函数来实现这一点。CreatePrimitive()是实例化我们Primitive的函数,而PrimitiveType.Cube是一个内部枚举,将传递给函数以实例化Cube原语。最后,新创建的GameObject被赋值给我们的变量cube1。
默认情况下,每个原语将被实例化在位置(0,0,0),因此下一步就是使用Transform将GameObject重新定位到3D世界中所需的位置。在这种情况下,对于Cube1',我们希望将其放置在(0,2,0)。因此,我们需要将新实例化立方体的localPosition向量更改为新的Vector3(0,2,0)。这代表了GameObject的位置。
您需要将这个新创建的脚本附加到场景中的某个对象上,以便在运行时执行。此时,附加到哪个GameObject并不重要。继续将此脚本附加到您的相机上。
注意:由于脚本不会修改它所附加到的GameObject,因此它不会对其产生任何影响。
当您运行场景时,它看起来会像这样
图2-运行脚本的截图
到现在为止,您应该已经足够熟悉在设计时和通过代码动态创建GameObject了。您也应该熟悉在设计时以及通过代码定位和旋转GameObject。接下来,我们将讨论场景的照明。
灯光、相机、行动!
Unity 3D为您提供了四种光源。深入研究照明的细节和理论超出了本文的范围。但要知道照明是您场景中非常重要的一个方面很重要。毕竟,没有照明,相机怎么能看到周围的环境呢?
四种类型
-
定向光
-
点光源
-
聚光灯
-
区域光
我将在未来的文章中讨论不同的光源。现在,让我们为场景创建一个点光源。为此,从主工具栏中,选择GameObject->Light->Point Light。一个点光源将被放置在您当前的场景中。继续从Hierarchy窗口中选择Point Light对象,并将(X,Y,Z)的位置向量属性更改为(0, 1.5, 3.5)。请注意,您的场景和游戏视图现在已经亮起来了。在设计时,您应该看到类似下图的景象
图3-设计时的照明
以及运行时下面的图,前提是您已将最新脚本应用于场景
图4-运行时照明
我希望将本系列的第二部分在此处结束。
关注点
请在您自己的时间多加练习,确保您理解到目前为止我们所涵盖的内容。另外,如果您是C#新手,请确保您阅读有关基础知识的内容,并尽可能多地练习。
历史
这是我将缓慢贡献给Code Project社区的系列文章中的第二篇。
-
Unity 3D – 游戏编程 – 第 2 部分
Unity 3D 网络文章
Unity 3D Leap Motion 和 Oculus Rift 文章