Unity3D遊戲開發之快速打造流行的關卡系統,unity3d遊戲開發

來源:互聯網
上載者:User

Unity3D遊戲開發之快速打造流行的關卡系統,unity3d遊戲開發

各位朋友,大家好,歡迎大家關注我的部落格,我是秦元培,我的部落格地址是blog.csdn.net/qinyuanpei。
今天想和大家分享的是目前在移動平台上較為流行的關卡系統,關卡系統通常是單機手機遊戲如《憤怒的小鳥》、《保衛蘿蔔》中對遊戲內容的組織形式,玩家可通過已解鎖的關卡(預設第一關是已解鎖的)擷取分數進而解鎖新的關卡,或者是通過付費購買解鎖新的關卡。那麼好了,在今天的文章中博主將帶領大家快速實現一個可擴充的關卡系統,這個執行個體的靈感來自博主最近的工作經曆,希望對大家學習Unity3D遊戲起到一定協助性的作用。

原理

在本地配置一個Xml檔案,在這個檔案中定義當前遊戲中關卡的相關資訊,通過解析該檔案並和UI綁定最終實現一個完整的關卡系統。

1、定義關卡

首先我們來定義一個關卡的基本結構:

public class Level{    /// <summary>    /// 關卡ID    /// </summary>    public string ID;    /// <summary>    /// 關卡名稱    /// </summary>    public string Name;    /// <summary>    /// 關卡是否解鎖      /// </summary>    public bool UnLock = false;}

在這裡,我們假定關卡的名稱和該關卡在Unity3D中情境名稱一致。其中最為重要的一個屬性是UnLock,該值是一個布爾型變數,表明該關卡是否解鎖,因為在遊戲中,只有解鎖的情境是可以訪問的。

2、定義關卡設定檔

從關卡的基本結構Level可以定義出如下的設定檔,這裡使用Xml作為設定檔的儲存形式:

<?xml version="1.0" encoding="utf-8"?><levels>  <level id="0" name="level0" unlock="1" />  <level id="1" name="level1" unlock="0" />  <level id="2" name="level2" unlock="0" />  <level id="3" name="level3" unlock="0" />  <level id="4" name="level4" unlock="0" />  <level id="5" name="level5" unlock="0" />  <level id="6" name="level6" unlock="0" />  <level id="7" name="level7" unlock="0" />  <level id="8" name="level8" unlock="0" />  <level id="9" name="level9" unlock="0" /></levels>

和關卡結構定義類似,這裡使用0和1來表示關卡的解鎖情況,0表示未解鎖,1表示解鎖,可以注意到預設情況下第一個關卡是解鎖的,這符合我們在玩《憤怒的小鳥》這類遊戲時的直觀感受。那麼好了,在完成了關卡的結構定義和設定檔定義後,接下來我們開始思考如何來實現一個關卡系統,因為此處並不涉及到Unity3D情境中的具體邏輯,因此我們在關卡系統中主要的工作就是維護好主介面情境和各個遊戲情境的跳轉關係,我們可以注意到這裡要完成兩件事情,即第一要將設定檔中的關卡以一定形式載入到主介面中,並告訴玩家哪些關卡是已解鎖的、哪些關卡是未解鎖的,當玩家點擊不同的關卡時可以得到不同的響應,已解鎖的關卡可以訪問並進入遊戲環節,未解鎖的關卡則需要獲得更多的分數或者是通過付費來解鎖關卡;第二是要對關卡進行編輯,當玩家獲得了分數或者是支付一定的費用後可以解鎖關卡進入遊戲環節。這兩點綜合起來就是我們需要對關卡的設定檔進行讀寫,因為我們注意到一個關卡是否解鎖僅僅取決於unlock屬性,那麼好了,明白了這一點後我們來動手編寫一個維護關卡的類。

3、編寫一個維護關卡的類

這裡直接給出代碼,因為從嚴格的意義上來說,這段代碼並非我們此刻關注的重點,可能這讓大家感到難以適應,因為文章明明就是在教我們實現一個關卡系統,可是此刻博主卻說這部分不重要了,請大家稍安勿躁,因為這裡有比代碼更為深刻的東西。

using UnityEngine;using System.Collections;using System.Collections.Generic;using System.Xml;public static class LevelSystem {    /// <summary>    /// 載入Xml檔案     /// </summary>    /// <returns>The levels.</returns>    public static List<Level> LoadLevels()    {        //建立Xml對象        XmlDocument xmlDoc = new XmlDocument();        //如果本地存在設定檔則讀取設定檔        //否則在本地建立設定檔的副本        //為了跨平台及可讀可寫,需要使用Application.persistentDataPath        string filePath = Application.persistentDataPath + "/levels.xml";        if (!IOUntility.isFileExists (filePath)) {            xmlDoc.LoadXml (((TextAsset)Resources.Load ("levels")).text);            IOUntility.CreateFile (filePath, xmlDoc.InnerXml);        } else {            xmlDoc.Load(filePath);        }        XmlElement root = xmlDoc.DocumentElement;        XmlNodeList levelsNode = root.SelectNodes("/levels/level");        //初始化關卡列表        List<Level> levels = new List<Level>();        foreach (XmlElement xe in levelsNode)         {            Level l=new Level();            l.ID=xe.GetAttribute("id");            l.Name=xe.GetAttribute("name");            //使用unlock屬性來標識當前關卡是否解鎖            if(xe.GetAttribute("unlock")=="1"){                l.UnLock=true;            }else{                l.UnLock=false;            }            levels.Add(l);        }        return levels;    }    /// <summary>    /// 設定某一關卡的狀態    /// </summary>    /// <param name="name">關卡名稱</param>    /// <param name="locked">是否解鎖</param>    public static void SetLevels(string name,bool unlock)    {        //建立Xml對象        XmlDocument xmlDoc = new XmlDocument();        string filePath=Application.persistentDataPath + "/levels.xml";        xmlDoc.Load(filePath);        XmlElement root = xmlDoc.DocumentElement;        XmlNodeList levelsNode = root.SelectNodes("/levels/level");        foreach (XmlElement xe in levelsNode)         {            //根據名稱找到對應的關卡            if(xe.GetAttribute("name")==name)            {                //根據unlock重新為關卡賦值                if(unlock){                    xe.SetAttribute("unlock","1");                }else{                    xe.SetAttribute("unlock","0");                }            }        }        //儲存檔案        xmlDoc.Save (filePath);    }}

這裡我們首先將關卡設定檔levels.xml放置在Resources目錄下,這是因為我們可以使用Resources.Load()這種方式來載入本地資源,這種方式對於Unity3D來說有著得天獨厚的優勢:
* 它使用相對於Resources目錄的相對路徑,所以在使用的時候不用考慮是相對路徑還是絕對路徑的問題
* 它使用名稱來尋找一個本地資源,所以在使用的時候不用考慮副檔名和檔案格式的問題
* 它可以是Unity3D支援的任意類型,從貼圖到預製體再到文字檔等等,可以和Unity3D的API完美地結合

說了這麼多它的優點,我們自然要痛心疾首地說說它的缺點,它的缺點是什麼呢?那就是不支援寫入操作,這當然不能責怪Unity3D,因為當Unity3D匯出遊戲的時候會將Rsources目錄下的內容壓縮後再匯出,我們當然不能要求在一個壓縮後的檔案裡支援寫入操作啦,所以我們是時候來總結下Unity3D中資源讀寫的常見方案了,那麼Unity3D中常見的資源讀寫方案由哪些呢?

1、Resources.Load:唯讀,當我們的資源不需要更新且對本機存放區無容量要求的時候可以採用這種方式
2、AssetBundle:唯讀,當我們的資源需要更新且對本機存放區有容量要求的時候可以採用這種方式
3、WWW:唯讀,WWW支援http協議和file協議,因此可以WWW來載入一個網路資源或者本地資源
4、PlayerPrefs:可讀可寫,Unity3D提供的一種的簡單的鍵-值型儲存結構,可以用來讀寫float、int和string三種簡單的資料類型,是一種較為鬆散的資料存放區方案
5、序列化和還原序列化:可讀可寫,可以使用Protobuf、序列化為Xml、二進位或者JSON等形式實現資源讀寫。
6、資料庫:可讀可寫,可以使用MySQL或者SQLite等資料庫對資料進行儲存實現資源讀寫。

好了,在瞭解了Unity3D中資源讀寫的常見方案後,我們接下來來討論下Unity3D中的路徑問題:
1、Application.dataPath:這個路徑是我們經常使用的一個路徑,可是我們真的瞭解這個路徑嗎?我看這裡要打個大大的問號,為什麼這麼說呢?因為這個路徑在不同的平台下是不一樣的,從官方API文檔中可以瞭解到這個值依賴於啟動並執行平台:
* Unity 編輯器:<工程檔案夾的路徑>/Assets
* Mac:<到播放器應用的路徑>/Contents
* IOS: <到播放器應用的路徑>/

4、編寫入口檔案
using UnityEngine;using System.Collections;using System.Collections.Generic;using UnityEngine.UI;using System.Xml.Serialization;public class Main : MonoBehaviour {    //關卡列表    private List<Level> m_levels;    void Start ()     {        //擷取關卡        m_levels = LevelSystem.LoadLevels ();        //動態產生關卡        foreach (Level l in m_levels)         {            GameObject prefab=(GameObject)Instantiate((Resources.Load("Level") as GameObject));            //資料繫結            DataBind(prefab,l);            //設定父物體            prefab.transform.SetParent(GameObject.Find("UIRoot/Background/LevelPanel").transform);            prefab.transform.localPosition=new Vector3(0,0,0);            prefab.transform.localScale=new Vector3(1,1,1);            //將關卡資訊傳給關卡            prefab.GetComponent<LevelEvent>().level=l;            prefab.name="Level";        }        //人為解鎖第二個關卡        //在實際遊戲中玩家需要滿足一定條件方可解鎖關卡        //此處僅作為示範        LevelSystem.SetLevels ("level1", true);    }    /// <summary>    /// 資料繫結    /// </summary>    void DataBind(GameObject go,Level level)    {        //為關卡綁定關卡名稱        go.transform.Find("LevelName").GetComponent<Text>().text=level.Name;        //為關卡綁定關卡圖片        Texture2D tex2D;        if(level.UnLock){            tex2D=Resources.Load("nolocked") as Texture2D;        }else{            tex2D=Resources.Load("locked") as Texture2D;        }        Sprite sprite=Sprite.Create(tex2D,new Rect(0,0,tex2D.width,tex2D.height),new Vector2(0.5F,0.5F));        go.transform.GetComponent<Image>().sprite=sprite;    }}

在這段指令碼中,我們首先載入了關卡資訊,然後將關卡資訊和介面元素實現綁定,從而實現一個簡單的關卡選擇介面,並人為地解鎖了第二個關卡。好吧,如果這是一個正式遊戲的配置關卡設定檔,相信大家都知道怎麼免費玩解鎖的關卡了吧,哈哈!當然,我不推薦大家這樣做,因為作為一個程式員,當你全身心地投入到一個項目中的時候,你就會明白完成一款軟體或者遊戲需要投入多少精力,所以大家盡量還是不要想破解或者盜版這些這些事情,畢竟作為開發人員可能他的出發點是想做出來一個讓大家都喜歡的產品,可是更現實的問題是開發人員一樣要生活,所以請善待他們吧。好了,言歸正傳,這裡的UI都是基於UGUI實現的,不要問我為什麼不用NGUI,因為我就是喜歡UGUI!我們知道我們需要為每個關卡的UI元素繫結一個響應的事件,因此我們需要為其編寫一個LevelEvent的指令碼:

using UnityEngine;using System.Collections;using UnityEngine.UI;using UnityEngine.EventSystems;public class LevelEvent : MonoBehaviour{    //當前關卡    public Level level;    public void OnClick()    {        if(level.UnLock){            //假設關卡的名稱即為對應情境的名稱            //Application.LoadLevel(level.Name);            Debug.Log ("當前選擇的關卡是:"+level.Name);        }else{            Debug.Log ("抱歉!當前關卡尚未解鎖!");        }    }}

記得在本文開始的時候,博主提到了一個假設,就是關卡的名稱和其對應的遊戲名稱一致的假設,相信到此處大家都知道為什麼了吧!為了讓每個關卡的UI元素知道自己對應於哪個關卡,我們設定了一個level變數,這個變數的值在載入關卡的時候已經完成了初始化,所以此時我們可以在這裡知道每個關卡的具體資訊,從而完成事件的響應。好了,今天的內容就是這樣了,我們來看看最終的效果吧!

可以注意到在第二次開啟遊戲後,第二個關卡已經解鎖了,說明我們在最開始設計的兩個目標都達到了,那麼內容就是這樣子啦,如果大家有什麼好的想法或者建議,歡迎在文章後面給我留言,謝謝大家!

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.