首页 > 代码库 > 【Unity3D游戏开发】—— 太空大战打飞机游戏详解

【Unity3D游戏开发】—— 太空大战打飞机游戏详解

    大家好我是Kaven,今天给大家带来的是一个Unity3D的打飞机的小项目。俗话说“眼看千变,不如手过一边”,学了差不多两星期Unity3D了,是时候找个小项目练练手了。于是就翻出了cocos2d-x的一个例子,模仿着写了一个飞机大战的游戏,虽然有一些功能没有实现,但总体上是完成了。闲话少说,下面开始吧。

    由于这个用的是Unity3d自带的原生的2D插件native 2d,所以创建项目时左下角选择2d。


    这个小游戏我分了三个场景,菜单场景,游戏场景,结束场景。由于菜单场景与结束场景都比较简单,所以主要就说说游戏主场景。源码在文章结尾处。


1.创建游戏场景

  由于创建的是2d项目,则将图片拖入Scene中,该图片自动成为一个精灵,所以将图片信息拖入摆放即可。

                     

    下面创建一个GameManager文件,其绑定在摄像机中,主要负责飞机生命,玩家得分,背景播放等的显示功能。而生命,得分的显示需要我们创建GUIText来摆放。

代码如下:

using UnityEngine;
using System.Collections;

public class GameManager : MonoBehaviour {

    //显示控件位置
    private Transform life;
    private Transform score;

    //游戏主角对象
    private GameObject player;

	void Start () {

        //得到显示控件位置
        life = transform.Find("Life");
        score = transform.Find("Score");
        //获取主角飞机对象
        player = GameObject.FindGameObjectWithTag("Player");
	}

	void Update () {
        if (player != null)
        {
            life.guiText.text = "生命:" + player.GetComponent<Player>().max_life.ToString();
            score.guiText.text = "分数:" + player.GetComponent<Player>().grade.ToString();
        }
        if (life.guiText.text == "生命:0")
        {
            //跳转到游戏结束场景
            Application.LoadLevel(2);
        }
	}
}

然后我们来实现背景的滚动。首先创建两个一样的背景图片上下摆放,如下图所示,这里切换到3d模式,更加形象

         

下面创建用于绑定在背景上的脚本BGControll.cs,代码如下

using UnityEngine;
using System.Collections;

public class BGControll : MonoBehaviour {

    //背景滚动速度
    public float speed = 1.5f;
	
	void Update () {
        transform.Translate(Vector3.down * Time.deltaTime * speed);
        if (transform.position.y <= -5.6f)
        {
            transform.position = new Vector3(0, 5.7f, 0);
        }
        
	}
}
然后将脚本绑定在两张背景图上即可。

2.创建主角飞机

主角创建与背景创建基本一样,也是可以用一张2d贴图,就不多说了。主角脚本主要处理移动,发射子弹,处理碰撞等,代码如下:

using UnityEngine;
using System.Collections;

public class Player : MonoBehaviour {

    //飞机生命值
    public int max_life = 5;
    //子弹飞行速度
    public float m_speed;
    //子弹对象
    public GameObject bullet;
    //子弹发射位置
    private Transform bulletLeft;
    private Transform bulletRight;
    //发射子弹频率
    private float bulletRate = 0;
    private Transform m_transform;

    //声音资源
    private AudioSource audio;
    [HideInInspector]
    //分数
    public int grade = 0;

	//初始化变量
	void Start () {
        bulletLeft = this.transform.Find("bulletL");
        bulletRight = this.transform.Find("bulletR");
        m_transform = this.transform;
        audio = GetComponent<AudioSource>();
	}
	
	void Update () {
        //上方向键
	    if (Input.GetKey(KeyCode.UpArrow))
	    {
            m_transform.Translate(Vector3.up * Time.deltaTime * m_speed);
	    }
        //左方向键
        if (Input.GetKey(KeyCode.LeftArrow))
        {
            m_transform.Translate(Vector3.left * Time.deltaTime * m_speed);
        }
        //下方向键
        if (Input.GetKey(KeyCode.DownArrow))
        {
            m_transform.Translate(Vector3.up * Time.deltaTime * (-m_speed));
        }
        //右方向键
        if (Input.GetKey(KeyCode.RightArrow))
        {
            m_transform.Translate(Vector3.left * Time.deltaTime * (-m_speed));
        }

        bulletRate -= Time.deltaTime;
        if (bulletRate <= 0)
        {
            bulletRate = 0.1f;
            //按下空格键,发射子弹
            if (Input.GetKey(KeyCode.Space))
            {
                
                Instantiate(bullet, bulletLeft.transform.position, Quaternion.Euler(new Vector3(0, 1, 0)));
                Instantiate(bullet, bulletRight.transform.position, Quaternion.identity);
                //播放射击声音
                audio.Play();
            }
        }
        

        check();
	}

    //检测边界,使飞机不飞出摄像机
    void check()
    {
        if (m_transform.position.x <= -1.3f)
        {
            m_transform.position = new Vector3(-1.3f, m_transform.position.y, 0);
        }
        if (m_transform.position.x >= 1.3f)
        {
            m_transform.position = new Vector3(1.3f, m_transform.position.y, 0);
        }
        if (m_transform.position.y >= 2.5f)
        {
            m_transform.position = new Vector3(m_transform.position.x, 2.5f, 0);
        }
        if (m_transform.position.y <= -2.5f)
        {
            m_transform.position = new Vector3(m_transform.position.x, -2.5f, 0);
        }
    }

    //碰撞时调用该方法,飞机减血
    public void Hit(int Value)
    {
        if (max_life > 0)
        {
            max_life -= Value;
        }
        else
        {
            Destroy(this.gameObject);
            //Application.LoadLevel(2);
        }
    }

    //增加分数
    public void Add(int score)
    {
        grade += score;
    }
}
上面代码中我用了bulletLeft,bulletRight,这是我为主机添加的两个子对象,主要是用于发射子弹,相当于炮筒吧,而bullet则是定义的预设,其中绑定的脚本后面会说明。



下面是Bullet子弹预设绑定的脚本代码,子弹主要功能为与敌人发生碰撞,主角得分,敌人减血。

using UnityEngine;
using System.Collections;

public class Bullet : MonoBehaviour {

    //子弹的速度
    public float m_speed = 2f;
    //子弹存在时间
    public float lifeTime = 20f;
    //子弹碰撞到敌人伤害
    public int power = 2;
    //玩家飞机
    private GameObject player;

	void Start () {
        //根据Tag找到玩家飞机对象
	    player = GameObject.FindGameObjectWithTag("Player");
	}
	
	void Update () {
        //移动子弹
        transform.Translate(Vector3.up * Time.deltaTime * m_speed);
	}

    void OnTriggerEnter2D(Collider2D mCollider)
    {
        //如果碰撞的是敌人
        if (mCollider.gameObject.tag == "Enemy")
        {
            //敌人减血
            mCollider.gameObject.GetComponent<Enemy>().Hit(power);
            //得分
            player.GetComponent<Player>().Add(100);
            //销毁子弹
            Destroy(this.gameObject);
        }
    }

    //当离开摄像机范围时触发销毁事件
    void OnBecameInvisible()
    {
        Destroy(this.gameObject);
    }
}
期间犯了个非常2的错误就是将OnTriggerEnter2D写成了OnTriggerEnter导致一直没有碰撞,我竟然忘记我建的是2d项目了,函数名改了也不知道,查了些资料终于解决了,不仔细啊。


好了,通过以上的代码我们就可以按键盘的方向键移动飞机,按空格键发射子弹了,我已经等不及要打敌人了。

3.创建敌机

敌机也是先拖入敌机图片,然后绑定脚本,添加组件,然后保存为预设,我们以后还有个敌机生产器要生产敌机呢。

我创建了三种敌机预设


敌机主要是移动,碰撞。下面来看看敌机脚本:

using UnityEngine;
using System.Collections;

public class Enemy : MonoBehaviour {

    //敌机移动速度
    public float m_speed = 3;
    //生命值
    public int max_life = 5;
    //得到爆炸动画
    private Animator animator;

    //爆炸声音
    private AudioSource audio;

	void Start () {
        animator = GetComponent<Animator>();
        audio = GetComponent<AudioSource>();
	}
	
	void Update () {
        transform.Translate(Vector3.down * Time.deltaTime * m_speed);
	}

    void OnTriggerEnter2D(Collider2D mCollider)
    {
        //敌机与主机碰撞双方掉血
        if (mCollider.gameObject.tag == "Player")
        {
            //敌机掉血
            this.Hit(1);
            //主机掉血
            mCollider.gameObject.GetComponent<Player>().Hit(1);
        }
    }

    //减血
    public void Hit(int value)
    {
        //如果生命值大于0
        if (max_life > 0)
        {
            //生命值降低
            max_life -= value;
        }
        //如果等于0,则播放动画
        else
        {
            audio.Play();
            animator.SetBool("isLive", false);
            AnimatorStateInfo animatorState = animator.GetCurrentAnimatorStateInfo(0);
            //销毁敌机
            if (animatorState.IsName("Base Layer.explosion"))
            {
                Destroy(this.gameObject);
            }
        }
    }

    //离开摄像机视野时销毁对象
    void OnBecameInvisible()
    {
        Destroy(this.gameObject);
    }
}

上面使用到了一个动画,就是敌机爆炸时的动画。那么如何创建动画呢?首先资源中可以拖动一组连续的爆炸图片到场景中,系统会自动提醒创建动画,命名为explosion即可。然后Project视图中就出现了一个animation和一个animator。在菜单栏找到Windows -> Animation和 Winodws -> Animator可以打开如下窗口。



具体的动画编辑可以看看雨松MOMO的关于动画的文章.


下面是敌机生产器,在主界面的左右两边的顶部分别放上两个空物体用来创建敌机。敌机创建时间为1~3秒不等,然后绑定创建脚本即可。

using UnityEngine;
using System.Collections;

public class SpawnEnemy : MonoBehaviour {

    //生成敌人速度
    public float spawnTime = 1.0f;
    //敌人对象数组
    public GameObject[] enemys;

	void Start () {
        StartCoroutine("spawn");
	}

    void Update()
    {
        //生成敌人速度在1~3秒之间变化
        spawnTime = Random.Range(1.0f, 3.0f);
    }

    IEnumerator spawn()
    {
        //等待
        yield return new WaitForSeconds(spawnTime);
        //克隆对象
        Instantiate(enemys[Random.Range(0, 3)], new Vector3(transform.position.x * Random.Range(-1.2f, 1.2f), transform.position.y, 0), Quaternion.Euler(0, 0, 0));
        StartCoroutine("spawn");
    }
}

4.关于碰撞检测

子弹:

    void OnTriggerEnter2D(Collider2D mCollider)
    {
        //如果碰撞的是敌人
        if (mCollider.gameObject.tag == "Enemy")
        {
            //敌人减血
            mCollider.gameObject.GetComponent<Enemy>().Hit(power);
            //得分
            player.GetComponent<Player>().Add(100);
            //销毁子弹
            Destroy(this.gameObject);
        }
    }

敌机:

    void OnTriggerEnter2D(Collider2D mCollider)
    {
        //敌机与主机碰撞双方掉血
        if (mCollider.gameObject.tag == "Player")
        {
            //敌机掉血
            this.Hit(1);
            //主机掉血
            mCollider.gameObject.GetComponent<Player>().Hit(1);
        }
    }
碰撞检测需要添加2d刚体组件和2d碰撞器组件,在Physics 2D中,不要选错了。关于碰撞检测的比较可以看看我的这篇文章【Unity3D游戏开发】—— 碰撞检测。


最后就运行一下感受一下打飞机的快感吧(遮面)

  

基本上这个一个小游戏也就完成了,关于其他的一些功能可以随便加嘛,多几个关卡就多创建几个场景,还能加上BOSS战什么的。

由于无法导出我就打包了,源代码:https://github.com/GeeKaven/MoonPlane


最后说下我的想法吧, 今天早上一大早起来就去图书馆了,然后就坐在电脑前一直做到下午晚饭时才搞定。虽然这对于一个大牛来说不难,可能一会儿就解决了。但对于我一个新手还是蛮有挑战性的(哈哈)。有些事是不做永远不知道难度有多大,有些事是做了才会明白会有这么多的问题,还有些事是做了会觉得也不过如此嘛,总之一点理论的基础上多实践,熟能生巧慢慢的也就会了。


最后附上一句话共勉:
             每一个成功者都是一位苦行僧,只有他们自己才知道,通往成功的道路上,到底有多少寂寞与不解。



文章作者:wxk_bigkang(Kaven) 

转帖注明:http://blog.csdn.net/wxk_bigworld/article/details/25565325