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






4.97/5 (25投票s)
本系列文章的第四篇,讨论Unity 3D以及如何开始您自己的3D项目。
引言
在本系列的第四部分中,我们将了解如何使用Unity 3D中新的UI架构创建用户界面元素。在我们的代码库基础上,我们将创建一个简单的界面来执行一些我们通过键盘输入在第二部分和第三部分中编程的功能。如果您尚未阅读,请花点时间阅读。
-
Unity 3D – 游戏编程 – 第 4 部分
Unity 3D 网络文章
Unity 3D Leap Motion 和 Oculus Rift 文章
在本系列的第一部分中,我们从Unity 3D环境的最基础知识开始。熟悉IDE以及在整个项目中将要使用的不同部分。我们还介绍了如何使用设计器中的工具对选定的游戏对象应用不同的变换:定位、旋转和缩放。最后,我们研究了如何创建我们的第一个脚本,并使用该脚本对我们的立方体的Y轴应用旋转变换。
在本系列的第二部分中,我们更多地研究了通过编码对给定对象进行变换。我们还研究了如何创建对场景中对象的渲染至关重要的光源。
在本系列的第三部分中,我们研究了如何通过键盘处理用户输入,并根据键码采取特定操作。
在本系列的第 4 部分中,我们将介绍如何创建一个简单的用户界面。我们将开发的用户界面将为我们提供一种向用户提供反馈的方式,以及另一种用户向我们的游戏或模拟输入的方法。
Windows Phone 8.x 演示
我提供了一个免费的手机应用程序,您可以下载并在Windows Phone上预览演示。要下载该移动应用程序,请点击此链接:CodeProjectArticleSample
文章代码和视觉效果的实时预览
实时预览链接:http://www.noorcon.com/CodeProject/CodeProjectArticlePreview.html
背景
本文假定读者普遍熟悉编程概念。还假定读者理解并具有C#语言的经验。建议本文读者也熟悉面向对象编程和设计概念。我们将在本文中根据需要简要介绍它们,但不会深入探讨细节,因为它们是完全独立的主题。我们还假设您对学习3D编程充满热情,并具备3D图形和向量数学的基本理论概念。
最后,本文使用Unity 3D 4.6.1版,这是截至首次发布日期的最新公开版本。本系列中讨论的大部分主题将与旧版本的游戏引擎兼容,也许也与预计今年某个时候发布的新版本兼容。然而,有一个主题在当前4.6.1版本与旧版本的游戏引擎相比有显著不同,那就是UI(用户界面)管线。这是由于引擎中新的UI架构,它远优于此版本之前我们所拥有的。就我个人而言,我对新的UI架构非常满意。
使用代码
下载文章系列的项目/源代码:下载源代码。
随着后续文章的提交,项目/源代码也将随之扩展。新的项目文件和源文件将包含系列中较旧的部分。
注意:要获取最新代码,请转到本系列最新发布的部分并下载代码。
Unity 3D中的用户界面设计
如您所知,每个程序都由一些输入和一些输出组成。在本系列的第 3 部分中,我们研究了如何通过计算机键盘从用户那里获取输入。现在,我们将研究如何向用户显示反馈,以便他/她更好地了解程序中正在发生的事情,并通过创建另一种方式让用户向程序输入来继续。
注意:我们将构建的用户界面仅适用于Unity 3D 4.6.1及更高版本。
在Unity 3D最新发布之前,游戏程序员和设计师必须花费大量时间来正确开发其游戏的用户界面。而且他们必须通过编码和编程完成大部分设计。通过最新发布,我们拥有一个更好的引擎来处理UI部分,因此将显著提高UI开发的效率。
假设您已经完成了本系列的所有部分,让我们打开我们的Unity 3D项目。您将看到一个如下图所示的场景。
图 1-来自第 3 部分代码的屏幕截图
让我们继续创建一个**HUD** *(平视显示器)*,它将显示场景中每个原始对象的位置和旋转。
如果您还记得之前的文章,我们有三个设计时原始对象(立方体),已使用设计器放置在场景中,如上图所示。我们还有一个脚本,当我们在键盘上按下空格键时,将在运行时创建三个额外的原始对象(立方体)。
要绘制UI元素,我们需要创建一个UI画布。这一点很重要,每个UI元素都需要是画布的子元素才能在屏幕上绘制。
要创建 UI 画布,请在 Unity 3D 的 Hierarchy 窗口中右键单击鼠标,您将看到一个上下文菜单。
图 2-在层次结构窗口中右键单击后的上下文菜单
请注意,在上下文菜单中的“UI”菜单下,您可以使用的UI项。它们是:
-
Panel
-
Button
-
文本
-
Image
-
RawImage
-
Slider
-
滚动条
-
Toggle
-
InputField
-
画布
-
EventSystem
正如您所看到的,您可以使用的UI元素类型是有限的,但尽管如此,您拥有实现游戏或模拟所需功能的一切。稍加创意,这些UI元素将大有作为!
继续并从菜单中选择Canvas对象。当您这样做时,您会注意到场景中会自动放置两个新的GameObject,**Canvas**和**EventSystem**。
图 3-注意场景中放置了两个新对象
我们将使用相同的方法添加UI元素。每次要添加UI元素时,我们都会右键单击以获取上下文菜单,然后从UI菜单下选择正确的UI组件。
那么我们假设我们想根据以下草图创建我们的用户界面。
图 4-我们的用户界面草图
创建用户界面 - 面板 1
我们可以开始创建如图4所示的用户界面。让我们从面板1开始。在Hierarchy窗口中,选择**Canvas** GameObject并右键单击以调出上下文菜单。选择UI->Panel以在场景中插入一个**Panel** GameObject。双击**Panel** GameObject使其聚焦在场景视图中,如下图所示。
图 5-显示画布上绘制面板的屏幕截图
这里有很多事情正在发生。首先,请注意,当您双击Hierarchy窗口中列出的项目时,Scene View会聚焦到场景中指定的对象。因此,顺便提一下,如果您想在更复杂的场景中聚焦到给定对象,只需双击它即可。
其次,更重要的是,看一下检查器窗口,在图5中标记为数字1。让我们花点时间讨论几个重要的概念。
|
您现在应该已经熟悉**Inspector Window**了。此窗口列出了给定对象的所有附加组件,并根据对象类型列出不同的组件。 查看图 6,您会注意到我突出显示的三个区域。
**Rect Transform**组件是您应该熟悉的最重要的组件。它将用于相对于彼此的**UI对象**的定位和锚定! 我们将大量使用Rect Transform来放置和定位我们的UI元素。稍后会详细介绍。 Image组件将是给定UI元素的背景纹理。前提是该UI元素支持此类属性。开箱即用,每个UI元素都使用引擎设计的每个UI元素的默认纹理和变换。 最后,Panel组件显示面板背景图像的外观和感觉。稍后也会详细介绍! |
我们继续使用变换工具来调整面板的位置和大小。我们希望将面板锚定到屏幕的**左上角**。使用预设的锚点设置,我们可以简单地使用所需的**锚点预设**,如图7所示。
要调出“锚点预设”配置窗口,请单击图7中标记为1(一)的区域。您将获得一个新窗口,在图7中标记为2(二),列出了所有预设。从现有预设中,选择代表屏幕左上角的那个。
图 7-显示预配置的锚点
当您更改锚点设置时,您会注意到**Rect Transform**属性也已更新,以反映**Panel** GameObject的新锚点。默认情况下,它会填充整个Canvas GameObject的表面区域。
继续并将**Rect Transform**的**宽度**和**高度**属性分别设置为**200 x 100**。您的视图中应该有以下视图:
图 8-锚定和大小设置后的面板外观
现在,我们希望通过定位将 Panel GameObject 移动到靠近“左上角”的位置。使用定位工具,您可以将其移动到作为设计者您喜欢的区域。您也可以使用“Rect Transform”为“Pos X”和“Pos Y”组件应用特定的数字。在这种情况下,“Pos Z”将始终为 0。
图 9-定位到左上角后的面板
图 9 中有几点需要注意。首先,请注意,在场景视图中,在设计时,面板的实际位置似乎在右侧。这是因为我们从后面看画布对象。那是什么意思?
嗯,看看红色、绿色和蓝色这三个彩色箭头。这些箭头代表3D环境中每个轴的正方向。如果你到现在还没有弄清楚,红色箭头代表X轴,箭头总是指向正方向。绿色箭头代表Y轴,箭头总是指向正方向。蓝色箭头代表Z轴,箭头总是指向正方向。
在图 9 中,查看标有数字 2(二)和 3(三)的区域。您会注意到**Rect Transform**已修改为将*Pos X*设置为 110,*Pos Y*设置为 -60。这是从锚点(即左上角)的位移。
这没有什么神奇之处,您只需要摆弄定位和缩放,就能为您的UI设计找到合适的感觉。
现在我们已经添加了面板,让我们继续添加将显示原始对象位置和旋转的标签。
在层级窗口中,右键单击Panel GameObject并选择UI->Text。
图 10-第一个文本 UI 元素
就像我们的面板UI元素一样,我们来修改文本UI元素的锚点,将其设置为左上角,然后使用**Rect Transform**或设计时定位工具来调整它,并将其正确放置在面板的左上角。同时将文本UI元素的名称更改为lblCube1Position。
图 11-配置后 Cube1 原始标签的位置
继续并按照我们刚才的操作,放置另外两个文本UI元素,并根据图4中的草图定位它们。如果您一切都做对了,您将看到如下图所示的内容:
图 12-设计时原始对象的最终面板外观
好的,现在我们已经设计好一个面板,用于显示在第一部分设计时创建的原始对象的位置和旋转。现在我们需要编写一些代码来实际更新我们的标签。我们需要更新我们之前处理原始对象旋转的脚本,该脚本来自我们系列的第二部分。
如果您还记得,我们为每个立方体原始对象创建了三个单独的脚本,并在该脚本的**Update()**中将旋转应用于原始对象。现在,我们需要更新这些脚本,以便能够更新每个原始对象的标签。让我们继续修改Cube1的脚本。
我们将cube1Rotate.cs脚本修改为以下列表
using UnityEngine; using UnityEngine.UI; using System.Collections; public class cube1Rotate : MonoBehaviour { // variable to store Text UI Element public Text lblCube1Position; private string position = ""; // 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); if (this.lblCube1Position != null) { position = string.Format("P:<{0:F2},{1:F2},{2:F2}>; R:<{3:F2},{4:F2},{5:F2}>", this.transform.localPosition.x, this.transform.localPosition.y, this.transform.localPosition.z, this.transform.localEulerAngles.x, this.transform.localEulerAngles.y, this.transform.localEulerAngles.z); this.lblCube1Position.text = position; } } }
当您查看上面的列表时,您会注意到我们创建了一个类型为**Text**的公共变量。还要注意,我们必须导入**UnityEngine.UI**命名空间才能在我们的代码中使用UI对象。
然后,在我们的**Update()**函数中,我们添加了几行代码来检查**lblCube1Position**变量是否已初始化,如果已初始化,则我们继续构建一个表示给定对象位置和旋转的字符串,最后我们将新构建的字符串分配给**lblCube1Position** UI元素的文本字段。
第一步是更新我们的脚本,使其能够处理所需信息的更新和显示。第二步是确保我们新创建的脚本在设计器中得到正确配置。
图 13-lblCube1Position Text 变量的设计时赋值
如图13所示,您需要将Text UI元素分配给脚本中定义的**Text**变量。一种方法是选择场景中带有cube1Rotate.cs的GameObject,然后将lblCube1Position Text UI元素从**Hierarchy Window** (1) 拖放到脚本文件中定义的Text变量 (2) 和 (3) 上,如**Inspector Window**中所示。
当您运行此程序更新时,您将看到顶部的Text UI元素现在将显示位置和旋转。
注意:我不得不调整我的Panel UI元素和Text UI元素,因为文本长度的原因。这将在您的UI设计过程中发生,这是预期的情况。您需要相应地调整**Rect Transform**,以考虑屏幕上显示的数据量。
我已将我的 UI 元素更新如下:
-
面板UI元素具有以下配置:PosX=130; PosY=-40; Width=250; Height=75。
-
lblCube1Position 的文本 UI:PosX=125; PosY=-20; Width=230; Height=20; Font Size=12。
-
lblCube2Position 的文本 UI:PosX=125; PosY=-40; Width=230; Height=20; Font Size=12。
-
lblCube3Position 的文本 UI:PosX=125; PosY=-60; Width=230; Height=20; Font Size=12。
图 14-显示 Cube1 标签
请按照我们对Cube1执行的相同概念,将其应用于Cube2和Cube3。您需要先更新脚本,然后,在设计视图中,将相应的Text UI元素分配给脚本中支持Text对象的变量。
cube2Rotate.cs 的更新列表
using UnityEngine; using UnityEngine.UI; using System.Collections; public class cube2Rotate : MonoBehaviour { // variable to store Text UI Element public Text lblCube1Position; private string position = ""; // 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); if (this.lblCube1Position != null) { position = string.Format("P:<{0:F2},{1:F2},{2:F2}>; R:<{3:F2},{4:F2},{5:F2}>", this.transform.localPosition.x, this.transform.localPosition.y, this.transform.localPosition.z, this.transform.localEulerAngles.x, this.transform.localEulerAngles.y, this.transform.localEulerAngles.z); this.lblCube1Position.text = position; } } }
cube3Rotate.cs 的更新列表
using UnityEngine; using UnityEngine.UI; using System.Collections; public class cube3Rotate : MonoBehaviour { // variable to store Text UI Element public Text lblCube1Position; private string position = ""; // 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); if (this.lblCube1Position != null) { position = string.Format("P:<{0:F2},{1:F2},{2:F2}>; R:<{3:F2},{4:F2},{5:F2}>", this.transform.localPosition.x, this.transform.localPosition.y, this.transform.localPosition.z, this.transform.localEulerAngles.x, this.transform.localEulerAngles.y, this.transform.localEulerAngles.z); this.lblCube1Position.text = position; } } }
图 15-设计时原始对象位置和旋转的最终面板外观
如您所见,在屏幕上正确设计和定位 UI 确实需要时间。其中包含的内容远不止本文所示,但我相信对于任何有兴趣深入研究的人来说,这都是一个很好的开始。另外,别忘了,正如前面提到的,在引擎的早期版本中,您必须通过脚本完成我们刚刚做的一切!
创建用户界面 - 面板 2
我们继续创建第二个面板,它将做同样的事情,但用于动态创建的原始对象。我不会在这里重复这些步骤,因为它们基本相同,但位置会有所不同。
您的环境应如下所示:
图 16-配置面板 2 的屏幕视图
我们还需要修改处理动态原始对象的脚本 *createPrimitivesFromInput.cs* 如下:
using UnityEngine; using UnityEngine.UI; using System.Collections; public class createPrimitivesFromInput : MonoBehaviour { private GameObject cube1; // represents our Cube1' private GameObject cube2; // represents our Cube2' private GameObject cube3; // represents our Cube3' private bool PRIMITIVES_CREATED; public Text lblCube1Position; public Text lblCube2Position; public Text lblCube3Position; private string cube1position = ""; private string cube2position = ""; private string cube3position = ""; // Use this for initialization void Start () { this.PRIMITIVES_CREATED = false; // update position information for each primitive if(this.lblCube1Position != null){ this.lblCube1Position.text = this.cube1position; } if(this.lblCube2Position != null){ this.lblCube2Position.text = this.cube2position; } if(this.lblCube3Position != null){ this.lblCube3Position.text = this.cube3position; } } // Update is called once per frame void Update () { if (!this.PRIMITIVES_CREATED) { if (Input.GetKey (KeyCode.Space)) { this.CreateMyPrimitives(); } }else{ // apply the y-axis transform to Cube1' if(Input.GetKey(KeyCode.A)){ this.cube1.transform.Rotate(new Vector3(0,1,0), 1); } // apply the x-axis transform to Cube2' if(Input.GetKey(KeyCode.B)){ this.cube2.transform.Rotate(new Vector3(1,0,0), 1); } // apply the z-axis transform to Cube3' if(Input.GetKey(KeyCode.C)){ this.cube3.transform.Rotate(new Vector3(0,0,1), 1); } // code for the movement of Cube1' forward if(Input.GetKey(KeyCode.UpArrow)){ this.cube1.transform.Translate(Vector3.forward * Time.deltaTime); } // code for the movement of Cube1' backward if(Input.GetKey(KeyCode.DownArrow)){ this.cube1.transform.Translate(Vector3.back * Time.deltaTime); } // code for the movement of Cube1' left if(Input.GetKey(KeyCode.LeftArrow)){ this.cube1.transform.Translate(Vector3.left * Time.deltaTime); } // code for the movement of Cube1' right if(Input.GetKey(KeyCode.RightArrow)){ this.cube1.transform.Translate(Vector3.right * Time.deltaTime); } // update position information for each primitive if(this.lblCube1Position != null){ this.cube1position = string.Format("P:<{0:F2},{1:F2},{2:F2}>; R:<{3:F2},{4:F2},{5:F2}>", this.cube1.transform.localPosition.x, this.cube1.transform.localPosition.y, this.cube1.transform.localPosition.z, this.cube1.transform.localEulerAngles.x, this.cube1.transform.localEulerAngles.y, this.cube1.transform.localEulerAngles.z); this.lblCube1Position.text = this.cube1position; } if(this.lblCube2Position != null){ this.cube2position = string.Format("P:<{0:F2},{1:F2},{2:F2}>; R:<{3:F2},{4:F2},{5:F2}>", this.cube2.transform.localPosition.x, this.cube2.transform.localPosition.y, this.cube2.transform.localPosition.z, this.cube2.transform.localEulerAngles.x, this.cube2.transform.localEulerAngles.y, this.cube2.transform.localEulerAngles.z); this.lblCube2Position.text = this.cube2position; } if(this.lblCube3Position != null){ this.cube3position = string.Format("P:<{0:F2},{1:F2},{2:F2}>; R:<{3:F2},{4:F2},{5:F2}>", this.cube3.transform.localPosition.x, this.cube3.transform.localPosition.y, this.cube3.transform.localPosition.z, this.cube3.transform.localEulerAngles.x, this.cube3.transform.localEulerAngles.y, this.cube3.transform.localEulerAngles.z); this.lblCube3Position.text = this.cube3position; } } } // This function will be called when the Space Bar on the keyboard is pressed void CreateMyPrimitives(){ if (!this.PRIMITIVES_CREATED) { // 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); this.PRIMITIVES_CREATED = true; } } }
更新脚本并在设计器中将正确的文本UI元素分配给相应的变量并运行程序后,您将看到以下内容:
图 17-运行新更新的程序
如果您还记得本系列的第三部分,我们实现了从程序接收输入。生成动态原始对象的输入是键盘上的空格键。当您按下空格键时,您将看到以下屏幕:
图 18-动态原始对象创建时
请注意,新创建的原始对象没有旋转,那是因为我们在第 3 部分中让它们根据键盘输入旋转。我们将 A 键分配给 Cube1' 旋转,B 键分配给 Cube2' 旋转,C 键分配给 Cube3' 旋转。当您这样做时,您将获得如下更新:
图 19-显示所有位置和旋转信息
创建用户界面 - 创建按钮和事件处理
到目前为止,我们已经了解了如何在场景中放置**面板**和**文本**(标签)UI元素。现在让我们看看如何引入**按钮**。一般来说,**按钮**用于用户点击,点击后,程序中会触发某个特定的事件或动作。
这里的概念基本相同。按钮UI元素的放置、定位和缩放将与我们迄今为止讨论的面板和文本UI元素完全相同。
为了放置一个按钮,我们将在**Hierarchy Window**中选择**Canvas** GameObject,然后右键单击以调出上下文菜单。从那里选择UI->Button,在场景中放置一个Button UI元素。
图 20-在场景中创建的按钮
请注意图 20 中,**按钮**对象本身有一个子**文本**对象。那是按钮的标签!另请注意,**按钮**对象具有类似的属性,用于在场景中锚定和定位 UI 元素。让我们根据图 4 修改**按钮**的锚定和定位。
按钮将锚定到屏幕的右上角。
图 21-我们按钮的最终位置
|
查看“检查器窗口”,一旦您在场景中选择“按钮”UI元素,您会注意到“按钮对象”特有的一些属性。 注意:我已经折叠了我们目前不讨论且您已熟悉的组件,即**Rect Transform**,以便我们有足够的屏幕空间来显示**Button (script)**组件。 在 Unity 3D 中,您需要创建每个处理系统中特定函数的功能。如果您习惯于在 Windows 或 Web 环境中双击 UI 对象并让它自动为您创建具有适当参数的函数,这最初会有点不舒服。 查看图 22,您会注意到**On Click()**事件已被定义为占位符,但尚未与任何内容关联!您需要将其与一个函数关联!为此,您需要单击图中 (1) 指示的 (+) 图标。当您这样做时,您将得到以下结果:
|
请注意,**OnClick()**占位符未分配任何内容。这就是我们将分配负责处理场景中**按钮**的**OnClick**事件的GameObject的地方。
我们将使用 *createPrimitiveFromInput.cs* 脚本来处理按钮点击事件。所以我们继续修改源代码如下:
using UnityEngine; using UnityEngine.UI; using System.Collections; public class createPrimitivesFromInput : MonoBehaviour { private GameObject cube1; // represents our Cube1' private GameObject cube2; // represents our Cube2' private GameObject cube3; // represents our Cube3' private bool PRIMITIVES_CREATED; public Text lblCube1Position; public Text lblCube2Position; public Text lblCube3Position; private string cube1position = ""; private string cube2position = ""; private string cube3position = ""; // Use this for initialization void Start () { this.PRIMITIVES_CREATED = false; // update position information for each primitive if(this.lblCube1Position != null){ this.lblCube1Position.text = this.cube1position; } if(this.lblCube2Position != null){ this.lblCube2Position.text = this.cube2position; } if(this.lblCube3Position != null){ this.lblCube3Position.text = this.cube3position; } } // Update is called once per frame void Update () { if (!this.PRIMITIVES_CREATED) { if (Input.GetKey (KeyCode.Space)) { this.CreateMyPrimitives(); } }else{ // apply the y-axis transform to Cube1' if(Input.GetKey(KeyCode.A)){ this.cube1.transform.Rotate(new Vector3(0,1,0), 1); } // apply the x-axis transform to Cube2' if(Input.GetKey(KeyCode.B)){ this.cube2.transform.Rotate(new Vector3(1,0,0), 1); } // apply the z-axis transform to Cube3' if(Input.GetKey(KeyCode.C)){ this.cube3.transform.Rotate(new Vector3(0,0,1), 1); } // code for the movement of Cube1' forward if(Input.GetKey(KeyCode.UpArrow)){ this.cube1.transform.Translate(Vector3.forward * Time.deltaTime); } // code for the movement of Cube1' backward if(Input.GetKey(KeyCode.DownArrow)){ this.cube1.transform.Translate(Vector3.back * Time.deltaTime); } // code for the movement of Cube1' left if(Input.GetKey(KeyCode.LeftArrow)){ this.cube1.transform.Translate(Vector3.left * Time.deltaTime); } // code for the movement of Cube1' right if(Input.GetKey(KeyCode.RightArrow)){ this.cube1.transform.Translate(Vector3.right * Time.deltaTime); } // update position information for each primitive if(this.lblCube1Position != null){ this.cube1position = string.Format("P:<{0:F2},{1:F2},{2:F2}>; R:<{3:F2},{4:F2},{5:F2}>", this.cube1.transform.localPosition.x, this.cube1.transform.localPosition.y, this.cube1.transform.localPosition.z, this.cube1.transform.localEulerAngles.x, this.cube1.transform.localEulerAngles.y, this.cube1.transform.localEulerAngles.z); this.lblCube1Position.text = this.cube1position; } if(this.lblCube2Position != null){ this.cube2position = string.Format("P:<{0:F2},{1:F2},{2:F2}>; R:<{3:F2},{4:F2},{5:F2}>", this.cube2.transform.localPosition.x, this.cube2.transform.localPosition.y, this.cube2.transform.localPosition.z, this.cube2.transform.localEulerAngles.x, this.cube2.transform.localEulerAngles.y, this.cube2.transform.localEulerAngles.z); this.lblCube2Position.text = this.cube2position; } if(this.lblCube3Position != null){ this.cube3position = string.Format("P:<{0:F2},{1:F2},{2:F2}>; R:<{3:F2},{4:F2},{5:F2}>", this.cube3.transform.localPosition.x, this.cube3.transform.localPosition.y, this.cube3.transform.localPosition.z, this.cube3.transform.localEulerAngles.x, this.cube3.transform.localEulerAngles.y, this.cube3.transform.localEulerAngles.z); this.lblCube3Position.text = this.cube3position; } } } // This function will be called when the Space Bar on the keyboard is pressed public void CreateMyPrimitives(){ if (!this.PRIMITIVES_CREATED) { // 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); this.PRIMITIVES_CREATED = true; } } }
在这种情况下,我们所要做的就是修改我们的**void CreateMyPrimitives()**函数,使其成为一个公共函数,例如**public void CreateMyPrimitives()**。
注意:所有事件函数都必须是 void 和 public!
这将是第一步。创建脚本来为我们处理事件。第二步将是确保将脚本分配到的对象分配给图23中列出的按钮元素的事件处理程序。
在我们的例子中,*createPrimitivesFromInput.cs* 附加到我们的**Main Camera** GameObject。所以您将选择**Button** GameObject 来调出**Inspector Window**,然后将**Main Camera** GameObject 拖放到**OnClick()**占位符中。结果将是以下内容:
图 24-主摄像机对象已分配给 OnClick() 按钮事件
下一步是从图24中箭头所指的下拉列表中获取函数。您将看到许多选项。我们对在我们的类中定义的函数感兴趣。所以选择我们的类,然后从类中选择代表我们将处理事件的函数。
图 25-显示按钮事件的类和函数选择
您现在已经创建了在 Unity 3D 中的第一个交互式 UI!恭喜!现在运行程序,您可以使用键盘上的空格键或屏幕上刚刚创建的按钮来实例化动态原始对象!
关注点
用户界面设计和开发本身是一个很大的话题,需要时间来掌握。在本文中,我们介绍了一些最基本的内容,以帮助您在Unity 3D中入门。目的是向您介绍概念,然后让您走自己的路,并在此基础上自行改进。
历史
这是我将慢慢贡献给Code Project社区的系列文章的第四篇。
-
Unity 3D – 游戏编程 – 第 4 部分
Unity 3D 网络文章
Unity 3D Leap Motion 和 Oculus Rift 文章