太空大战算是比较经典的游戏了。这两天在跟着视频自学了一下。能做到的效果就是飞机发出子弹打爆陨石,如果被陨石碰到就死掉。简单的赤果果。界面如下:
现在做个总结:(模型声音之类的是导入的资源包。)
很明显这个游戏是一个2D游戏,但是我们是在3D环境下做的,其实没太大影响。
我们让这个背景和飞机都在X-Z平面上,飞机在(0,0,0)的位置,背景在(0,-10,10)的位置,如图:
这样我们的飞机和陨石就相当于只在X-Z这个平面运动,三维就降到了二维。很明显,相机要在飞机的Y轴上方才能出现游戏的界面效果。
这样我们的大体位置就摆好了。下面写代码控制飞机的运动:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16void FixedUpdate(){ //水平移动 float h = Input.GetAxis ("Horizontal"); //垂直移动 float v = Input.GetAxis ("Vertical"); //移动方向 Vector3 move = new Vector3 (h, 0f, v); //移动速度 _rigi.velocity = speed * move; //限制飞机移动的范围,限制value的值在min和max之间, 如果value小于min,返回min。 如果value大于max,返回max,否则返回value _rigi.transform.position = new Vector3(Mathf.Clamp(_rigi.transform.position.x,bound.xMin,bound.xMax), 0, Mathf.Clamp(_rigi.transform.position.z,bound.zMin,bound.zMax) ); }
简单说一下FixedUpdate()和Update()的区别:
pdate()是每渲染新的一帧就会调用;FixedUpdate()是固定的时间间隔调用,时间可以通过edit->Project Settings->time进行设置
接触了一个新函数:Mathf.Clamp。Mathf是常用数学函数的集合。
1
2
3static function Clamp(value:float,min:float,max:float):float 限制value的值在min和max之间, 如果value小于min,返回min。 如果value大于max,返回max,否则返回value
控制飞机发出子弹:
1
2
3
4
5
6
7
8
9void Update(){ //如果敲击空格键,生成子弹 if(Input.GetKeyDown(KeyCode.Space)){ _audio.PlayOneShot (fire,0.7f); Instantiate(bolt,spawnPos.position,spawnPos.rotation); } }
bolt是一个预制体Prefab,spawnPos是一个空物体。Instantiate的意思就是在这个空物体的位置生成一个子弹。
怎么销毁子弹呢?这里用到了OnTriggerExit()
1
2
3
4
5void OnTriggerExit(Collider other){ //print (other.name); //销毁物体 Destroy (other.gameObject); }
也就是在飞机和子弹的平面上有一个box collider,当然这个collider是触发模式。判断当子弹离开这个collider的时候,就销毁子弹。如图:
好了飞机能够飞能够发射子弹了。那么我们生成陨石吧。
我们把本身已经导入的模型拉入到Hierarchy中(附在一个空物体上),然后做成Prefab。现在控制单个陨石的旋转和降落以及碰到子弹就爆炸。
首先旋转:
1
2
3
4
5
6
7
8
9
10
11
12
13
14public class RandowRotate : MonoBehaviour { //陨石旋转速度 public float Tumble = 5; private Rigidbody _rigi; void Start(){ _rigi = GetComponent<Rigidbody> (); //设置刚体的角速度???? _rigi.angularVelocity = Random.insideUnitSphere * Tumble; } void Update(){ } }
Random.insideUnitSphere的意思就是返回半径为1的球体内的一二随机点。
然后运动:
1
2
3
4
5
6
7
8public class Mover : MonoBehaviour { private Rigidbody _rigi; public float speed = 10; void Start(){ _rigi = GetComponent<Rigidbody> (); _rigi.velocity = speed * new Vector3 (0,0,1); } }
这个Mover.cs其实是控制飞机写的,但是照样能控制陨石,只需把Unity界面内的speed改成复数即可。
碰撞爆炸效果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16void OnCollisionEnter(Collision other){ //如果陨石碰到飞机,飞机爆炸,飞机销毁 if (other.gameObject.tag == "Player") { Instantiate (playerExplosion, other.gameObject.transform.position, other.gameObject.transform.rotation); Destroy (other.gameObject); _gc.GameOver (); } //陨石爆炸,陨石销毁 Instantiate (explosion,transform.position,transform.rotation); Destroy (this.gameObject); _gc.AddScore (score); }
playerExplosion和explosion都是公有变量,在Unity面板中拉入效果即可。如图:
如果要添加声音的话,也要在这些个预制体面板中添加AudioSource,这样每次生成爆炸效果的时候也会生成Audio。因为我发现调用audio 的时候只能在Update()这样的函数中调用,如果只是一个普通的函数,声音是不会响的。所以以陨石爆炸为例,简单记录一下声音的附载:
这就不用写代码控制。Unity已经帮你做好了。puls,通过上图我们可以看到一个cs文件,这个是根据时间销毁爆炸效果的(毕竟爆炸也是一个粒子系统),代码如下:
1
2
3
4
5
6
7public class DestroyByTime : MonoBehaviour { //过了一定时间就销毁 public float lifetime; void Start(){ Destroy (this.gameObject,lifetime); } }
好了现在陨石可以降落了,飞机可以打陨石了。怎么控制陨石有规律的一波一波的降落呢?我们在Hierarchy面板中新建一个空物体,更名为GameController,GameController一般都是必不可少的。它的面板如下:
首先来看我们的GameController.cs文件:
1
2
3
4
5
6
7
8
9
10
11
12public Vector3 spawnValues; //创建三个物体 public GameObject[] hazard; //一波10个小陨石 public int numPerWave = 10; //出现小陨石之前的等待时间 public float startWait = 2f; //小陨石间隔时间 public float spawnWait = 1f; //每波之间的间隔时间 public float waveWait = 4f;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36void Update(){ if (gameOver && Input.GetKeyDown (KeyCode.R)) { //Application.LoadLevel (Application.loadedLevel); SceneManager.LoadScene("spceshooter"); } } //有规律的随机产生小陨石 IEnumerator SpawnWaves(){ yield return new WaitForSeconds (startWait); while(true){ for (int i = 0; i < numPerWave; i++) { Spawn (); //????? yield return new WaitForSeconds (spawnWait); } yield return new WaitForSeconds(waveWait); if (gameflag==false) { break; } } } //随机创建小陨石 void Spawn(){ //取三个物体中的一个 GameObject o = hazard[Random.Range(0,hazard.Length)]; //产生随机位置,主要是x不同 Vector3 p = new Vector3 (Random.Range(-spawnValues.x,spawnValues.x),spawnValues.y,spawnValues.z); //默认旋转 Quaternion q = Quaternion.identity; //初始化小陨石 Instantiate (o, p, q); }
需要了解的是yield,这个方法在代码中是经常使用的。这个比较复杂,自行百度。有空再写篇博客介绍。
现在可以有多波小陨石了。怎么计分呢,控制游戏结束,重新开始呢?
在Hierarchy面板中添加三个GUIText,如图:
分别对应得分、游戏结束、游戏重新开始的界面。控制代码如下,这些代码最好是写在GameController.cs:
1
2
3
4
5
6
7
8
9
10//得分 public GUIText scoreText; private int score = 0; //游戏结束 public GUIText gameOver; public bool gameflag = true; //游戏重新开始 public GUIText gameRestart;
1
2
3
4
5void Start(){ //???? spawnValues.x = 6; StartCoroutine (SpawnWaves ()); scoreText.text = "Score: " + score;
1
2
3
4
5//先为空,不显示 gameOver.text = ""; gameRestart.text = ""; }
得分以及游戏结束:
1
2
3
4
5
6
7
8
9
10
11//分数增加 public void AddScore(int v){ score += v; scoreText.text = "Score: " + score; } //游戏结束 public void GameOver(){ gameflag = false; gameOver.text = "Game over"; gameRestart.text = "press R to restart"; }
得分及游戏结束的判断在DestroyByContact.cs文件内,只要是判断陨石碰到了飞机,就调用GameOver();只要是判断陨石碰到了子弹,就调用分数增加。那么怎么判断R键的按下呢。需要在GameController.cs的update()内进行判断:
1
2
3
4
5
6void Update(){ if (gameOver && Input.GetKeyDown (KeyCode.R)) { //Application.LoadLevel (Application.loadedLevel); SceneManager.LoadScene("spceshooter"); } }
ok大功告成!一个原型就这么做出来了。可以改进的地方很多,比如说:
判断飞机打到什么程度就换关卡,不同关卡的难度怎么设计?
怎么添加敌人这个角色,让敌人能够移动并发射子弹打飞机?
飞机有没有大招?
飞机能不能快速移动?
能不能让场景有移动的感觉?
---------------------------------------性感的分割线-------------------------------------------------------------
写项目的时候出现的错误以及解决办法:
说一下背景bg:其实很简单,就是一个Quad,然后把长图贴上去即可。
1.给飞机添加了一个mesh collider,添加了Rigibody之后,运行总是报错:
Non-convex MeshCollider with non-kinematic Rigidbody is no longer supported in Unity 5.
If you want to use a non-convex mesh either make the Rigidbody kinematic or remove the Rigidbody component. Scene hierarchy path "Player", Mesh asset path "Assets/SpaceShooter/Models/vehicle_playerShip_collider.FBX" Mesh name "player_ship_collider"
这个错误的意思就是:
碰撞检测时,如果想使用非凸网格,删除刚体 或者勾选 is kinematic 选项。但是不能删除刚体, is kinematic的意思是是否运动学的。如果勾选上了,这个对象只受transform影响不受force影响,所以就别想让飞机动弹了。解决办法是:
勾选Convex。勾选上了以后就是用非凸网格。报错就消失了。
2.刚开始collider之间的is trigger都填上了,陨石和子弹都在一个平面内的Collider内,所以判断不到陨石和子弹的碰撞,后来只有这个平面Boundary是触发的,其他的都变成了非触发的collider。就可以检测到碰撞了。
原型代码请看评论,评论会给出链接
最后
以上就是丰富冬日最近收集整理的关于初学者Unity项目--太空大战的全部内容,更多相关初学者Unity项目--太空大战内容请搜索靠谱客的其他文章。
发表评论 取消回复