2.4 创建主角
这个游戏的主角是一艘太空飞船,我们将使用一个飞船模型作为主角的游戏体,并赋予它一个脚本,控制它的运动。脚本是实现游戏逻辑的核心,它本身并不能独立运行,必须作为某个游戏体的组件才能运行。
2.4.1 创建脚本
步骤 01 在Project窗口中找到Player.fbx,将其拖动到Hierarchy窗口中创建飞船模型的游戏体,然后在Inspector窗口中将其旋转-180°,如图2-16所示。
图2-16 创建飞船游戏体
步骤 02 在Project窗口中选择Assets并单击鼠标右键,选择【Create】→【Folder】创建一个文件夹,将其命名为Scripts,我们可以将所有创建的脚本都放入到这个文件夹中。
步骤 03 选择Scripts文件夹并单击鼠标右键,选择【Create】→【C# Script】创建一个C#脚本,将其命名为Player。
步骤 04 双击Player.cs脚本将其打开,会发现Unity已经自动创建了一些基本代码:
using UnityEngine; using System.Collections; public class Player : MonoBehaviour { // Use this for initialization void Start() { } // Update is called once per frame void Update() { } }
Player是类的名字,这个名字必须与脚本的文件名一致,它默认继承自MonoBehaviour,只有继承自MonoBehaviour的类才能作为Unity脚本组件使用。
步骤 05 选择主角的飞船模型游戏体,然后在菜单栏中选择【Component】→【Scripts】→【Player】,将脚本指定给主角游戏体作为组件。
步骤 06 默认新创建的脚本都会出现在菜单栏【Component】→【Scripts】下面,为了便于管理脚本,也可以自定义脚本在菜单栏中的位置。在Player类前面添加AddComponentMenu属性:
[AddComponentMenu("MyGame/Player")] public class Player : MonoBehaviour
步骤 07 在菜单栏中选择【Component】,会发现Player脚本出现在自定义的MyGame子菜单中,如图2-17所示。
图2-17 自定义脚本在菜单中的位置
2.4.2 控制飞船移动
我们将使用键盘上的上、下、左、右键来控制飞船的行动。
步骤 01 在Player类中添加一个控制飞行速度的属性:
public float m_speed = 1;
步骤 02 在Player类中,默认有一个Update函数,这个函数在程序运行时,每帧都会被调用,我们将在这个函数中添加键盘操作的代码:
void Update () { // 纵向移动距离 float movev=0; // 水平移动距离 float moveh=0; // 按上键 if ( Input.GetKey( KeyCode.UpArrow ) ) { movev -= m_speed * Time.deltaTime; } // 按下键 if ( Input.GetKey( KeyCode.DownArrow ) ) { movev += m_speed * Time.deltaTime; } // 按左键 if ( Input.GetKey( KeyCode.LeftArrow ) ) { moveh += m_speed * Time.deltaTime; } // 按右键 if ( Input.GetKey( KeyCode.RightArrow ) ) { moveh -= m_speed * Time.deltaTime; } // 移动 this.transform.Translate( new Vector3( moveh, 0, movev ) ); }
Input是一个包装了输入功能的类,它包括几乎所有的键盘、鼠标或触控操作函数。
Time.deltaTime表示每帧的经过时间,那些需要每帧做增减变动的数值都建议乘上Time.deltaTime。在本示例中,速度与Time.deltaTime相乘,表示每帧移动N米距离。
this.transform调用的是游戏体的Transform组件,Transform组件提供的主要功能都是和移动、旋转、缩放游戏体有关的。我们调用了Translate函数移动游戏体,其中有一个Vector3类型的参数,用来表示x、y、z三个方向上的移动距离。
步骤 03 在Update函数中如果每帧都去调用this.transform组件,就会造成一定的效率问题,我们可以在对象初始化时只调用一次并将其保存起来。Start函数会在对象被实例化时自动调用一次,类似构造函数,可以在这里进行一些初始化的工作,如下所示:
m_transform = this.transform; void Start () { m_transform = this.transform; }
提示
MonoBehaviour的派生类不能使用构造函数初始化。
步骤 04 在Update函数中修改控制移动的代码,将this.transform替换为this.m_transform,这样程序就不用每帧都去查找Transform组件,提高了程序的运行效率,如下所示:
this.m_transform.Translate( new Vector3( moveh, 0, movev ) );
步骤 05 运行游戏,应当可以控制飞船移动了,但我们可能会觉得飞船的移动速度不够快。退出游戏运行模式,选择主角飞船游戏体,在Inspector窗口中找到Player脚本组件,将Speed的值加大至6,如图2-18所示。
图2-18 脚本组件
Speed的值与Player脚本内的m_speed的值是对应的,这样只需要在场景中修改Speed的值就可以改变飞船的移动速度,而不用每次都去修改原始的代码。
提示
只有public类型的属性才能在编辑器窗口实例化。
再次运行游戏,飞船的移动速度明显提高了。
2.4.3 创建子弹
这是一个射击游戏,飞船还需要发射子弹,我们先创建它的游戏体和脚本。
步骤 01 在Project窗口中找到rocket.fbx模型文件,将其拖动到Hierarchy窗口中创建子弹的游戏体,如图2-19所示。
图2-19 创建子弹游戏体
步骤 02 创建Rocket.cs脚本,将其指定给子弹游戏体。
步骤 03 在Rocket类中添加三个属性,分别控制子弹的飞行速度、生存时间和威力,如下所示:
[AddComponentMenu("MyGame/Rocket")] public class Rocket : MonoBehaviour { public float m_speed=10; // 子弹飞行速度 public float m_liveTime=1; // 生存时间 public float m_power=1.0f; // 威力 protected Transform m_trasform; // Use this for initialization void Start () { m_trasform = this.transform; Destroy(this.gameObject, m_liveTime); // 一定时间后自我销毁 }
在Start函数中,我们调用了Destroy函数,在一定时间后销毁自身。
步骤 04 在Rocket的Update函数中添加如下代码:
void Update () { // 向前移动 m_trasform.Translate( new Vector3( 0, 0, -m_speed * Time.deltaTime ) ); } }
运行游戏,子弹将飞速地前进,一定时间后会自动消失。
2.4.4 创建子弹Prefab
前面我们已经创建了子弹的游戏体,游戏中的子弹是玩家操作发射的,并可以发射很多子弹。对于需要重复使用的游戏体,需要将其制作成Prefab。
在本书第1章介绍过,Prefab在Unity中可以理解为可重用的游戏体。当我们创建了一个游戏体,为其设置了脚本、参数等,可以将其保存为Prefab,然后在任何时间、场景,甚至在其他Unity游戏中重复使用。下面我们把子弹游戏体制作成一个Prefab。
步骤 01 在Project窗口中的Assets目录内创建一个名为prefabs的文件夹用于保存prefab,然后单击鼠标右键,选择【Create】→【Prefabs】创建一个空的Prefab,将其命名为Rocket,如图2-20所示。
图2-20 创建空的Prefab
步骤 02 在Hierarchy窗口中选择前面创建的子弹游戏体,将其拖动到刚刚创建的prefab上面,Prefab的制作就完成了,如图2-21所示。
图2-21 制作子弹的Prefab
步骤 03 场景中的原始子弹游戏体已经不再需要了,可以按Delete键将其删除。
现在,我们已经完成了Prefab的创建。注意,步骤1并不是必须的,如果没有提前创建一个空的Prefab,步骤(2)直接拖动到Project窗口空白处,同样可以创建Prefab。
2.4.5 发射子弹
接下来,我们将使用Instantiate函数动态地创建子弹游戏体。
步骤 01 首先,我们要将飞船和子弹的Prefab关联起来。打开Player.cs脚本,添加一个Transform属性,它将指向子弹的Prefab,如下所示:
public Transform m_rocket;
步骤 02 回到Unity编辑器,选择Player游戏体,在Inspector窗口中找到Player脚本组件,会发现新增加了一个Rocket选项,选择子弹的Prefab,将其拖动到Rocket选项上,如图2-22所示。
图2-22 关联Prefab
步骤 03 打开Player.cs脚本,在Update函数的最下面添加如下代码发射子弹:
void Update () { // ... // 按空格键或鼠标左键发射子弹 if ( Input.GetKey( KeyCode.Space ) || Input.GetMouseButton(0) ) { Instantiate( m_rocket, m_transform.position, m_transform.rotation ); } }
提示
Unity的游戏体只能使用Instantiate函数实例化,不能使用new。
步骤 04 运行游戏,按空格键或鼠标左键可以发射子弹,但现在的发射速度太快,我们再略修改一下代码,先添加一个控制发射频率的属性:
float m_rocketTimer = 0;
步骤 05 在Update函数中修改代码,每隔0.1秒发射一次子弹,如下所示:
void Update () { // ... m_rocketTimer -= Time.deltaTime; if (m_rocketTimer <= 0 ) { m_rocketTimer = 0.1f; // 按空格键或鼠标左键发射子弹 if ( Input.GetKey( KeyCode.Space ) || Input.GetMouseButton(0) ) { Instantiate( m_rocket, m_transform.position, m_transform.rotation ); } }