Unity 3D 脚本开发基础教程一口气讲完!(。・∀・)ノ

Unity 3D 脚本

脚本可以说是一款游戏的灵魂,Unity 3D 脚本用来界定用户在游戏中的行为,是游戏制作中不可或缺的一部分,它能实现各个文本的数据交互并监控游戏运行状态。

以往的 Unity 3D 主要支持:

  1. JavaScript

  1. C#

  1. Boo

但是以 Boo 作为开发语言的使用者非常少,若继续投入大量资源来支持这一开发语言显然不值得,所以在 Unity 5.0后,不再支持 Boo。

目前,Unity 3D 官方教程及实例基本都是以 JavaScript 和 C# 为开发语言。

JavaScript

JavaScript 学习难度低、学习门槛低,是十分适合初学者的一门脚本编辑语言。

注:

  • 建议初学者选择 JavaScript 作为入门阶段的脚本编辑语言。

C#

C# 编程是基于 C 和 C++ 编程语言的,相对于 JavaScript 难度较高。但是 C# 在编程理念上更符合 Unity 3D 的引擎原理。

注:

  • 如果你有一定的 C# 编程基础,C# 将是更好的选择。

  • 如果没有 C# 编程基础,你可以先选择 JavaScript 。通过不断的学习积累下,到达了 Unity 3D 的进阶阶段,可以再改用 C# 。

Unity 3D JavaScript 脚本基础 

Unity 3D 中的 JavaScript 也被称作 UnityScript,和基于浏览器的 JavaScript 有较大的区别。

变量

JavaScript 有4种变量:

  1. 数值变量。
  2. var a=1000;
  3. var b=3.1415926;

  1. 字符串变量。

是由单引号或双引号括起来的 Unicode 字符序列。

  1. 布尔值。

只有 true 和 false 。用来描述某个事物为真或为假。

  1. 数组

是数据的集合,数组中的每一个元素都有自己独有的下标,下标从0开始计数。

表达式和运算符

表达式是关键字、变量、常量和运算符的组合,可以用于执行运算、处理字符或测试数据。

运算符:

  • 算数运算符(二元运算符)指的是数学中最基本的加减乘除等运算。
算数运算符描述示例
+两变量相加a + b
-两变量相减a - b
*两变量相乘a * b
/两变量相除a / b
%求余数a % b
++变量递增a++
--变量递减a--

  • 相等运算符用于比较两个值。以返回布尔值作为比较结果(可归为关系运算符)。
相等运算符描述示例
==判断两变量是否相等a == b
===判断两变量是否完全相等a === b
!==判断两变量是否不相等a !== b
!===判断两变量是否完全不相等a !=== b

  • 关系运算符用于判断两个值之间的关系,以返回布尔值作为判断结果。
关系运算符描述示例
<小于a < b
<=小于或等于a <= b
>大于a > b
>=大于或等于a >= b

  • 赋值运算符用于将=右边的值赋给左边的变量,该值为任意类型的值。

 
  1. var a = 1;
  2. var b = a;
  3. var c = abc;

注:

b = a = 1,而不是字母a。

  • 逻辑运算符用于针对布尔值的操作。
逻辑运算符描述
&&并(与)
||
!

  • 其他运算符。

除以上常见运算符外,还有:

  • 按位运算符。

  • 条件运算符。

  • typeof 运算符。

  • new 运算运算符。

  • delete 运算符。

等等。

语句

JavaScript 提供了完整的基础编程语句,如下:

  1. var 语句。

 
  1. var 变量名 = 值

  1. 函数定义语句 。

 
  1. function 函数名(参数){
  2. 执行语句
  3. }

  1. 条件和分支语句。

  • 条件语句 if...else 完成程序流程快中的分支功能:如果其中的条件成立,则执行if后子语句;如果条件不成立,则执行else后子语句。

 
  1. if(条件){
  2. 子语句1
  3. } else{
  4. 子语句2
  5. }

  • 分支语句 switch可以根据一个变量的不同取值采取不同的处理方法。

如果表达式中取的值与分支语句中的任何一条子语句都不匹配,将执行 default(默认语句)中的语句。

 
  1. switch(expression){
  2. case label1:子语句1
  3. case label2:子语句2
  4. ...
  5. default:子语句n
  6. }

  1. 注释语句:///*....*/用于做注释,便于阅读代码,理解代码。

 
  1. //这是单行注释
  2. /*
  3. 这可以是多行注释
  4. 这可以是多行注释
  5. 这可以是多行注释
  6. */

  1. 循环语句:forfor...inwhile

  • for语句中只要条件成立,循环体语句就会循环执行。

 
  1. for(条件){
  2. 子语句
  3. }

注:

若条件一直成立,将进入死循环,要避免这种情况出现。

  • while语句所控制的循环不断的判断条件,如果条件始终成立,则一直循环直到条件不再成立。

 
  1. while(条件){
  2. 子语句
  3. }

  • for...in语句用于将一个对象的属性不断赋值给变量,直到将该对象的属性都枚举完。

 
  1. for (variablename in object){
  2. statement
  3. }

注:

  • break 语句用于结束循环,并执行循环体的下一条语句。

  • continue 语句用与结束本次循环,并马上开始下一次循环。

函数

函数(function)是一个可执行的程序段。函数被定义后,可以多次被程序调用。

函数是命名的程序段,这个程序段可以被当作一个整体引用和执行。使用函数时要注意以下几点:

  • 函数由关键字 function 定义(也可由 Function 构造函数构造)。

  • 使用 function 关键字定义的函数在一个作用域内是可以在任意处调用的。

  • 函数名是调用函数时引用的名称,它是大小写敏感的,调用函数时要注意这一点。

  • return 语句用于返回表达式的值

Unity 3D C# 脚本基础

在 Unity 3D 中编程首选 C# 作为编程脚本语言。

变量

  1. 变量定义。

任何一个脚本中都缺不了变量,C# 脚本也不例外。

变量主要用于存储数据,在 Unity 3D 的脚本中,每个变量必须拥有唯一的名称,脚本在识读变量时采用的是字符串匹配方式,所以对变量名称大小写敏感。

一旦 Unity 3D 脚本挂到某个 Unity 3D 对象上,在 Unity 3D 的属性面板中就会显示出该脚本中的各个公共变量。开发人员也可以在属性面板中对公共变量的值进行设置,设置后的值将会影响脚本的运行,相当于在脚本中对该变量进行了赋值。

在 Unity 3D 中,定义 C# 变量的格式如下:

 
  1. 数据类型 变量名称
  2. int age;

  1. 变量赋值。

可以通过对变量赋值来对其初始化,赋值时使用赋值运算符“=”。

赋值的格式有两种,一种是

 
  1. int age;
  2. age=5;

另一种是以字面形式初始化,形式如下:

 
  1. int age=5;

  1. 变量的数据类型。

变量有 6 种数据类型。

  • 整数类型

整数类型的变量取整数数值。C# 将整数分为 8 种类型,如下表所示。使用时,根据数值的可能大小,选择范围最小的类型,一般常用的类型为 short、int 和 long 3 种。例如:

 
  1. byte classSize=23;
  2. ushort student=2344;
选项描述取值范围
sbyte有符号 8 位整数-128 〜127
byte无符号 8 位整数0 〜255
short有符号 16 位整数-32768〜32767
ushort无符号 16 位整数0〜65535
int有符号 32 位整数-2147489648〜2147483647
uint无符号 32 位整数0〜42994967295
long有符号 64 位整数-2⁶³ 〜2⁶³
ulong无符号 64 位整数0 〜2⁶⁴

  • 浮点类型

浮点类型变量主要用于处理含有小数的数值数据。根据小数位数不同,C# 提供了单精度浮点类型 float 和双精度浮点类型 double,例如:

 
  1. float angles=36.5f;
  2. double rate=0.253D;

C# 的浮点类型如下表所示。

选项描述取值范围
float32位单精度浮点类型-2¹²⁸〜2¹²⁸
double64位单精度浮点类型-2¹⁰²⁴〜2¹⁰²⁴

  • 布尔类型

布尔(bool)类型表示真或假,布尔类型变量的值只能是 true 或 false,不能将其他的值赋给布尔类型。例如:

 
  1. bool b=true;

在定义全局变量时,若没有特殊要求,不用对整数类型、浮点类型和布尔类型的变量进行初始化,整数类型和浮点类型的变量默认初始化为 0,布尔类型的变量默认初始化为 false

  • 字符类型

为保存单个字符,C# 支持字符(char)类型,字符类型的字面量是用单引号括起来的。一些特殊字符要用反斜线\后跟一个字符表示,称为转义字符,如下表所示。

转义字符描述
\'单引号
\"双引号
\\反斜线
\0
\a报警
\b退格
\f换页
\n换行
\r回车
\t水平制表符
\v垂直制表符

  • 引用类型

引用类型是构建 C# 应用程序的主要数据类型,C# 的所有引用类型均派生自 System.Object

引用类型可以派生出新的类型,也可以包含空(null)值。引用类型变量的赋值只复制对象的引用,而不复制对象本身。 枚举类型 枚举类型为定义一组可以赋给变量的命名整数常量提供了一种有效的方法。

编写日期相关的应用程序时,经常需要使用年、月、日、星期等数据。可以将这些数据组织成多个不同名称的枚举类型。

使用枚举类型可以增强程序的可读性,在 C# 中使用关键字 enum 类声明枚举类型的变量,格式如下:

 
  1. enum枚举名称
  2. {
  3. 常量1=值1;
  4. 常量2=值2;
  5. ...
  6. 常量n=值n
  7. }

变量的声明

声明变量就是指定变量的名称和类型,未经声明的变量不能在程序中使用。

在 C# 中,声明一个变量由一个类型和跟在后面的一个或多个变量名组成,多个变量之间用逗号分开,声明变量以分号结束。例如:

 
  1. int age=30;
  2. float rate=20f;
  3. string name="Tom";

在 C# 的变量声明之前还可以添加访问修饰符:

  • private(默认修饰符),只能在本类中访问。

  • protected,只能在类或派生类中访问。

  • internal,只能在本项目中访问。

如果想让脚本中定义的变量在 Unity 3D 中的 Inspector 面板上显示,必须用 public 修饰。

在声明变量时,要注意变量名的命名规则: 变量名必须以字母开头; 变量名只能由字母、数字和下画线组成,而不能包含空格、标点符号、运算符等其他符号; 变量名不能与 C# 中的关键字名称相同; 变量名不能与 C# 中的库函数名称相同。

表达式和运算符

  1. 表达式。

表达式是由运算符和操作数组成的。

运算符设置对操作数进行什么样的运算,例如,加、减、乘、除都是运算符。

操作数是计算机指令的组成部分,它指出了指令执行的操作所需要的数据来源。

操作数包括文本、常量、变量和表达式等。例如:

 
  1. int i=256;
  2. i=256/2+125;
  3. int j=2;
  4. j=j*j-2;

  1. 运算符。

运算符用于执行表达式运算,会针对一个以上操作数进行运算。例如 2+3,操作数是 2 和 3,而运算符是+

运算符的种类很多,包括算术运算符、简单赋值运算符、关系运算符、位逻辑运算符、位移运算符以及特殊算术运算符等,与 JavaScript 类似,在此不再赘述。

语句

  1. 顺序执行语句。

当表达式中有多个运算符时,需要考虑运算符的执行顺序,也就是运算符的优先级。

例如,要对一本书的信息进行记录,书名为《程序设计语言》,价格为 36.5 元,书的编号为 243434。在代码中定义 3 个变量 bookNamePricebookID 分别用来保存书名、价格以及编号,并分别对这3个变量进行赋值和输出,代码如下:

 
  1. class Program
  2. {
  3. static void Main(string[]args){
  4. string bookName="程序设计语言";
  5. Console.WriteLine("书名"+bookName);
  6. float Price=36.5f;
  7. Console.WriteLine("价格"+Price);
  8. string bookID="243434";
  9. Console.WriteLine("编号"+bookID);
  10. }
  11. }

在该实例中,首先定义变量,为其赋值,然后输出变量的值,程序是自上而下地一行一行顺序执行。

  1. 选择分支语句。

在日常生活中,并非所有的事情都能按部就班地进行,程序也一样。为了实现一定的目标,经常需要改变程序语句执行的顺序。

在程序中,有时需要在满足某种条件时再执行某一语句。为了达到目标而进行选择的语句就是选择分支语句。

  • if语句。

一个 if 语句后可跟一个可选的 else 语句,else 语句在布尔表达式为假时执行。C# 中 if…else 语句的语法如下:

 
  1. if(布尔表达式){
  2. 如果布尔表达式为真执行的语句
  3. }else{
  4. 如果布尔表达式为假执行的语句
  5. }

如果布尔表达式为 true,则执行 if 块内的代码;如果布尔表达式为 false,则执行 else 块内的代码。

一个 if 语句后可跟一个可选的 else if…else 语句,这种形式可用于测试多种条件。C# 中 if…else 语句的语法如下:

 
  1. if(布尔表达式1)
  2. {当布尔表达式1为真时执行的语句}
  3. else if(布尔表达式2)
  4. {当布尔表达式2为真时执行的语句}
  5. else if(布尔表达式3)
  6. {当布尔表达式3为真时执行的语句}
  7. else
  8. {当上面的条件都不为真时执行的语句}

  • switch语句。

当程序做条件判断时,可以用 if…else 语句,但是当条件很多时,如果还用 if…else 语句是很麻烦的。为了解决这个问题,C# 设置了分支语句 switch。其语法格式如下:

 
  1. switch(值)
  2. {
  3. case值1:语句1;
  4. break;
  5. case值2:语句2;
  6. break;
  7. ····
  8. case值n:语句n;
  9. break;
  10. }

当 switch 后面的值取某一值时,就执行相应 case 后面的语句。例如,当值是值 1 时,执行语句 1;当值是值 2 时,执行语句 2……当值是 n 时,执行语句 n。

  1. 循环语句。

  • do…while 循环。

do…while 循环是在循环的尾部检查条件。 do…while 循环与 while 循环类似,但是 do…while 循环至少会确保一次循环。

C# 中的 do…while 循环语法如下:

 
  1. do{
  2. 语句
  3. }while(条件);

条件表达式出现在循环的尾部,所以循环中的语句会在条件被测试之前至少执行一次。如果条件为真,控制流会转回上面的 do,重新执行循环中的语句。这个过程会不断重复,直到给定条件变为假为止。

  • while 循环。

在 C# 中 while 循环是常用的一种循环语句,while 循环的特点是直到条件为假时才跳出循环。C# 中的 while 循环的语法如下:

 
  1. while(条件)
  2. {语句}

在这里,语句可以是一个语句,也可以是几个语句组成的代码块。条件可以是任意的表达式,当为任意非零值时都为真。当条件为真时执行循环;当条件为假时,程序流将继续执行紧接着循环的下一条语句。

while 循环的关键点是循环可能一次都不会执行。当条件测试的结果为假时,会跳过循环主体,直接执行 while 循环的下一条语句。

  • for 循环。

for 循环适合指定循环次数的应用,在使用时,需初始化一个作为计数器的变量值。例如:

 
  1. for(int i=1;i<=10;i++){
  2. Console.WriteLine("{0}, i");
  3. }

上面的 for 语句声明了计数器变量后,使用分号分开,接着给出条件判断的表达式 i2=10,再使用分号分开,最后给出对计算器变量的操作 i++。如果把 i++ 放在循环体内也是可以的。

函数

在 Unity 3D 中,C# 脚本需要预先载入类库,代码示例如下:

 
  1. using UnityEngine;
  2. using System.Collections;
  3. public class NewBehaviourScript:MonoBehaviour{
  4. }

其中,NewBehaviourScript 是脚本的名称,它必须和脚本文件的名称一致(如果不同,脚本无法在物体上被执行)。

所有游戏执行语句都包含在这个继承自 MonoBehaviour 类的自创脚本中。

Unity 3D 脚本中的常用函数如下:

  1. Update()

正常更新,创建 JavaScript 脚本时默认添加这个方法,每一帧都会由系统调用一次该方法。

  1. LateUpdate()

推迟更新,此方法在 Update() 方法执行完后调用,每一帧都调用一次。

  1. FixedUpdate()

置于这个函数中的代码每隔一定时间执行一次。

  1. Awake()

脚本唤醒,用于脚本的初始化,在脚本生命周期中执行一次。

  1. Start()

在 Update() 之前、Awake() 之后执行。Start() 函数和 Awake() 函数的不同点在于 Start() 函数仅在脚本启用时执行。

  1. OnDestroy()

当前脚本销毁时调用。

  1. OnGUI()

绘制游戏界面的函数,因为每一帧要执行多次,所以一些时间相关的函数要尽量避免直接在该函数内部使用。

  1. OnCollisionEnter()

当一个游戏对象与另外的游戏对象碰撞时执行这个函数。

  1. OnMouseDown()

当鼠标在一个载有 GUI 元素(GUI Element)或碰撞器(Collider)的游戏对象上按下时执行该函数。

  1. OnMouseOver()

当鼠标在一个载有 GUI 元素或碰撞器的游戏对象上经过时执行该函数。

  1. OnMouseEnter()

鼠标进入物体范围时执行该函数。和 OnMouseOver() 不同,OnMouseEnter() 函数只执行一次。

  1. OnMouseExit()

鼠标离开物体范围时执行该函数。

  1. OnMouseUp()

当鼠标释放时执行该函数。

  1. OnMouseDrag()

按住鼠标拖动对象时执行该函数。

Unity 3D 编写脚本

本节我们正式在 Unity 3D 中编写一个简单的脚本实例。

本节以C#Script为脚本编程语言编写实例。

创建脚本

  1. 执行 Assets → Create → C#Script 或 JavaScript 菜单命令创建一个空白脚本,将其命名为 Move。

在 Project 面板中双击 Move 打开脚本进行编写。在 Upadate()函数中写入代码。

 
  1. using UnityEngine;
  2. using System.Collections;
  3. public class Move:MonoBehaviour{
  4. void Update(){
  5. transform.Translate(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
  6. }
  7. }

Input.GetAxis()函数返回-1~1的值,在水平轴上,左方向键对应 -1,右方向键对应 1。由于目前不需要向上移动摄像机,所以 Y 轴的参数为 0

  1. 执行 Edit → Project Settings → Input(输入)菜单命令,对映射到水平和垂直方向的名称和快捷键进行修改。

链接脚本

脚本创建完成后,需要将其添加到物体上。在 Hierarchy 视图中,单击需要添加脚本的游戏物体 Main Camera(主摄像机),然后执行 Component → Script → Move 菜单命令,如下图所示,Move 脚本就链接到了 Main Camera 上。

摄像机(Camera)是向玩家捕获和显示世界的设备。通过自定义和操纵摄像机,可以自由旋转游戏视角。场景中摄像机的数量不受限制,它们可以以任何顺序放置在屏幕上的任何地方,或者只捕获屏幕的某些部分。摄像机参数如下图和下表所示。

参数描述功能
Clear Flags清除标识确定屏幕哪些部分将被清除。这是为了方便使用多个摄像机捕捉不同的游戏元素。
Background背景在完成了视图中的所有元素的绘制后以及没有天空盒的情况 下,剩余屏幕的颜色。
Culling Mask消隐遮罩包含层或忽略层将被摄像机渲染。在检视窗口向对象分配层。
Projection投影切换摄像机以模拟透视。
Perspective透视透视摄像机,由原点向外扩散性发射。即,距离越远,它的视口 区域也就越大,透视视图和人眼的视觉感受是一致的。
Orthographic正交正交摄像机,无论远近,它的视口范围永远是固定的,相机会均 匀地渲染物体,没有透视感。
Size大小当摄像机设置为正交模式时,摄影机视口的大小。
Field of view视野摄像机的视野,沿着本地 Y 轴测量,以度为单位。
Clipping Planes裁剪面摄像机从开始到结束渲染的距离。
Near相对于摄像机,绘制将发生的最近点。
Far相对于摄像机,绘制将发生的最远点。
Viewport Rect视口矩形摄像机画面显示在屏幕上的区域。
X摄像机视图的开始水平位置。
Y摄像机视图的开始垂直位置。
W宽度摄像机输出在屏幕上的宽度。
H高度摄像机输出在屏幕上的高度。
Depth深度摄像机在渲染顺序上的位置。具有较低深度的摄像机将在较高 深度的摄像机之前渲染。
Rendering Path渲染路径定义摄像机的渲染路径。
Target Texture目标纹理用于将摄像机视图输出并渲染到屏幕,一般用于制作导航图或 者画中画等效果。
Occlusion Culling遮挡剔除指定是否剔除物体背向摄像机的部分。
HDR高动态光照渲染启用摄像机的高动态范围渲染功能。

运行测试

单击播放按钮,在 Scene 视图中,使用键盘上的 W(前)、S(后)、A(左)、D(右)键移动摄像机,转换视角或位置。

注意事项

在 Unity 3D 中,C# 脚本的运行环境使用了 Mono 技术。

Mono 是指 Novell 公司致力于 .NET 开原的工程,利用 Mono 技术可以在 Unity 3D 脚本中使用所有 .NET 的相关类。

  1. 脚本中的类都继承自 MonoBehaviour 类。

Unity 3D 中所有挂载到游戏对象上的脚本中包含的类都继承自 MonoBehaviour 类。

MonoBehaviour 类中定义了各种回调方法,如 Start 、update 等等。

通过在 Unity 中创建 C# 脚本,系统模板已经包含了必要的定义。

  1. 使用 Awake 或 Start 方法初始化。

用于初始化的 C# 脚本代码必须置于 Awake 或 Start 方法中。

Awake 和 Start 的不同之处在于:

  • Awake 方法是在加载场景时,并且在所有 Start 方法之前运行。

  • Start 方法是在第一次调用 Update 或 FixedUpdate 方法之前调用。

  1. 类名必须匹配文件名。

C# 脚本中类名必须和文件名相同,否则当脚本挂载到游戏对象时,控制台会报错。

  1. 只有满足特定情况时变量才能显示在属性查看器中。

只有公有的成员变量才能显示在属性查看器中,而 private 和 protected 类型的成员变量不能显示,如果要使属性项在属性查看器中显示,它必须是 public 类型的。

  1. 尽量避免使用构造函数。

不要在构造函数中初始化任何变量,而应使用 Awake 或 Start 方法来实现。

在单一模式下使用构造函数可能会导致严重后果,因为它把普通类构造函数封装了,主要用于初始化脚本和内部变量值,这种初始化具有随机性,容易引发引用异常。因此,一般情况下尽量避免使用构造函数。

Unity 3D 实践案例一

游戏场景中出现的所有物体都属于游戏对象,游戏对象之间的交互可通过脚本的控制来实现。

创建游戏对象的方法:

  1. 将物体模型资源由 Project 视图直接拖拽到 Hierarchy 面板中。
  2. 在 Unity 3D 菜单 GameObject 中创建 Unity 3D 自带的游戏对象,如 Cube、Camera、Light 等。
  3. 利用脚本编程,动态的创建或删除游戏对象。

本案例使用第三种方法进行讲解。即利用脚本动态创建游戏对象。 利用脚本动态创建游戏对象的方法又分为两种。

  • 使用 CreatePrimitive 方法创建 Unity 3D 系统自带的基本游戏对象。
  • 使用 Instantiate 实例化方法将预制体实例化为对象。

案例设计

本案例通过 C# 脚本在 Unity 3D 中创建一个 Cube 模型和一个 Sphere 模型,通过屏幕左上角按钮进行创建操作。

案例实施

  1. 使用 CreatePrimitive 方法创建 Unity 3D 系统自带的基本游戏对象,创建空白脚本写入代码。

 
  1. using UnityEngine;
  2. using System.Collections;
  3. public class CreatePrimitive:MonoBehaviour{
  4. void OnGUI(){
  5. if(GUILayout.Button("CreateCube", GUILayout.Height(50))){
  6. GameObject m_cube=GameObject.CreatePrimitive(PrimitiveType.Cube);
  7. m_cube.AddComponent<Rigidbody>();
  8. m_cube.GetComponent<Renderer>().material.color=Color.blue;
  9. m_cube.transform.position=new Vector3(0, 10, 0);
  10. }
  11. if(GUILayout.Button("CreateSphere", GUILayout.Height(50))){
  12. GameObject m_cube=GameObject.CreatePrimitive(PrimitiveType.Sphere);
  13. m_cube.AddComponent<Rigidbody>();
  14. m_cube.GetComponent<Renderer>().material.color=Color.red;
  15. m_cube.transform.position=new Vector3(0, 10, 0);
  16. }
  17. }
  18. }

在上述 OnGUI() 函数中,单击 CreateCube 按钮或 CreateSphere 按钮后,将分别调用 CreatePrimitive 方法,从而创建 Cube 和 Sphere 游戏对象,并且为这两个游戏对象添加刚体、颜色以及位置属性。

  1. 使用 Instantiate 实例化方法将预制体实例化为对象。

当制作好了游戏组件(场景中的任意一个 GameObject)后,将它制作成一个组件模板,用于批量的套用工作,例如场景中“重复”的东西—— “敌人”、“士兵”、“子弹”……称为预制体。

默认生成的预制体其实就像克隆体,其内储存着一个游戏对象,包括游戏对象的所有组件及其下的所有子游戏对象。

  • 创建预制体

首先在场景中创建一个立方体,然后在 Project 面板中单击 Create 菜单的下拉三角,在出现的对话框中选择 Prefab 创建一个预制体,并为其命名为 MyCube 。

然后在 Hierarchy 视图中将立方体拖曳至 Project 视图中的 MyCube,完成预制体的制作并与立方体 Cube 关联。在 Hierarchy 视图中与预制体关联的游戏对象为蓝色。

  • 预制体实例化

将预制体复制一份到场景里的过程叫实例化,在 Project 面板中将预制体拖曳到 Inspector 面板中可以实例化一个对象。

预制体的实例化不是普通的复制,预制体实例化后产生的新游戏对象依然保持着与预制体的关联,也就是预制体进行添加组件、修改组件属性等改变,预制体实例化产生的游戏对象也会发生相应的改变。

 
  1. if(Input.GetButtonDown("Fire1")){
  2. Instantiate(newObject, transform.position, transform.rotation);
  3. }

上述代码中调用 Instantiate 方法实例化游戏对象与调用 CreatePrimitive 方法创建游戏对象的最终结果是完全一样的,实例化游戏对象会将对象的脚本及所有继承关系实例化到游戏场景中。

Instantiate 实例化方法比创建物体的 CreatePrimitive 方法执行效率要高很多。

在开发过程中通常会使用 Instantiate 方法实例化对象,Instantiate 方法调用时一般与预制体 Prefab 结合使用。

Unity 3D 实践案例二

在脚本编写中我们会经常用到移动、旋转、缩放功能,可以使用transform.Translate()transform.Rotate() 和 transform.localScale 来实现这一效果。

本案例通过创建一个旋转立方体的实践操作,熟悉并掌握脚本编译中移动、旋转、缩放的函数编写以及与OnGUI 函数交互功能的实现。

案例设计

本案例计划通过 C# 脚本在 Unity 内创建一个简单的 Cube 模型,采用 OnGUI 函数写 3 个交互按钮,实现与 Cube 模型进行移动、旋转、缩放的交互功能。

案例实施

  1. 为游戏项目里的游戏场景添加两个游戏对象:CubeDirectional

Cube是脚本具体操作的游戏对象。

Directional是产生场景照明效果的游戏对象。

创建一个 Plane 位于 Cube 下方。调整游戏场景中 3 个游戏对象的位置,使得 Game 视图达到最佳的效果。

  1. 在 Project 视图里,新建一个 C# 脚本,命名为 MyScript,打开此脚本并添加下面的代码:

 
  1. using UnityEngine;
  2. using System.Collections;
  3. public class MyScript:MonoBehaviour
  4. {
  5. //声明4个变量
  6. public GameObject myCube;
  7. public int transSpeed=100;
  8. public float rotaSpeed=10.5f;
  9. public float scale=3;
  10. void OnGUI(){
  11. if(GUILayout.Button("移动立方体")){
  12. myCube.transform.Translate(Vector3.forward*transSpeed*Time.deltaTime, Space.World);
  13. }
  14. if(GUILayout.Button("旋转立方体")){
  15. myCube.transform.Rotate(Vector3.up*rotaSpeed, Space.World);
  16. }
  17. if(GUILayout.Button("缩放立方体")){
  18. myCube.transform.localScale=new Vector3(scale, scale, scale);
  19. }
  20. }
  21. }

该脚本声明了 4 个变量,并都使用 public进行修饰,所以它们都可以作为属性出现在组件下。

OnGUI() 函数用于在界面中显示按钮,玩家可以通过单击按钮实现与立方体的交互功能。

  1. 将脚本 MyScript 赋予 Main Camera

  1. 运行游戏,在 Game 视图的左上角会出现 3 个按钮:“移动立方体”、“旋转立方体”和“缩放立方体”。单击按钮,即可完成对立方体对象的指定操作。

Unity 3D 实践案例三

Unity 3D 中提供了第一人称和第三人称虚拟漫游组件,虚拟漫游可以提升游戏玩家对游戏产生的沉浸感。

本案例通过脚本实现第一人称虚拟漫游功能的实践操作,熟悉并掌握编写 Unity 3D 脚本实现游戏功能的方法。

案例设计

本案例采用 C# 脚本开发第一人称虚拟漫游功能。

构建简单的 3D 场景,使用 WSAD键即可在场景内自由行走,还可以通过鼠标实现观察者视角的旋转功能。

案例实施

  1. 执行 GameObject → 3D Object → Plane 命令创建一个平面。

  1. 执行 GameObject → Create Empty 命令创建空物体,并将标签设为 Player

一个标签是用来索引一个或一组游戏对象的词。标签是为了编程的目的而对游戏对象的标注,游戏开发人员可以使用标签来书写脚本代码,通过搜索找到包含想要的标签的对象。

添加标签方法很简单,选中 Inspector 面板右上方的 Tag,点击 Add Tag 将在检视面板打开标签管理器,然后在里面输入 Player

然后再次选择空物体,在 Tag 的下拉列表中找到 Player 标签,完成添加标签。

  1. 为主角添加角色控制器组件。执行 Component → Physics → Character Controller 命令,如下图所示。

角色控制器主要用于第三人称或第一人称游戏主角控制,并不使用刚体物理效果,具体参数如下表所示。

参数描述功能
Slope Limit坡度限制限制碰撞器只能爬小于等于该值的斜坡
Step Offset台阶高度角色可以迈上的最高台阶高度
Skin Width皮肤厚度皮肤厚度决定了两个碰撞器可以互相渗透的深度
Min Move Distance最小移动距离如果角色移动的距离小于该值,角色就不会移动
Center中心该值决定胶囊碰撞器在世界空间中的位置
Radius半径胶囊碰撞器的横截面半径
Height高度胶囊碰撞器的高度

  1. 添加 Rigidbody 组件,取消选中 Use Gravity 复选框,选中 Is Kinematic 复选框使其不受物理影响,而是受脚本控制。

  1. 调整 Character Controller 的位置和大小,使其置于平面之上。

  1. 创建 C# 脚本,将其命名为 Player

  1. 输入如下代码:

 
  1. using UnityEngine;
  2. using System.Collections;
  3. public class Player:MonoBehaviour{
  4. public Transform m_transform;
  5. //角色控制器组件
  6. CharacterController m_ch;
  7. //角色移动速度
  8. float m_movSpeed=3.0f;
  9. //重力
  10. float m_gravity=2.0f;
  11. void Start(){
  12. m_transform=this.transform;
  13. //获取角色控制器组件
  14. m_ch=this.GetComponent<CharacterController>();
  15. }
  16. void Update(){
  17. Control();
  18. }
  19. void Control(){
  20. //定义3个值控制移动
  21. float xm=0, ym=0, zm=0;
  22. //重力运动
  23. ym-=m_gravity*Time.deltaTime;
  24. //前后左右移动
  25. if(Input.GetKey(KeyCode.W)){
  26. zm+=m_movSpeed*Time.deltaTime;
  27. }else if(Input.GetKey(KeyCode.S)){
  28. zm-=m_movSpeed*Time.deltaTime;
  29. }
  30. if(Input.GetKey(KeyCode.A)){
  31. xm-=m_movSpeed*Time.deltaTime;
  32. }else if(Input.GetKey(KeyCode.D)){
  33. xm+=m_movSpeed*Time.deltaTime;
  34. }
  35. //使用角色控制器提供的Move函数进行移动
  36. m_ch.Move(m_transform.TransformDirection(new Vector3(xm, ym, zm)));
  37. }
  38. }

上述代码主要是控制角色前后左右移动。

在 Start 函数中,首先获取 CharacterController 组件,然后在 Control 函数中通过键盘操作获得 X 和 Y 方向上的移动距离,最后使用 CharacterController 组件提供的 Move 函数移动角色。

使用 CharacterController 提供的功能进行移动时,会自动计算移动体与场景之间的碰撞。

  1. 在 Hierarchy 视图中选中 Player 游戏对象,在其 Inspector 属性面板中选择 Component→Script,选择 Player 脚本将其链接到 Player 游戏对象上。

  1. 此时运行测试,按 W、S、A、D 键可以控制主角前后左右移动,但是在 Game 视图中却观察不到主角在场景中移动的效果,这是因为摄像机还没有与主角的游戏对象关联起来,需要添加摄像机代码。打开 "Player.cs",添加如下代码:

 
  1. //摄像机Transform
  2. Transform m_camTransform;
  3. //摄像机旋转角度
  4. Vector3 m_camRot;
  5. //摄像机高度
  6. float m_camHeight=1.4f;
  7. //修改Start函数, 初始化摄像机的位置和旋转角度
  8. void Start(){
  9. m_transform=this.transform;
  10. //获取角色控制器组件
  11. m_ch=this.GetComponent<CharacterController>();
  12. //获取摄像机
  13. m_camTransform=Camera.main.transform;
  14. Vector3 pos=m_transform.position;
  15. pos.y+=m_camHeight;
  16. m_camTransform.position=pos;
  17. //设置摄像机的旋转方向与主角一致
  18. m_camTransform.rotation=m_transform.rotation;
  19. m_camRot=m_camTransform.eulerAngles;
  20. //锁定鼠标
  21. Screen.lockCursor=true;
  22. }
  23. void Update(){
  24. Control();
  25. }
  26. void Control(){
  27. //获取鼠标移动距离
  28. float rh=Input.GetAxis("Mouse X");
  29. float rv=Input.GetAxis("Mouse Y");
  30. //旋转摄像机
  31. m_camRot.x-=rv;
  32. m_camRot.y+=rh;
  33. m_camTransform.eulerAngles=m_camRot;
  34. //使角色的面向方向与摄像机一致
  35. Vector3 camrot=m_camTransform.eulerAngles;
  36. camrot.x=0;camrot.z=0;
  37. m_transform.eulerAngles=camrot;
  38. //操作角色移动代码
  39. //使摄像机位置与角色一致
  40. Vector3 pos=m_transform.position;
  41. pos.y+=m_camHeight;
  42. m_camTransform.position=pos;
  43. }

上述代码通过控制鼠标旋转摄像机方向,使角色跟随摄像机的 Y 轴旋转方向,在移动角色时,使摄像机跟随角色运动。

  1. 单击 Play 按钮进行测试,效果如下图所示,通过鼠标操作可以在场景中旋转视角,通过 W、S、A、D 键可以在场景中向前、向后、向左、向右移动。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值