目录
说明:“Player”为游戏场景中的一个对象,在本游戏中就是一个有C#脚本控制的具有智慧的球体。
概述
通过创建经典的竞技场射击游戏来学习 Unity 中脚本的基本知识。本示例是Georgi Ivanov发表的英文文章的翻译,是学习Unity 应用C#脚本的很好学习资料,作为初学者请依该文方法练习一遍,保证您对Unity脚本编程已经掌握。
![最终效果图](https://img-blog.csdnimg.cn/img_convert/2ee423ea6cf61f9d22b689b06d295a90.gif)
Unity 的很多力量都来自其丰富的脚本语言C#。您可以使用它来处理用户输入、操作场景中的对象、检测碰撞、生成新的 GameObject 和在场景周围投射方向光线,以帮助处理游戏逻辑。这听起来可能令人生畏,但 Unity 公开了记录良好的 API,这些 API 使这些任务变得轻而易举 — 即使对于新手开发人员!在本教程中,您将创建一个自上而下的射击游戏,该游戏使用 Unity 脚本来处理敌人的生成、玩家控制、发射弹丸和游戏的其他重要方面。
注意:本教程是为 Unity 5.3 或更高版本编写的。您可以在此处下载最新版本的Unity。虽然 Unity 也支持 UnityScript 和 Boo,但 C# 是大多数开发人员倾向于使用的编程语言,并且有充分的理由。C# 被全球数百万开发人员用于应用程序、Web 和游戏开发,并且有大量的信息和教程可以帮助您。
一、准备工作
下载GameSample初学者项目,解压缩,并在 Unity 中打开创建的文件夹,您应该看到:
在"Scene view"视图中有一个小竞技场,这将是游戏的战场,一个相机和一个光源。如果您的布局与屏幕截图中不同,请选择右上拉菜单,然后更改为Default。
二、开始创建步骤
1、创建“Player”
在"Hierarchy"中,单击"Create"按钮,然后从 3D 部分中选择" Sphere"。将 Sphere定位在 (X:0, Y:0.5, Z:0),并命名“Player”:
Unity 使用entity-component来构建其游戏对象。这意味着所有 GameObject 都是组件的容器,可以附加这些容器来赋予其行为和属性。下面是 Unity 内置的组件的一些示例:
Tranform:每个游戏对象都附带这个组件。它持有游戏对象的位置、旋转和比例。
Box Collider:立方体形状的碰撞器,可用于检测碰撞。
Mesh Filter:用于显示 3D 模型的网格数据。
“Player”游戏对象将需要响应与场景中其他对象的碰撞。为此,请在"Hierarchy"窗口中选择“Player”,然后在"Inspector "窗口中的单击"Add Component"按钮,在弹出的菜单中选择"Physics > Rigidbody",这将向“Player”添加一个刚体组件,以便它可以使用 Unity 的物理引擎。调整 Rigidody 的值,像这样:将拖动设置为 1,将角拖动设置 0,并选中"Freeze Position"旁边的 Y复选框。这将确保“Player”无法上下移动,并且在旋转时没有添加阻尼。
2、创建“Player” 移动脚本
现在,“Player” 已经准备好了,是时候创建脚本,从键盘开始输入并移动播放机了。在"Project"窗口中,单击" Create"按钮并选择" Folder"。命名新文件夹Scripts并在其中创建一个名为“Player” 的子文件夹。在“Player” 文件夹中,单击"Create"按钮并选择"C#Scripts"。命名您的新脚本PlayerMovement。步骤如下所示:
注:使用这样的文件夹可以方便地按角色组织所有内容,并减少混乱。您将制作多个脚本供“Player”使用,因此向它提供自己的文件夹是有意义的。
双击PlayerMovement.cs脚本。这将打开加载脚本时的首选代码编辑器。Unity 附带了在所有程序上预安装的 MonoDevelop,Windows 用户可以选择安装 Visual Studio来取代它,并在运行安装程序时使用。本教程假定您使用的是 MonoDevelop,但 Visual Studio 用户应该能够毫无问题。一旦您选择的编辑器打开,您将看到:
这是 Unity 在新脚本中生成的默认的类。它继承了 MonoBehaviour 基类,这样脚本才能够在游戏中运行,同时还有一些特殊的方法对特定事件作出响应。如果你是一个 iOS 开发者,这个类就好比 UIViewCotnroller。Unity 会在运行脚本时以特定顺序调用多个方法。最常见的几个方法包括:
Start()
:在脚本进行第一次更新之前,将调用此方法一次。Update()
:当游戏正在运行并启用脚本时,此方法将触发每帧。OnDestroy()
:在游戏对象附加到此脚本被销毁之前,将调用此方法。OnCollisionEnter()
:当对撞机或刚体连接到其他碰撞器或刚体时,将调用此方法。
在 Start() 方法前,添加两行代码:
<span style="color:#ffffff"><span style="color:#cc99cd">public</span> <span style="color:#f08d49">float</span> acceleration;
<span style="color:#cc99cd">public</span> <span style="color:#f08d49">float</span> maxSpeed;</span>
这是它应该的格式:
这意味着这两个变量能够在检视器中看到并修改,而无需在脚本和编辑器中来回切换。acceleration 表示玩家的速度随着时间递增。maxSpeed 则表示速度的上限。在它们后面声明这几个变量:
<span style="color:#ffffff"><span style="color:#cc99cd">private</span> Rigidbody rigidBody;
<span style="color:#cc99cd">private</span> KeyCode[] inputKeys;
<span style="color:#cc99cd">private</span> Vector3[] directionsForKeys;</span>
不能通过检查器设置私有变量,开发人员有责任在适当的时间初始化它们。rigidBody 用于保存一个对刚体组件的引用,即附着在 Player GameObject 上的刚体组件。
inputKeys 是一个键盘码的数组,用于检查输入。directionsForKeys 用于保存一个 Vector3 变量数组,这些变量表示方向数据。将 Start() 方法修改为:
<span style="color:#ffffff"><span style="color:#70a2ff"><span style="color:#cc99cd">void</span> <span style="color:#70a2ff">Start</span> ()</span> {
inputKeys = <span style="color:#cc99cd">new</span> KeyCode[] { KeyCode.W, KeyCode.A, KeyCode.S, KeyCode.D };
directionsForKeys = <span style="color:#cc99cd">new</span> Vector3[] { Vector3.forward, Vector3.left, Vector3.back, Vector3.right };
rigidBody = GetComponent<Rigidbody>();
}</span>
此代码链接每个键的相应方向,例如按W将对象向前移动。最后一行获得了一个对所附着的刚体组件的引用,将它保存到 rigidBody 变量以便使用。要真正移动玩家的角色,还需要处理键盘输入。将 Update() 修改为 FixedUpdate() 并加入以下代码:
<span style="color:#ffffff"><span style="color:#999999">// 1</span>
<span style="color:#70a2ff"><span style="color:#cc99cd">void</span> <span style="color:#70a2ff">FixedUpdate</span> ()</span> {
<span style="color:#cc99cd">for</span> (<span style="color:#f08d49">int</span> i = <span style="color:#f08d49">0</span>; i < inputKeys.Length; i++){
<span style="color:#cc99cd">var</span> key = inputKeys[i];
<span style="color:#999999">// 2</span>
<span style="color:#cc99cd">if</span>(Input.GetKey(key)) {
<span style="color:#999999">// 3</span>
Vector3 movement = directionsForKeys[i] * acceleration * Time.deltaTime;
}
}
}</span>
这里有几件重要的事情:
FixedUpdate()
速率与帧速率无关,它和刚体一起使用。和以尽量快的速度运行不同,这个方法会以固定的间隔执行。- 此循环检查是否按下了有效键。
- 获取按下键的方向,将其乘以加速度和完成最后一帧的秒数。这将生成用于移动对象的方向矢量(X、Y 和 Z 轴上的速度)。就可以用它来移动 Player 对象了。
如果您是游戏编程的新朋友,您可能会问自己为什么必须乘以Time.detalTime。虽然游戏运行帧速率(或帧每秒)会因硬件和承受的压力而异,但这可能会导致在功能强大的计算机上发生太快,在较弱的机器上速度过慢,这可能会导致不希望发生的行为。通常的办法是,当需要按每帧执行一个动作时,都乘上 Time.deltaTimeTime.
在 FixedUpdate() 方法后添加:
<span style="color:#ffffff"><span style="color:#70a2ff"><span style="color:#cc99cd">void</span> <span style="color:#70a2ff">movePlayer</span>(<span style="color:#f08d49">Vector3 movement</span>)</span> {
<span style="color:#cc99cd">if</span>(rigidBody.velocity.magnitude * acceleration > maxSpeed) {
rigidBody.AddForce(movement * <span style="color:#f08d49">-1</span>);
} <span style="color:#cc99cd">else</span> {
rigidBody.AddForce(movement);
}
}</span>
这个方法向刚体施加一个力,驱使它移动。乳沟当前速度超过 maxSpeed,这个力会转成反方向,让玩家减速,将速度有效地限制在最大速度下。在 FixedUpdate() 方法中,在 if 语句右括号结束之前,添加:
<span style="color:#ffffff">movePlayer(movement);</span>