当前位置:   article > 正文

Unity 3D游戏开发+脚本编程完整指南:Unity脚本概览-控制物体的运动

Unity 3D游戏开发+脚本编程完整指南:Unity脚本概览-控制物体的运动
Unity 脚本概览
脚本编写并不困难,但是如果直接从细节开始讲起,会让
读者难以看到脚本编程的全貌。因此本章不急于阐述脚本编写
的细节,只介绍简单的修改物体位置、处理用户输入和检测碰
撞的方法,让读者用最简单的方式做出第一个 3D 滚球跑酷游
戏,体会脚本编程的思路和整体方法。

1.1 控制物体的运动

仅通过控制物体的位置,就能做出好玩的小游戏。本节将
详细讲解创建脚本、改变物体位置和处理用户输入等基本操
作,并对容易产生误解的地方做出提示。

1.1.1 新建脚本

首先在场景中新建一个球体,接着新建脚本并挂载到该球
体上。
新建脚本有两种方法。第一种方法是在 Project (工程)窗
口的某个文件夹中,如在 Assets (资源根目录)中单击鼠标右
键,选择 Create→C# Script 选项,如图 1-1 所示。这时 Unity 会在
该文件夹下建立一个扩展名为 .cs 的脚本文件(扩展名在 Unity
辑器中是隐藏的),并立即让用户为其指定一个名称,默认为
NewScript
图1-1 创建C#脚本
图1-1 创建C#脚本
第二种方法是选中球体,在 Inspector (检视)窗口中单击
Add Component (添加组件)按钮,在菜单中选择 New script
(新建脚本)选项,再输入文件名,最后单击 Create and Add
(创建并添加)按钮,则会在 Assets 下新建指定名称的脚本文
件,如图 1-2 所示。
图1-2 直接创建脚本Ball的方法
图1-2 直接创建脚本Ball的方法
如果采用第一种方法,那么创建的脚本文件就还未挂载到
球体上。如果采用第二种方法,则在创建完成的同时脚本文件
也挂载完成了。
将脚本文件挂载到球体上也有两种方法:一是拖曳 Project
窗口中的脚本文件到场景中的球体上,二是拖曳 Project 窗口中 的脚本文件到 Inspector 窗口里所有项目的后面。推荐使用第二
种方法,因为它更为直观。
将脚本文件拖曳到 Inspector 窗口时会出现一条蓝色标记
线,代表插入的位置,如图 1-3 所示。一般来说脚本组件的顺序
不是很重要,可以插入到任意两个组件之间或者最后。
图1-3 插入脚本组件的操作
图1-3 插入脚本组件的操作
当不小心挂载了多个同样的脚本组件时,在组件名称上单
击鼠标右键,并选择 Remove Component (删除组件)选项即可
删除多余的组件,如图 1-4 所示
图1-4 删除组件的操作
图1-4 删除组件的操作
小提示
创建脚本组件的注意事项
Unity 规定,能够挂载到物体上的脚本文件必须是 脚本
组件 (另有一种不是组件的脚本文件),脚本组件要继承
MonoBehaviour ,且脚本代码中的 class 名称必须与文件名
一致。一般脚本创建时会自动生成这部分内容,但是如果修
改了脚本文件名,那么在挂载时就会报错。这时就必须修改
文件名或 class 名称,让它们一致,这样才能正确挂载。 Unity 支持一个物体挂载多个同样的脚本组件,但一般来
说只需要一个。如果由于操作失误挂载了多个脚本组件,就
要删除多余的,这也是建议把脚本文件拖曳到 Inspector 窗口
内的原因,这样易于确认是否挂载了多个脚本组件。

1.1.2 StartUpdate事件

双击脚本文件,或者在脚本组件上单击鼠标右键,选择
Edit Script (编辑脚本),就可以打开脚本文件进行编辑。本书
建议使用 Visual Studio 2017 Visual Studio 2019 作为代码编辑
器。在初次安装 Unity 时会默认安装 Visual Studio 社区版,该版
本可免费使用。
打开脚本文件,可以看到如下内容。
  1. using UnityEngine;
  2. public class Ball : MonoBehaviour
  3. {
  4. // Use this for initialization
  5. void Start()
  6. {
  7. }
  8. // Update is called once per frame
  9. void Update()
  10. {
  11. }
  12. }
这个脚本文件名为 Ball ,引用了 UnityEngine 这个命名空
间,组件类型为 Ball ,继承了 MonoBehaviour ,这些都是自动生
成的部分。这里需要关注的是其中的 Start() 函数和 Update()
数。
Start() 函数在游戏开始运行的时候执行一次,特别适合进行
组件初始化。而 Update() 函数每帧都会执行,在不同设备上更新
的频率有所区别,特别是当系统硬件资源不足时,帧率就会降
低,因此 Update() 函数实际执行的频率是变化的。
Start Update 又被称为 事件 ,因为它们分别是在 该组件
开始运行 更新该组件 这两个事件发生时被调用的。第 2
会详细讲解脚本组件的各种事件函数。
这里介绍一个非常常用的方法: Debug.Log() 用于向 Console
(控制台)窗口输出一串信息。下面改动脚本,加上两句输出 信息的代码。
  1. using UnityEngine;
  2. public class Ball : MonoBehaviour
  3. {
  4. // Use this for initialization
  5. void Start()
  6. {
  7. Debug.Log("组件执行开始函数!");
  8. }
  9. // Update is called once per frame
  10. void Update()
  11. {
  12. Debug.Log("当前游戏进行时间:" + Time.time);
  13. }
  14. }
运行游戏,找到 Console 窗口,如果没有打开,则选择主菜
单中的 Window→General→Console 选项打开,如图 1-5 所示。
图1-5 让Console窗口显示出来
图1-5 让Console窗口显示出来
执行游戏, Console 窗口内会产生大量信息,如图 1-6 所示。
图1-6 Console窗口输出的信息
图1-6 Console窗口输出的信息
暂停游戏,查看 Console 窗口中的信息,发现开始函数只执
行了一次,后面所有的 当前游戏进行时间:秒数 都是 Update()
函数被调用时输出的。如果仔细观察,可以发现时间的流逝不
太稳定,特别是在游戏刚启动的时候偏差较大。
Debug.Log() 函数是写代码、查 bug 的好帮手,越是编程高
手,越是使用得多。 Debug.Log() 函数也是最简单、最可靠的一
种调试手段,未来还会反复使用这个方法,建议从现在开始就
多多应用它。

1.1.3 修改物体位置

Unity 里修改物体位置,实际上就是修改 Transform (变
换)组件的数据。在 Inspector 窗口里可以方便地查看和修改
Transform 组件的位置( Position )、旋转( Rotation )和缩放
Scale )参数,如图 1-7 所示。
图1-7 查看和修改物体位置
图1-7 查看和修改物体位置

在界面中可以修改的参数在脚本中也能修改,而在脚本中
可以修改的参数就不一定会在界面上出现了,因为脚本的功能
比界面上展示的功能要多得多。
修改 Transform 组件中的 Position 有两种常用方法。一种是使
Translate() 函数。
  1. // 物体将沿着自身的右侧方向(X轴正方向也称为向右)前进1.5个单位
  2. transform.Translate(1.5f, 0, 0);
另一种是直接指定新的位置。如果在游戏一开始就把物体
放在( 1, 2.5, 3 )的位置,则只需要修改 Start() 函数。
  1. void Start () {
  2. transform.position = new Vector3(1, 2.5f, 3);
  3. }
这里可能有两个让人觉得奇怪的地方: “2.5” 要写作 “2.5f”
设置位置时要用 new Vector3(x, y, z) 这种写法。 这些主要源于 C# 语法的规定。直接写 2.5 会被认为是一个
double 类型的数,而这里需要的是 float 类型的数,所以必须加上
f 后缀。而写 “new” 的原因是 Vector3 是一个值类型,而 position
一个属性,由于 C# 中引用和值的原理,因此不能使
“transform.position.y = 2.5f” 这种写法直接修改物体的位置。这
里的解释可能对初学者来说有点模糊,不过没有关系,可以先
记住并使用这个语法,随着 C# 编程学习的深入自然会明白。
如果要做一个连续的位移动画,只需要让物体每帧都移动
一段很小的距离就可以了。也就是说,可以把改变位置的代码
写在 Update() 函数里,但是每帧都要比前一帧多改变一些位置。
例如,以下两种方法都可以让物体沿 z 轴前进。
  1. void Update()
  2. {
  3. transform.Translate(0, 0, 0.1f);
  4. // 在这个例子中等价于:
  5. transform.position += new Vector3(0, 0, 0.1f);
  6. }
以上两句代码只选择写其中一句即可,另一句可以注释
掉。运行游戏,观察小球是否持续运动。
小提示
物体位置的两种写法有本质区别
如果深究,这里有两个要点:一是向量的使用和向量加
法,二是局部坐标系与世界坐标系之间的区别和联系。
Translate() 函数默认为局部坐标系,而修改 position 的方
法是世界坐标系。 3D 数学基础知识会在后续章节详细说明,
这里先把关注点放在位移逻辑上。
前面提到,由于系统繁忙时无法保证稳定的帧率,因此对
于上面的写法,如果帧率高,小球移动就快;帧率低,小球移
动就慢。我们需要在 每帧移动同样的距离 每秒移动同样的
距离 之间做出选择。
按游戏开发的常规方法,应当选择 每秒移动同样的距
。举个例子,如果帧率为 60 / 秒时物体每帧移动 0.01 米,那
么帧率只有 30 / 秒时就应该每帧移动 0.02 米,这样才能保证物 体移动 1 秒的距离都是 0.6 米。这里虽然原理复杂,但实际只需
要略作修改即可。
  1. transform.Translate(0, 0, 5 * Time.deltaTime);
  2. // 或
  3. transform.position += new Vector3(0, 0, 5 * Time.deltaTime);
  4. }
Time.deltaTime 表示两帧之间的间隔,如帧率为 60 / 秒时这
个值为 0.0167 秒,帧率只有 10 / 秒时这个值为 0.1 秒,用它乘以
移动速度就可以抵消帧率变化的影响。由于 Time.deltaTime 是一
个比较小的数,因此速度的数值应适当放大一些。

1.1.4 读取和处理输入

Unity 支持多种多样的输入设备,如键盘、鼠标、手柄、摇
杆、触摸屏等。很多输入设备有着类似的控制方式,如按键盘
上的 W 键或上箭头键,将手柄的左摇杆向前推,都代表
,用 Unity 的术语表述则是 沿纵轴( Vertical )向上 。以下
代码就可以获取用户当前的纵轴输入和横轴输入。
  1. void Update()
  2. {
  3. float v = Input.GetAxis("Vertical");
  4. float h = Input.GetAxis("Horizontal");
  5. Debug.Log("当前输入纵向:" + v + " " + "横向:" + h);
  6. }
Input.GetAxis() 函数的返回值是一个 float 类型的值,取值范
围为 -1~1 Unity 用这种方法将各种不同的输入方式统一在了一
起。
以上代码会输出 v h 的数值。运行游戏,然后按键盘上的
W A S D 键或方向键,查看 Console 窗口是否正确读取到了
输入值。
通过简单的乘法就可以将输入的幅度与物体运动的速度联
系起来。
  1. void Update()
  2. {
  3. float v = Input.GetAxis("Vertical");
  4. float h = Input.GetAxis("Horizontal");
  5. Debug.Log("当前输入纵向:" + v + " " + "横向:" + h);
  6. transform.Translate(h * 10 * Time.deltaTime, 0, v * 10 * Time.deltaTime);
  7. }
代码解释:先取得当前的纵向、横向输入(取值范围
-1~1 ),然后将输入值与速度(代码中的数字 10 )和
Time.deltaTime 相乘,以控制物体的移动。每帧最小移动距离为
0 ,在 60 帧时 x 轴的每帧最大移动幅度是 1*10*0.0167 ,即 0.167
米, z 轴同理。
运行游戏,按 W A S D 键或方向键,看看效果。

1.1.5 实例:实现一个移动的小球

下面利用上面所讲解的内容,实现一个移动小球的案例,
具体操作如下。
01 新建一个球体。
02 调整摄像机的朝向和位置,可以设置为俯视、平视或斜
上方视角。
03 为球体创建脚本 Ball 并挂载,完整代码如下。
  1. using UnityEngine;
  2. public class Ball : MonoBehaviour
  3. {
  4. public float speed = 10;
  5. void Start()
  6. {
  7. }
  8. void Update()
  9. {
  10. float v = Input.GetAxis("Vertical");
  11. float h = Input.GetAxis("Horizontal");
  12. transform.Translate(h * speed * Time.deltaTime, 0, v * speed * Time.deltaTime);
  13. }
  14. }
04 运行游戏。单击 Game (游戏)窗口,然后按 W A
S D 键或方向键就可以看到球体以一定的速度移动了。运行游
戏的效果如图 1-8 所示。
图1-8 控制小球移动
图1-8 控制小球移动

通过修改 Translate() 函数的参数,可以使球体沿不同方向移
动,如修改为以下代码就能实现沿 xy 平面移动,而不是沿 xz
面移动。
  1. transform.Translate(h*speed*Time.deltaTime,
  2. v*speed*Time.deltaTime, 0);
用这种写法修改速度非常方便,不需要反复修改代码。因
speed 是一个 public 字段( C# 中类的成员变量称为字段,两种
术语叫法,不影响理解), Unity 会为这种字段提供一个快捷的
修改方法。在场景中选中球体,在 Inspector 窗口中查看它的脚
本组件,如图 1-9 所示。
图1-9 脚本中的速度变量显示在面板中
图1-9 脚本中的速度变量显示在面板中

读者会发现这里出现了一个 Speed 变量,值为 10 ,这就是脚
本中的 public float speed = 10 所带来的效果。由于在真实的游戏
开发过程中有很多参数需要设计师反复细致调节,因此 Unity
认将 public 字段直接放在界面上,用户可以随时修改速度,并且
被修改的参数会在下一次 Update() 函数执行时立即生效,也就是
说在游戏运行时也可以随时修改参数并生效。
唯一需要注意的是,如果游戏运行前 Speed 的值设定为 20
运行时改为了 30 ,那么停止运行游戏后, Speed 的值会回到 20
运行时的改动都不会被自动保存,需要在调整时记下合适的数
字,停止运行后手动修改。将参数显示到编辑器中还有更多技
巧,将在第 2 章继续讨论。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/105947
推荐阅读
相关标签
  

闽ICP备14008679号