Space Shooter

根據(jù)Unity官網(wǎng)的SpaceShooter教程視頻和參考資料的博客完成了這個小游戲溃蔫,下邊簡單的記錄一下健提。
參考資料:
http://www.reibang.com/p/8cc3a2109d3b
https://unity3d.com/cn/learn/tutorials/projects/space-shooter-tutorial


開發(fā)步驟

  • 1.首先在Unity的 Asset Store下載游戲的資源包,包含了游戲的背景伟叛,模型矩桂,音頻等。

  • 2.將飛機模型Player添加到新建的場景_Scenes中,調(diào)整相機位置侄榴,新建燈光雹锣,調(diào)整燈光位置。

  • 3..為Player添加腳本PlayerController癞蚕,控制Player移動蕊爵。


 void FixedUpdate()
    {
        float moveHorizontal = Input.GetAxis("Horizontal");
        float moveVertical  = Input.GetAxis("Vertical");

        //moveHorizontal和moveVertical記錄玩家輸入的方向數(shù)據(jù)
        Vector3 movement = new Vector3(moveHorizontal, 0.0f, moveVertical);
        //剛體根據(jù)矢量方向movement移動,位移為speed
        GetComponent<Rigidbody>().velocity = movement * speed;

        //移動時傾斜桦山,以Z為中心軸旋轉(zhuǎn)
        GetComponent<Rigidbody>().rotation = Quaternion.Euler(0.0f, 0.0f, GetComponent<Rigidbody>().velocity.x * - tilt);

        //首先序列化Boundary類,保存飛船在X和Z軸上的范圍
        //Mathf.Clamp將飛船的x和z值鎖定在屏幕范圍內(nèi)
        GetComponent<Rigidbody>().position = new Vector3(
            Mathf.Clamp(GetComponent<Rigidbody>().position.x, boundary.xMin, boundary.xMax),
            0.0f,
            Mathf.Clamp(GetComponent<Rigidbody>().position.z, boundary.zMin, boundary.zMax)
            );
    }
  • 4.添加游戲背景攒射,制作子彈,為子彈添加碰撞:添加Rigidbody(diselect use gravity)恒水, 添加碰撞Mesh Collider(select is Trigger)会放,為子彈編寫腳本Mover,使子彈發(fā)射钉凌,將子彈設(shè)為Prefab
   public float speed;
    void Start () {
       //transform.forward:The blue axis of the transform in world space.
        GetComponent<Rigidbody>().velocity = transform.forward * speed;
    }
    
  • 5.設(shè)置火箭發(fā)射子彈咧最。設(shè)置子彈發(fā)射點,新建Shot Spawn御雕,把Shot Spawn拖入到Player下面矢沿,并調(diào)整Shot Spawn的位置到合適的點,在PlayerController編寫腳本
    private float nextFire;  //記錄下一發(fā)子彈的時間
    public float fireRate;    //控制子彈發(fā)射的間隔時間
    public GameObject shot;    //保存我們發(fā)射的子彈Prefab
    public Transform shotSqawn;   //保存子彈的發(fā)射點

    void Update () {
        //按下發(fā)射鍵(ctrl或鼠標左鍵)并且到達下一發(fā)子彈發(fā)射時間
        if (Input.GetButton("Fire1") && Time.time > nextFire)
        {
            nextFire = Time.time + fireRate;//重置下一發(fā)子彈發(fā)射時間
           //Instantiate方法生成一發(fā)子彈
           //3個參數(shù)酸纲,一個是發(fā)射的對象(復(fù)制出一個新的)捣鲸,一個是發(fā)射對象的position,一個是發(fā)射對象的rotation
            Instantiate(shot, shotSqawn.position, shotSqawn.rotation);
            GetComponent<AudioSource>().Play();
        }
    }
  • 6.回收子彈闽坡,飛船每發(fā)射一顆子彈栽惶,就會生成一個Bolt對象。創(chuàng)建一個盒子疾嗅,這個盒子大小和我們的屏幕差不多媒役,然后我們可以判斷如何子彈飛出了這個盒子,就意味著子彈飛出了屏幕宪迟,這個時候我們就可以通知程序來回收子彈酣衷。創(chuàng)建cube,命名Boundary次泽,將盒子調(diào)整到屏幕大小穿仪,刪除 mesh renderer,添加Box Colider意荤,is Trigger set on啊片,為盒子添加腳本DestroyByBoundary
Paste_Image.png
     //DestroyByBoundary中添加碰撞的觸發(fā)方法。即當子彈碰到時玖像,子彈被回收
    void OnTriggerExit(Collider other)
    {
        //注意紫谷,避免將飛船回收
        if(other.tag != "Player")
        {
            Destroy(other.gameObject);
        }
    }
  • 7.創(chuàng)建障礙,創(chuàng)建GameObject,命名為Asteroid笤昨,為Asteroid添加美術(shù)效果祖驱,我們在Assets->Models文件夾中找到prop_asteroid_01文件,然后將其拖入Asteroid做其的子對象瞒窒,為Asteroid添加Rigidibody吹缔,diselect use gravity永脓,將阻力drag 和angular drag設(shè)為0,添加capsule collider茄猫,編寫腳本RandomRotator笛求,使其自動旋轉(zhuǎn)
    public float tumble;
    //tumble來設(shè)置障礙翻滾的幅度
    void Start () {
        GetComponent<Rigidbody>().angularVelocity = Random.insideUnitSphere * tumble;
    }

  • 8.首先我們需要檢測子彈和障礙是否碰撞度气,如果碰撞了校哎,我們需要創(chuàng)建一個爆炸效果來給予反饋薄辅,最后我們還需要將子彈和障礙刪除掉。再為子彈編寫一個腳本DestroyByContact
    public GameObject explosion;    //爆炸效果
    public GameObject playerExplosion;
    void OnTriggerEnter(Collider other)
    {
        //初始時障礙物會與盒子boundary發(fā)生碰撞觸發(fā)此函數(shù)巴比,要規(guī)避此種觸發(fā)
        if (other.tag == "Boundary")
        {
            return;
        }
        //生成一個爆炸效果
        Instantiate(explosion, transform.position, transform.rotation);
        if(other.tag == "Player")
        {
            Instantiate(playerExplosion, other.transform.position, other.transform.rotation);
            gameController.GameOver();
        }

        gameController.AddScore(scoreValue);
        //Debug.Log(other.name);
        Destroy(other.gameObject);
        Destroy(gameObject);
    }

回到Unity編輯器术奖,接著在Assets->Prefabs->VFX->Explosions文件夾中找到explosion_asteroid文件,然后拖入到我們定義的explosion變量中匿辩。

  • 8. 使障礙物向下移動,隨機生成障礙榛丢。mover是之前為子彈添加的腳本铲球,同樣可以用到障礙物上,只需將speed設(shè)置為負數(shù)晰赞,即可實現(xiàn)向下移動稼病。新建一個GameObject, 命名為GameController掖鱼,實現(xiàn)這些邏輯
    public GameObject hazard;   //設(shè)置生成的障礙對象
    public Vector3 spawnValue;  //設(shè)置障礙生成的位置
    public int hazardCount;     //每次生成的障礙的數(shù)量
    public float spawnWait;     //每個障礙生成的間隔時間
    public float startWait;     //游戲開始生成障礙的準備時間
    public float waveWait;      //設(shè)置每一波的間隔時間


    IEnumerator SpawnWaves()
    {
        //讓代碼等待一定時間然走,這是個協(xié)同程序,可以讓游戲不暫停的同時戏挡,讓代碼暫停
        yield return new WaitForSeconds(startWait);
        while (true)
        {
            for (int i = 0; i < hazardCount; i++)
            {
                //隨機生成障礙位置
                Vector3 spawnPosition = new Vector3(Random.Range(-spawnValue.x, spawnValue.x), spawnValue.y, spawnValue.z);
                //Rotation屬性如何確定芍瑞,Rotation屬性是一個Quaternion類型的變量,
                //所以我們定義了一個spawnRotation的變量褐墅,用來記錄障礙的Rotation屬性拆檬,在這里我們不需要讓障礙一開始帶著任何的旋轉(zhuǎn)
                //所以我們直接賦值Quaternion.identity
                Quaternion spawnRotation = Quaternion.identity;

                Instantiate(hazard, spawnPosition, spawnRotation);
                
                yield return new WaitForSeconds(spawnWait);
            }
            yield return new WaitForSeconds(waveWait);

            if(gameOver == true)
            {
                restartText.text = "Press R for Restart";
                restart = true;
                break;
            }
        }
        
    }
    
        void Start () {
            //因為SpawnWaves中有協(xié)同程序,StartCoroutine為啟動一個協(xié)同程序
           StartCoroutine(SpawnWaves());
    }


  • 9. 回收爆炸效果妥凳,新建腳本DestroyByTime竟贯,lifetime為存活時間,將此腳本添加到perfab下的爆炸效果中逝钥,并設(shè)置lifetime屑那。
    public float lifeTime;

    // Use this for initialization
    void Start () {
        Destroy(gameObject,lifeTime);
    }
  • 10.添加背景音樂,Assets->Audio文件夾中找到背景音樂music_background直接拖入GameController對象,同理持际,障礙爆炸和飛船爆炸音效沃琅,也是將其音頻文件直接拖到對應(yīng)的perfab文件中,這些音頻文件是默認play on awake的选酗,其中還要講背景音樂選中l(wèi)oop阵难,子彈音效不能設(shè)為play on wake,只需要在發(fā)射時有聲音芒填,需要在腳本中設(shè)置呜叫,只需在PlayController中添加
    void Update () {
        //按下發(fā)射鍵(ctrl或鼠標左鍵)并且到達下一發(fā)子彈發(fā)射時間
        if (Input.GetButton("Fire1") && Time.time > nextFire)
        {
            nextFire = Time.time + fireRate;
            //Instantiate方法生成一發(fā)子彈,他有3個參數(shù)
           //一個是發(fā)射的對象(復(fù)制出一個新的)殿衰,一個是發(fā)射對象的position朱庆,一個是發(fā)射對象的rotation
            Instantiate(shot, shotSqawn.position, shotSqawn.rotation);

            //當子彈發(fā)射時開啟音效
            GetComponent<AudioSource>().Play();
        }
    }
  • 11.游戲計分按鈕等,新建 scoreText闷祥,restartText娱颊,gameOverText,并調(diào)整其在屏幕上的位置凯砍。在GameController腳本設(shè)置箱硕。
    public Text scoreText;
    public Text restartText;
    public Text gameOverText;
    private bool gameOver;
    private bool restart;
    private int score;

    IEnumerator SpawnWaves()
    {
        //讓代碼等待一定時間,這是個協(xié)同程序悟衩,可以讓游戲不暫停的同時剧罩,讓代碼暫停
        yield return new WaitForSeconds(startWait);
        while (true)
        {
            for (int i = 0; i < hazardCount; i++)
            {
                //隨機生成障礙位置
                Vector3 spawnPosition = new Vector3(Random.Range(-spawnValue.x, spawnValue.x), spawnValue.y, spawnValue.z);
                //Rotation屬性如何確定,Rotation屬性是一個Quaternion類型的變量座泳,
                //所以我們定義了一個spawnRotation的變量惠昔,用來記錄障礙的Rotation屬性,在這里我們不需要讓障礙一開始帶著任何的旋轉(zhuǎn)
                //所以我們直接賦值Quaternion.identity
                Quaternion spawnRotation = Quaternion.identity;

                Instantiate(hazard, spawnPosition, spawnRotation);
                
                yield return new WaitForSeconds(spawnWait);
            }
            yield return new WaitForSeconds(waveWait);

            if(gameOver == true)
            {
                restartText.text = "Press R for Restart";
                restart = true;
                break;
            }
        }
        
    }

    void UpdateScore()
    {
        scoreText.text = "Score :" + score; 
    }

    public void  AddScore(int newScoreValue)
    {
        score += newScoreValue;
        UpdateScore();
    }

    public void GameOver ()
    {
        gameOverText.text = "Game Over !";
        gameOver = true;

    }
    void Start () {
        gameOver = false;
        restart = false;
        restartText.text = "";
        gameOverText.text = "";
        score = 0;
        UpdateScore();
        //因為SpawnWaves中有協(xié)同程序挑势,StartCoroutine為啟動一個協(xié)同程序
        StartCoroutine(SpawnWaves());
    }
    
    void Update () {
        if (restart)
        {
            if (Input.GetKeyDown(KeyCode.R))
            { 
                //重新加載
                Application.LoadLevel(Application.loadedLevel);
            }
        }
    }

在GameController腳本中設(shè)置計分的相關(guān)方法镇防,在DestoryByContact的腳本中調(diào)用這些方法

using UnityEngine;
using System.Collections;

public class DestroyByContact : MonoBehaviour {

    public GameObject explosion;
    public GameObject playerExplosion;


    
    public int scoreValue;//分值
    private GameController gameController; //gameController腳本類

    // Use this for initialization
    void Start () {
        //獲取GameController的實例
        //首先找到GameController的GameObject,然后在通過GetComponent方法來獲取GameController腳本的實例
        GameObject gameControllerObject = GameObject.FindWithTag("GameController");
        if (gameControllerObject != null)
        {
            gameController = gameControllerObject.GetComponent<GameController>();
        }
        if (gameController == null)
        {
            Debug.Log("Cannot find 'GameController' script");
        }
    }
    
    // Update is called once per frame
    void Update () {
    
    }

    void OnTriggerEnter(Collider other)
    {
        //初始時障礙物會與盒子boundary發(fā)生碰撞觸發(fā)此函數(shù)潮饱,要規(guī)避此種觸發(fā)
        if (other.tag == "Boundary")
        {
            return;
        }
        //生成一個爆炸效果
        Instantiate(explosion, transform.position, transform.rotation);

        //飛船碰到障礙物時爆炸效果
        if(other.tag == "Player")
        {
            Instantiate(playerExplosion, other.transform.position, other.transform.rotation);

            //飛船爆炸時調(diào)用gameover方法
            gameController.GameOver();  
        }

        gameController.AddScore(scoreValue);
        //Debug.Log(other.name);
        Destroy(other.gameObject);
        Destroy(gameObject);
    }
}

總結(jié):在實際操作中来氧,對unity的一些構(gòu)建,基本的操作不熟悉香拉,還有場景的設(shè)置即位置等饲漾。了解到了腳本的功能,控制GameObject缕溉,觸發(fā)事件考传,不同的腳本之間相互調(diào)用。這個游戲证鸥,通過腳本新建的對象如子彈障礙物是不會自動回收的僚楞,需要在最外圍設(shè)置一個框來檢測物體碰撞觸發(fā)事件進行回收勤晚,同時爆炸時Instantiate方法產(chǎn)生的爆炸效果,也不會被自動回收泉褐,需要對這些效果寫腳本DestoryByTime定時進行Destory赐写。


畫一個非常丑的圖。膜赃。挺邀。

  • PlayerController:飛船的移動,發(fā)射子彈跳座。
  • Mover:設(shè)置速度端铛,自動控制移動。
  • RandomRotator: 自動控制障礙物旋轉(zhuǎn)疲眷。
  • DestoryByContact:碰撞時觸發(fā)的方法禾蚕,物體消失,爆炸特效狂丝,計分换淆,飛船爆炸時調(diào)用GameController的GameOver方法。
  • DestoryByBoundary:子彈或障礙物出邊界几颜,即與Boundary相碰撞觸發(fā)方法Destory對象倍试。
  • DestoryByTime:特效對象Destory。
  • GameController: 產(chǎn)生障礙物蛋哭,計分县习,Restart等方法實現(xiàn)。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末具壮,一起剝皮案震驚了整個濱河市准颓,隨后出現(xiàn)的幾起案子哈蝇,更是在濱河造成了極大的恐慌棺妓,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,324評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件炮赦,死亡現(xiàn)場離奇詭異怜跑,居然都是意外死亡,警方通過查閱死者的電腦和手機吠勘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評論 3 392
  • 文/潘曉璐 我一進店門性芬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人剧防,你說我怎么就攤上這事植锉。” “怎么了峭拘?”我有些...
    開封第一講書人閱讀 162,328評論 0 353
  • 文/不壞的土叔 我叫張陵俊庇,是天一觀的道長狮暑。 經(jīng)常有香客問我,道長辉饱,這世上最難降的妖魔是什么搬男? 我笑而不...
    開封第一講書人閱讀 58,147評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮彭沼,結(jié)果婚禮上缔逛,老公的妹妹穿的比我還像新娘。我一直安慰自己姓惑,他們只是感情好褐奴,可當我...
    茶點故事閱讀 67,160評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著挺益,像睡著了一般歉糜。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上望众,一...
    開封第一講書人閱讀 51,115評論 1 296
  • 那天匪补,我揣著相機與錄音,去河邊找鬼烂翰。 笑死夯缺,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的甘耿。 我是一名探鬼主播踊兜,決...
    沈念sama閱讀 40,025評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼佳恬!你這毒婦竟也來了捏境?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,867評論 0 274
  • 序言:老撾萬榮一對情侶失蹤毁葱,失蹤者是張志新(化名)和其女友劉穎垫言,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體倾剿,經(jīng)...
    沈念sama閱讀 45,307評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡筷频,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,528評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了前痘。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片凛捏。...
    茶點故事閱讀 39,688評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖芹缔,靈堂內(nèi)的尸體忽然破棺而出坯癣,到底是詐尸還是另有隱情,我是刑警寧澤最欠,帶...
    沈念sama閱讀 35,409評論 5 343
  • 正文 年R本政府宣布示罗,位于F島的核電站蓬网,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏鹉勒。R本人自食惡果不足惜帆锋,卻給世界環(huán)境...
    茶點故事閱讀 41,001評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望禽额。 院中可真熱鬧锯厢,春花似錦、人聲如沸脯倒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽藻丢。三九已至剪撬,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間悠反,已是汗流浹背残黑。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留斋否,地道東北人梨水。 一個月前我還...
    沈念sama閱讀 47,685評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像茵臭,于是被迫代替她去往敵國和親疫诽。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,573評論 2 353

推薦閱讀更多精彩內(nèi)容