游戏环境已经基本搭建完成。
接下来需要用脚本实现飞船的移动、射击、爆炸等效果。
脚本语言采用C#
并在Project视图中创建一个专门存放脚本的目录。
选择Assets。Create->Folder创建一个新文件夹,更名为Scripts。
3.1 给飞船添加移动功能
点击Player,Add Component->New Script。更名为Player Controller。
并将它放在Scripts目录中。
通过物理的方式移动飞船,需要用到FixedUpdate()函数。
此函数会在每个固定的物理步骤前自动调用。
Input.GetAxis----->return the value of the virture axis identified by axisname.
可通过键盘或是操纵杆去实现输入。它返回的值在-1与1之间。
如果通过鼠标控制,则返回的值不在-1与1之间,而是根据鼠标的灵敏度而定。
首先定义水平和垂直方向的移动参数:
1 float moveHorizontal = Input.GetAxis("Horizontal");
2 float moveVertical = Input.GetAxis("Vertical");
Vector3: 表示3D的点和矢量。Vector3(x,y,z)类似于空间坐标系。
由于飞船在Y轴上移动无任何改变,可设置y值为0.0f。
x可表示水平移动,赋值为moveHorizontal。
z代表垂直移动,赋值为moveVertical。
rigidbody.velocity: 刚体的速度向量。
定义一个全局变量speed,使其与Vector3对象相乘,而此值可赋值给velocity用来调节飞船移动速度。
1 Vector3 movement = new Vector3(moveHorizontal, 0.0f, moveVertical);
2 GetComponent<Rigidbody>().velocity = movement * speed;
3.2 限制飞船移动范围
当飞船移动时,会出现一个问题。
当我们无限左移(或是其他方向移动)飞船时,由于游戏界面是有限的,
我们可能会将飞船移至视野之外。因此,控制飞船的移动范围是十分必要的。
我们通过rigidbody.velocity移动飞船,也可以通过限制rigidbody的位置来限制飞船。
在移动飞船之前,需要先限制飞船的移动范围。
当我们要把飞船移动到范围之外时,代码会限制它停在边缘。
利用rigidbody.position去限制飞船位置。
rigidbody.position=new Vector3(x,y,z);
飞船的上下位置不需要设置(y值的大小对飞船在Game View中的位置显示没有影响)
因此设置其值为0.0f(float类型)。
飞船四个方向的位置移动显示只受x和z的影响。
在此之前,了解一下Mathf:A collection of common math function.
我们要用的函数是“Clamp”,它包含两个值,最大和最小。
Clamp:Clamps a value between a minimum float and maximum float value.
public static int Clamp(int value,int min ,int max);
通过测试飞船位置,可得到飞船行驶的位置范围,并将其赋值给相应的变量。
1 GetComponent<Rigidbody>().position = new Vector3
2 (
3 Mathf.Clamp(GetComponent<Rigidbody>().position.x, boudary.xMin, boudary.xMax),
4 0.0f,
5 Mathf.Clamp(GetComponent<Rigidbody>().position.z, boudary.zMin, boudary.zMax)
6 );
其中xMin等变量设置为全局变量,这样可在Inspector视图对应的Script中调节变量值。
但这四个变量占用了不少的空间,而且只能被此脚本使用。
那么,能不能想办法整理这个空间,使它能重复调用。
重建一个class命名为Boundary
1 [System.Serializable]
2 public class Boundary
3 {
4 public float xMin, xMax, zMin, zMax;
5 }
然后,创建一个Boundary对象boundary。利用该对象调用xMin等值。
注意[System.Serializable]不能少。它能够序列化我们的class,只有完成了序列化,
才能显示在Inspector中。
如下图:
3.3 使飞船在运动的时候倾斜
利用rigidbody.rotation。
rigidbody.rotation = Quaternion.Euler(x,y,z);
我们只需让飞船沿z轴旋转,因此可设置x,y值为0.0f。
代码如下:
GetComponent<Rigidbody>().rotation = Quaternion.Euler(0.0f,0.0f,GetComponent<Rigidbody>().velocity.x * -tilt);
其中tilt为全局变量,可通过它调节飞船的旋转幅度。
3.4 创建子弹
注意一点,当创建完子弹以后如何去掉黑边、如何令子弹高亮。
即,将子弹的Shader设置为Additive(Mobile->Particles->Additive)
之后,需要给子弹添加rigidbody(去掉Use Gravity)
为了更准确的完成碰撞效果,添加Capsule Collider,根据子弹形状大小去调整Collider。
3.4.1 子弹运动脚本
Start()函数,会在对象实例化的最前帧执行。
我们需要子弹沿z轴向上移动,因为障碍物会从反方向飞过来。
z轴是子弹运动的方向。同时,还需控制子弹运动的速度。
1 public float speed;
2 void Start()
3 {
4 GetComponent<Rigidbody>().velocity = transform.forward * speed;
5 }
Transform.forward:The blue axis of the transform in world space.
其中blue axis应该表示z axis。
3.4.2 给飞船添加发射子弹功能
之前的子弹已经保存至Prefabs文件夹中作为资源使用。
当玩家点击屏幕时,会实例化一个子弹的克隆体。
检测按钮输入事件与物理无关,因此不需要在FixedUpdate()函数中发射子弹。
故,可将此代码写入Update()中。
Unity会在每一帧刷新前执行Update()中的代码。
实例化子弹:
Object.Instantiate(Object original,Vector3 position,Quaternion rotation)
Clones the object original and returns the clone
实例化需要三个值,分别为:目标物体、位置、方向。
点击屏幕,发射子弹。
Input.GetButton:
Returns true while the virtual button identified by buttonName is held down。
1 public GameObject shot; //子弹
2 public Transform shotSpawn; //依附于飞船的子弹容器
3 public float fireRate; //子弹发射的速率
4 private float nextFire;
5 void Update()
6 {
7 if (Input.GetButton("Fire1") && Time.time > nextFire)
8 {
9 nextFire = Time.time + fireRate;
10 Instantiate(shot, shotSpawn.position, shotSpawn.rotation);
11 GetComponent<AudioSource>().Play();
12 }
13 }
3.4.3 子弹发射边界
在子弹连续不断的发射过程中,就会产生出无限的子弹克隆体。
这样会很影响电脑的运行效果。
因此,需要给子弹设置一个边界,当它超出这个边界之后就会销毁。
Create->Cube
由于边界与子弹之间需要用到触发器,故勾选Is Trigger。
调节Cube(边界),使它铺满整个Game View。
禁用Mesh Renderer,在Game View中便看不到Cube了。
当子弹离开Cube之后,销毁它。
Collider.OnTriggerExit(Collider):
OnTriggerExit is called when the Collider other has stopped touching the trigger.
1 void OnTriggerExit(Collider other)
2 {
3 Destroy(other.gameObject);
4 }
3.5 添加障碍物
障碍物添加方法与之前的飞船、子弹相似,不再阐述。(Rigidbody、Capsule Collider)
给障碍物添加旋转效果:
rigidbody.angularVelocity:物体旋转的速率。
给它设置一个随机数,这样看起来更真实。
Random.insideUnitSphere:Vector3类型方法,它会产生x,y,z随机数。
1 public float tumble;
2 void Start()
3 {
4 GetComponent<Rigidbody>().angularVelocity = Random.insideUnitSphere * tumble;
5 }
当障碍物在旋转的过程中,由于受到阻力的影响,最终会停下。
因此,需要将Rigidbody下的drag和angular drag设置为0。
3.5.1 障碍物与子弹碰撞
Collider.OnTriggerEnter:
OnTriggerEnter is called when the Collider other enters the trigger。
此时要做一个判断,因为障碍物是处于Cube中的,如果单纯的利用该方法,
那么只要运行,障碍物就会消失。
void OnTriggerEnter(Collider other)
{
if(other.tag == "Boundary") //判断是否为Cube,将Boundary的Tag设置为"Boundary"
{
return;
} Destroy(other.gameObject); //销毁障碍物
Destroy(gameObject); //销毁子弹
}
3.5.2 爆炸
当子弹或是飞船和障碍物碰撞时,实例化对应的爆炸效果。
子弹:
Instantiate(explosion, transform.position, transform.rotation);
飞船:
1 if (other.tag == "Player") //将Player的Tag设置为"Player"
2 {
3 Instantiate(playerExplosion, other.transform.position, other.transform.rotation);
4 gameController.GameOver();
5 }
令障碍物向飞船方向移动。
将之前写好的子弹的Mover脚本拖拽至障碍物中去,改变它的速度为负值。