【Unity3D開(kāi)發(fā)】打飛碟小游戲

本文同時(shí)發(fā)布至我的個(gè)人博客增显,點(diǎn)擊進(jìn)入我的個(gè)人博客閱讀。本博客供技術(shù)交流與經(jīng)驗(yàn)分享隔嫡,可自由轉(zhuǎn)載甸怕。轉(zhuǎn)載請(qǐng)?jiān)谠u(píng)論區(qū)或私信簡(jiǎn)單通知,感謝腮恩!

游戲簡(jiǎn)介

打飛碟小游戲是一款射擊類小游戲梢杭,游戲規(guī)則如下:

  • 每回合開(kāi)始,玩家按“空格鍵”發(fā)射若干飛碟(飛碟數(shù)量隨回合數(shù)增加而增加)秸滴,玩家通過(guò)鼠標(biāo)控制點(diǎn)擊武契,擊中飛碟時(shí)獲得10分。
  • 若飛碟直到掉落在地還未被玩家擊中荡含,則玩家扣除10分咒唆。
  • 若玩家分?jǐn)?shù)低于或等于0分時(shí),游戲失敗释液。
  • 玩家分?jǐn)?shù)每達(dá)到一定分?jǐn)?shù)(20分全释、40分等),則關(guān)卡升級(jí)误债。發(fā)射飛碟數(shù)量增多浸船,發(fā)射速度加快。

游戲展示

設(shè)計(jì)思路

? 這次游戲設(shè)計(jì)繼續(xù)采用MVC架構(gòu)寝蹈,根據(jù)自頂向下的設(shè)計(jì)思路李命,我首先考慮最頂層的控制類GameScenceController,它應(yīng)該以單實(shí)例形式存在箫老,并且通過(guò)對(duì)其它類提供功能不同的接口來(lái)實(shí)現(xiàn)向類轉(zhuǎn)達(dá)信息封字。所以從功能考慮,GameScenceController實(shí)現(xiàn)了IUserInterface(用戶操作接口), IQueryStatus(狀態(tài)查詢接口), setStatus(狀態(tài)設(shè)置接口), IScore(分?jǐn)?shù)相關(guān)功能接口)耍鬓,這樣我們就完成了Control層的設(shè)計(jì)阔籽。

? Model層負(fù)責(zé)游戲邏輯,這次游戲邏輯較為簡(jiǎn)單牲蜀,所以我用GameModel類來(lái)實(shí)現(xiàn)控制游戲分?jǐn)?shù)仿耽、判斷游戲輸贏的邏輯功能。

? View層較為復(fù)雜各薇。首先需要一個(gè)場(chǎng)景類Scence來(lái)設(shè)置飛碟的數(shù)量大小速度等屬性、控制發(fā)射飛碟和隱藏飛碟,并且它要負(fù)責(zé)判斷場(chǎng)景中的飛碟狀態(tài)并告知控制類峭判。其次开缎,為了實(shí)現(xiàn)飛碟對(duì)象的重復(fù)使用,使用工廠類DiskFactory創(chuàng)建和回收飛碟林螃。最后奕删,UI部分需要提供兩個(gè)類:一個(gè)UserInterface類負(fù)責(zé)處理用戶的操作UI類負(fù)責(zé)實(shí)現(xiàn)游戲的UI視圖疗认。

開(kāi)發(fā)過(guò)程

(一)理解“接口”與使用接口

? 由于之前做的更多是一些C和C++的小項(xiàng)目完残,對(duì)于C#這種純面向?qū)ο笳Z(yǔ)言中“接口”的概念總覺(jué)得沒(méi)辦法很好地理解,相信也有許多新手和我有相同的困惑横漏。通過(guò)這次項(xiàng)目的開(kāi)發(fā)經(jīng)歷谨设,我覺(jué)得自己已經(jīng)能較完整的理解“接口”這個(gè)概念了。下面結(jié)合代碼來(lái)談?wù)勎覀€(gè)人對(duì)“接口”設(shè)計(jì)的理解:

public class GameScenceController : IUserInterface, IQueryStatus, setStatus, IScore
{
  //...
}

public class UserInterface : MonoBehaviour
{
    //...
    void Start()
    {
        //將控制類實(shí)例轉(zhuǎn)化為特定的接口類型
        _uerInterface = GameScenceController.getGSController() as IUserInterface;
        _queryStatus = GameScenceController.getGSController() as IQueryStatus;
        _changeScore = GameScenceController.getGSController() as IScore;
    }
}

? 像許多新手一樣缎浇,此前我對(duì)接口的理解停留在重要性上——使用接口可以使代碼可讀性更強(qiáng)扎拣,結(jié)構(gòu)更加清晰。其實(shí)素跺,使用接口在架構(gòu)設(shè)計(jì)的層面上看二蓝,有一定的必要性。就像上面的代碼所示指厌,對(duì)于UserInterface類刊愚,他需要向控制類獲取游戲狀態(tài)、分?jǐn)?shù)的相關(guān)信息踩验,同時(shí)需要向控制類傳達(dá)用戶操作信息鸥诽,但是它并不需要也不應(yīng)該得到設(shè)置游戲狀態(tài)的功能。像這種晰甚,需要向一個(gè)類提供部分功能的情況衙传,使用接口就能容易地實(shí)現(xiàn),只要將控制類實(shí)例轉(zhuǎn)化為特定的接口類型厕九,就能限制其提供的功能蓖捶。這符合面向?qū)ο笤O(shè)計(jì)的封裝性原則。

(二)關(guān)于Unity剛體—— Rigidbody

? 當(dāng)Unity中的一個(gè)對(duì)象被添加了Rigidbody組件扁远,它便成為了一個(gè)“剛體”俊鱼,即Unity會(huì)對(duì)其應(yīng)用物理引擎,于是對(duì)象便會(huì)像現(xiàn)實(shí)物體一樣存在受到力的作用畅买。這次開(kāi)發(fā)過(guò)程在設(shè)置Disk時(shí)簡(jiǎn)單的使用了一下Rigidbody組件并闲。

public void sendDisk(List<GameObject> usingDisks)
{
    this.usingDisks = usingDisks;
    this.Reset(round);
    for (int i = 0; i < usingDisks.Count; i++)
    {
        var localScale = usingDisks[i].transform.localScale;
        usingDisks[i].transform.localScale = new Vector3(localScale.x * diskScale, localScale.y * diskScale, localScale.z * diskScale);
        usingDisks[i].GetComponent<Renderer>().material.color = diskColor;
        usingDisks[i].transform.position = new Vector3(startPosition.x, startPosition.y + i, startPosition.z);
        //使用Rigidbody
        Rigidbody rigibody;
        rigibody = usingDisks[i].GetComponent<Rigidbody>();
        rigibody.WakeUp();
        rigibody.useGravity = true;
        rigibody.AddForce(startDiretion * Random.Range(diskSpeed * 5, diskSpeed * 8) / 5, ForceMode.Impulse);
        GameScenceController.getGSController().setScenceStatus(ScenceStatus.shooting);
    }
}

public void destroyDisk(GameObject disk)
{
    disk.GetComponent<Rigidbody>().Sleep();
    disk.GetComponent<Rigidbody>().useGravity = false;
    disk.transform.position = new Vector3(0f, -99f, 0f);
}

? 結(jié)合這次的工廠回收機(jī)制,一個(gè)Disk使用完畢后并不是將它銷毀谷羞,而是暫時(shí)隱藏等待再次使用帝火。這樣的情況下溜徙,我們將Disk暫時(shí)保存時(shí),需要暫時(shí)讓其Rigidbody不工作犀填。原因很簡(jiǎn)單蠢壹,如果不停用其Rigidbody,那么Disk會(huì)一直受到力的作用而不停的運(yùn)動(dòng)九巡,這顯然是會(huì)消耗游戲性能的图贸,所以我們不希望這種情況出現(xiàn)。這里我選擇了使用Rigidbody.Sleep()Rigidbody.WakeUp()來(lái)停用和啟用Rigidbody組件冕广。

(三)再談工廠模式

? 上次制作簡(jiǎn)單工廠項(xiàng)目時(shí)曾小試過(guò)一次工廠模式的使用疏日,這次在項(xiàng)目中我同樣也引入工廠模式來(lái)創(chuàng)建和回收飛碟。

public class DiskFactory : System.Object
{
    public GameObject diskPrefab;

    private static DiskFactory _diskFactory;
    List<GameObject> usingDisks;
    List<GameObject> uselessDisks;

    public static DiskFactory getFactory()
    {
        if (_diskFactory == null)
        {
            _diskFactory = new DiskFactory();
            _diskFactory.uselessDisks = new List<GameObject>();
            _diskFactory.usingDisks = new List<GameObject>();
        }
        return _diskFactory;
    }

    public List<GameObject> prepareDisks(int diskCount)
    {
        for (int i = 0; i < diskCount; i++)
        {
            if (uselessDisks.Count == 0)
            {
                GameObject disk = GameObject.Instantiate<GameObject>(diskPrefab);
                usingDisks.Add(disk);
            }
            else
            {
                GameObject disk = uselessDisks[0];
                uselessDisks.RemoveAt(0);
                usingDisks.Add(disk);
            }
        }
        return this.usingDisks;
    }

    public void recycleDisk(GameObject disk)
    {
        int index = usingDisks.FindIndex(x => x == disk);
        uselessDisks.Add(disk);
        usingDisks.RemoveAt(index);
    }
}

? 這次的工廠模式實(shí)現(xiàn)較為完整撒汉,使用兩個(gè)List——usingDisksuselessDisks來(lái)分別儲(chǔ)存正在使用和使用完畢的Disk沟优。需要注意的是,由于DiskFactory并非是繼承MonoBehaviour的類神凑,所以無(wú)法直接導(dǎo)入預(yù)設(shè)净神,只能在其它類中導(dǎo)入Disk預(yù)設(shè)再將其傳給DiskFactory

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末溉委,一起剝皮案震驚了整個(gè)濱河市鹃唯,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌瓣喊,老刑警劉巖坡慌,帶你破解...
    沈念sama閱讀 222,807評(píng)論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異藻三,居然都是意外死亡洪橘,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,284評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門棵帽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)熄求,“玉大人,你說(shuō)我怎么就攤上這事逗概〉芡恚” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 169,589評(píng)論 0 363
  • 文/不壞的土叔 我叫張陵逾苫,是天一觀的道長(zhǎng)卿城。 經(jīng)常有香客問(wèn)我,道長(zhǎng)铅搓,這世上最難降的妖魔是什么瑟押? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 60,188評(píng)論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮星掰,結(jié)果婚禮上多望,老公的妹妹穿的比我還像新娘嫩舟。我一直安慰自己,他們只是感情好便斥,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,185評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布至壤。 她就那樣靜靜地躺著,像睡著了一般枢纠。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上黎棠,一...
    開(kāi)封第一講書(shū)人閱讀 52,785評(píng)論 1 314
  • 那天晋渺,我揣著相機(jī)與錄音,去河邊找鬼脓斩。 笑死木西,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的随静。 我是一名探鬼主播八千,決...
    沈念sama閱讀 41,220評(píng)論 3 423
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼燎猛!你這毒婦竟也來(lái)了恋捆?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 40,167評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤重绷,失蹤者是張志新(化名)和其女友劉穎沸停,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體昭卓,經(jīng)...
    沈念sama閱讀 46,698評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡愤钾,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,767評(píng)論 3 343
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了候醒。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片能颁。...
    茶點(diǎn)故事閱讀 40,912評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖倒淫,靈堂內(nèi)的尸體忽然破棺而出伙菊,到底是詐尸還是另有隱情,我是刑警寧澤昌简,帶...
    沈念sama閱讀 36,572評(píng)論 5 351
  • 正文 年R本政府宣布占业,位于F島的核電站,受9級(jí)特大地震影響纯赎,放射性物質(zhì)發(fā)生泄漏谦疾。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,254評(píng)論 3 336
  • 文/蒙蒙 一犬金、第九天 我趴在偏房一處隱蔽的房頂上張望念恍。 院中可真熱鬧六剥,春花似錦、人聲如沸峰伙。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,746評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)瞳氓。三九已至策彤,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間匣摘,已是汗流浹背店诗。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,859評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留音榜,地道東北人庞瘸。 一個(gè)月前我還...
    沈念sama閱讀 49,359評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像赠叼,于是被迫代替她去往敵國(guó)和親擦囊。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,922評(píng)論 2 361

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