Unity

Unity

官方網站:點擊進入
官方粉絲團:點擊進入

《Unity》製作塔防遊戲

【Unity】 【2D】 【塔防】 【游戲】
作者:Dimitris-Ilias 時間:2015-10-08
 
  如果你已經迫不及待地想下載源碼了,可以到GitHub下載遊戲完整源碼。第二部分教程已經發佈在這裡。(翻譯版請點我
 
  除非你最近幾年生活在與世隔絕的山洞中,否則你一定玩過一款塔防遊戲,比如Plants vs. Zombies(植物大戰殭屍),Kingdom Rush(王國保衛戰),geoDefense, Jelly Defense(果凍防禦)或者其他塔防遊戲。你一定花了大量時間在建造防禦設施、殺死敵人、和挑戰更高等級關卡上面。
 
  因為塔防是最常見遊戲類型的一種,所以你可以在應用商店那裡找到。我決定試試自己的運氣,使用unity3D從零開發一款塔防遊戲,當前這篇教程就是開始。因為這篇教程比我之前的長一些,所以這個教程拆分為兩個部分。在這第一篇教程中,我們將大致了解以下這些知識:遊戲描述、關卡編輯器、單獨的XML文件創建和解析,為了提升性能將會在遊戲中使用物件池(object pool)。通過下面運行在Unity編輯器中的遊戲截屏來體驗下游戲吧。
 
《Unity》製作塔防遊戲
 
  實際上,遊戲場景和玩法都相當簡單。獾子攻擊玩家的兔子家園,玩家拋出可愛的小兔子通過射箭來保護自己的家園。為了能有更多的兔子守衛者,玩家需要收集胡蘿蔔幣。如果玩家殺死了本回合預定數目的所有獾子,玩家就贏了,如果預定數目的獾子進入了兔子家園,那麼玩家就輸了。獾子通過指定路徑進入兔子家園,而這條路上不能放置兔子守衛者。
 
下面讓我們再深入挖掘一下游戲機​​制和玩法:
- 兔子家園:初始生命是10,如果10個獾進入家園,遊戲結束。
 
- 路徑:獾按照這個路徑依次進入房子,指定路徑點可以指示出他們的方向
- 獾:我們的敵人。它有一個速度屬性和血條,當它被箭射中時血會減少。它沿著已經被放置在路徑種的路徑點前行(不可見遊戲物體)。
 
-兔子:我們的守衛。它以一定的頻率射箭。它總是尋找附近的敵人攻擊。如果找到一個敵人,它就會開始射擊。如果敵人死亡或離開它的攻擊範圍,它就會開始搜索其他敵人。創造兔子需要消耗固定數目的胡蘿蔔。
 
- 胡蘿蔔:從屏幕的頂部的隨機落下。玩家需要拍或者點擊胡蘿蔔才能增加金幣,來創造更多的兔子。
 
- 兔子生成器:(哦也,我可以取一個更好的名字!)。它是屏幕左下方的兔子。玩家需要拖出兔子以創造新的小兔子。玩家不能在紅色高亮區域放置新的兔子(例如在路徑上)。當創造一個新的兔子時,一筆固定數目金幣將從玩家的賬戶上扣除。
 
-關卡製作器:所有遊戲關卡都被存儲在一個標準格式的XML 文件中。 Unity開發者可以選擇使用Unity編輯器“Custom Editor”菜單“Export Level”功能存儲場景編輯數據到XML 文件。
 
我們還有幾個"角色",將在本教程中提到。
- Unity開發者:使用我們“Custom Editor”的編輯器來為遊戲創建新的關卡
- 用戶/遊戲玩家:最終用戶,玩我們的遊戲!
 
 
XML 文件結構
 
正如我們所提到的所有的遊戲級別都存儲在XML 文件中。當我們設計遊戲關卡時,首先我們需要確定哪些數據是可替換的。看看Level1.xml 文件的內容,文件位於Resources文件夾中(為了能在運行時期間使用)。

《Unity》製作塔防遊戲
《Unity》製作塔防遊戲
《Unity》製作塔防遊戲
《Unity》製作塔防遊戲
 
  從上面的圖片,你可以看到我們存儲了玩家的初始內存值,兩個變量(最小值和最大值)來控制胡蘿蔔產生時間頻率,兔子家園的位置(此後稱之為"塔"),PathPieces (路徑)的X 和Y 坐標位置(我們的敵人前行路徑),路徑點的X 和Y位置坐標(即我們的敵人將按照這個路徑到達兔子家園),最後是每一輪遊戲信息(敵人數量)。
 
  具體來說,PathPieces 包含關於關卡路徑精靈,路徑點是空遊戲對象的創建信息,用來將敵人"指引"到達塔。產生胡蘿蔔的時間頻率是隨機選擇的,它的取值範圍是我們設置的最小值和最大值之間。在我們深入研究代碼之前,提一下我們使用Visual Studio開發遊戲代碼(免費的社區版在這裡)。為了方便調試,可以看下VisualStudio Unity開發集成工具(在這裡)。
 
 
XML 解析
 
所有這些信息對應存在於LevelStuffFromXML類。
《Unity》製作塔防遊戲
 
  我們使用ReadXMLFile方法在運行時獲取XML 文件中的信息。我們可以使用序列化的類,但我們還是選擇了LINQ 到XML。因為1,rulezzz2,它的語法很好3,它可以更好地控制如何把對​​象/屬性轉換為XML,反之亦然。
 
因此,我們使用XDocument API來解析Resources文件夾中的Level1.xml 文件,創建的LevelStuffFromXML實例,在我們的遊戲中使用。代碼用法非常簡單也很容易理解,但是,儘管如此,我們使用自頂向下的方法,從XMLRoot元素開始遍歷直到最後,在遍歷過程中獲取指定屬性和xml元素。即使你是一個初學者,也很容易理解!
 
《Unity》製作塔防遊戲
 
  有人也許會問,為什麼保存在XML 文件而不是將每個關卡信息保存到各自關卡場?好吧,那樣也是可以的。然而,我們選擇使用這種方法,有以下幾個原因:
• XML 是一個可閱讀標準,可以無需打開Unity編輯器就可以修改
• 我們的遊戲可以通過下載服務器添加的XML文件增加新的關卡
• 用戶/遊戲玩家可以創建新的關卡(非Unity編輯器),並將其上傳到社區網站上,用戶可以評分和下載
• 當然,你可以通過教程學習XML的使用(包括此教程)!
 
編輯器窗口
 
也許,你可能會問,我們需要自己寫XML文件嗎?嗯,有人會那樣做!但是,我們想要給Unity開發人員提供一種便捷的創建XML文件的方式,所以我們創建一個輕量的Unity編輯器定制窗口。如果你打開Unity項目,你將看到一個新的“Custom editor”菜單,並有一個項標題為“Export level”功能。
《Unity》製作塔防遊戲
 
點擊“Export level”功能,您將看到以下窗口:
《Unity》製作塔防遊戲
 
通過單擊“Add new round”按鈕,可以創建一個有特定數目敵人的新回合,可以拖動下面的滑塊來修改這些值。試一試吧!經過幾次修改,結果如下:
《Unity》製作塔防遊戲
 
這些就是我們創建的新關卡回合。當然,你可以刪除一個回合,如果你覺得做的有不合理的地方。此外,你還可以修改初始金幣、 胡蘿蔔產生時間和導出文件名。如果你試著在一個空的場景導出關卡文件,會出現下面的錯誤消息。
 
《Unity》製作塔防遊戲
 
親愛的朋友,每個用戶輸入數據都是惡魔(需要驗證)!!!你應該把非正常數據從應用程序和遊戲中排除,以保護用戶正常輸入的數據。在這個空場景,並沒有塔的實例對象,所以導出腳本不能正確地創建一個格式良好的遊戲xml文件(語法正確),因為我們每個關卡都必定需要一個塔的實例化對象(邏輯上不正確)。我們下面來深入看看代碼。
 
 
代碼
 
首先,腳本位於“Editor”文件夾。這是Unity推薦使用的文件夾(通用命名規則公)用來包含夾編輯器相關的代碼。
 
《Unity》製作塔防遊戲
 
  對應的類不是MonoBehavior,和我們之前創建的不一樣。它是一個特殊類型,稱為EditorWindow,有一個靜態方法ShowWindow和一個相應的C# 屬性,通過MenuItem屬性創建菜單項。也有一些變量來幫助我們創建編輯器窗口。
 
《Unity》製作塔防遊戲
OnGUI方法可以定義GUI元素的外觀顯示。
 
  我們使用ScrollView (通過EditorGUILayout.BeginScrollView和EditorGUILayout.EndScrollView方法) 控件展示Unity開發人員創建的多個Rounds(回合)(因為可能會創建很多回合)。對於每一回合,我們顯示一個標籤(用來提示回合的序號),還有這個回合有多少敵人和一個刪除回合的按鈕。然後下面是一個IntSlider控件,通過拖動滑塊來指定敵人的數目,添加一個按鈕來增加所需數目敵人的新回合。
 
  再創建另外幾個IntSlider控件用來設置初始金幣和胡蘿蔔產生時間(最小值和最大值) ,這樣我們就完成了GUI的創建。最後,如果Unity開發者設置正常的話,就可以通過按鈕調用“Export”方法保存為新的關卡。
 
《Unity》製作塔防遊戲
 
  在Export方法的開始部分,我們創建新的XDocument對象,並添加Root元素(稱為"Elements")。然後開始添加路徑XElements對象(每個路徑元素都有相應的X和Y屬性)。我們通過WaypointsAreValid方法先驗證路徑點之後再添加“WayPoints”元素,每個路徑點元素和路徑元素一樣都有相應的X和Y屬性。
 
《Unity》製作塔防遊戲
 
  Export方法的第二部分,我們將添加用戶創建的所有回合和每個回合敵人的相應數量。我們還要添加塔、 初始金幣和胡蘿蔔產生時間。如果不能在場景中找到一個標籤為“塔”的遊戲對象,我們將顯示一條錯誤消息然後退出方法。此外,在保存之前,我們有一些驗證邏輯(如下圖所示),並向用戶顯示最終確認信息。如果Unity開發人員選擇保存,我們將文件保存到Assets文件夾。
 
《Unity》製作塔防遊戲
 
  當Unity開發人員將路徑點放在場景中時,它們的標籤必須標記為“WayPoint”,然後添加OrderedWaypointForEditor組件。這些組件都有一個Order字段,敵人將會按照這些路徑點的順序前進。順序最小的路徑點是入口點,最大的路徑點就是兔子家園的位置。 WayPointsAreValid方法檢查所有路徑點確定他們都有一個OrderedWaypointForEditor組件,並且所有點的順序字段都是不同的值。如果這兩個條件都滿足,則該方法返回true。否則通知用戶錯誤並返回false。
 
《Unity》製作塔防遊戲
 
  InputIsValid方法對各種變量進行無效輸入檢查,如果變量無效返回false。如果用戶忘記添加指定的遊戲對象,ShowErrorForNull方法將顯示一個錯誤對話框。
這就是我們的EditorWndow(編輯器窗口)!所以,也許你會問,怎樣製作一個新的關卡呢?
 
製作塔防遊戲的一個新的關卡
 
讓我們看看遊戲創建新的關卡的步驟!
1.創建一個新的空場景,將相機坐標設置為0,0,-10,投影方式為正交,裁切面取值範圍Near為0.3,Far為1000。 Tag(標籤)為"MainCamera"。
 
2.將塔的預置(位於Prefabs文件夾) 拖到場景中
 
3.拖動一些路徑到場景,製作路徑!使用快捷鍵Ctrl-D(複製遊戲對象),然後使用快捷鍵V頂點對齊(使遊戲對象彼此對齊)。現在場景中應該是這樣的:
《Unity》製作塔防遊戲
 
4.創建一些空的遊戲對象來表示路徑的點。為了結果更清晰,在路徑之外創建一個遊戲對象(下面截圖中最下面的),一個在路徑的第一部分,一個在轉彎處,一個在兔子家園。所有創建的遊戲對象增加OrderedWaypointForEditor腳本並分別修改其Order字段。不要忘記修改他們的標籤為“Waypoints”! ! !
《Unity》製作塔防遊戲
 
5.打開“CustomEditor”,然後選擇Export Level菜單項。
6.添加一些回合、 編輯你想要修改的項,Filename項Level2.xml作為導出文件名。
《Unity》製作塔防遊戲
 
7.單擊“Export”並選擇“Agree”,轉到Resources文件夾並右鍵單擊,選擇“Refresh”刷新。 Level2文件將會顯示。將它拖動到Resources文件夾中。
《Unity》製作塔防遊戲
 
8.轉到Ut​​ilities.cs文件(位於Scripts文件夾),找到ReadXMLFile方法並將"Level1"更改為"Level2"(是的,我應該支持通過參數加載文件的)。
 
9.轉到新的場景,刪除除了攝像機(Camera)之外的所有遊戲對象。是的,你相信你自己的選擇!
10.將RootGO預設拖到場景中。把它的坐標設置為(0,0,0),如果坐標不是零點的話。
 
11.準備好了嗎?按編輯器的“播放”按鈕,將顯示你製作的新關卡!恭喜你,你應該感到自己的強大力量!
《Unity》製作塔防遊戲
 
 
物件池(Project Pool)
 
什麼是物件池?
 
另一件事值得一提的事情是在遊戲中使用對像池來管理遊戲對象。如果你對這方面知識不太熟悉,我們鼓勵你可以看看這個Unity視頻教程。簡而言之,物件池有助於提升性能。換句話說,很多時候你想要在遊戲中添加一些生命週期很短的遊戲物件。這將會產生以下流程:
• 創建物件(無論是通過“new GameObject”或“Ins​​tantiate”)
• 物件在其生命週期存活
• 當不再需要這個物件時調用Destroy()銷毀物件
 
然而,實例化物件有性能影響,Destroy不會立即從內存刪除該對象(這取決於垃圾回收器的生命週期)。而與之相反,物件池的工作流程如下:• 計算出需要多少個對象(例如我們懷疑遊戲在同一時間“存在”的箭的數目不會超過10支)。
• 實例化這些物件並將它們設置為非活動狀態
• 每次遊戲需要一個物件,從列表中取出第一個非活動狀態物件
• 對象設置為活動狀態,有自己的生命週期
• 當不再需要該物件時,它被設置為非活動狀態
這是對物件池的一個非常基本的解釋,現在來看看源代碼!

 
ObjectPooler 類的源代碼
 
《Unity》製作塔防遊戲
 
  開始部分是ObjectPooler類聲明的一些變量。父物件Transform和PooledObject物件是可選的,只有在你想為新創建的物件或通過預設實例化物件設置父物件時才會用到父物件的Transform。還有一個PoolLength變量和包含任何我們新創建遊戲物件所需組件的數組。 Initialize是個過載函數(指可以編寫相同名稱但不同形式的函數),這樣你可以為物件池中的對象添加任何組件類型。 PooledObject列表包含所有創建的遊戲物體。
 
《Unity》製作塔防遊戲
  CreateObjectInPool方法創建一個新的遊戲物件(PooledObject預設實例化或調用物件的構造函數),並通過參數判斷是否為物件添加組件(通過AddComponent(Type type) 方法)和設置父物件。最重要的是,設置對象狀態為非活動,並將其添加到list(列表)中。
 
《Unity》製作塔防遊戲
 
  GetPooledObject方法是供外部腳本調用。它試圖從PooledObjects列表中找到一個處於非活動狀態的遊戲物件(即準備使用)。如果找到,返回這個物件。否則將列表的容量增加一倍,創建遊戲物件並返回第一個創建的物件
 
ObjectPooler類的分析到此為止,我們現在看一看調用它的類。
 
ObjectPoolerManager 類
 
這個類的物件會引用ObjectPoolers。出於本教程的目的,我們將使用ObjectPooler來管理小兔子射出的箭和遊戲音頻物件
 
《Unity》製作塔防遊戲
 
  這個類是個單例模式,因為我們希望任何給定時間內都只有這一個實例。物件池管理器有兩個引用(箭和音頻),當Start()函數被調用時,我們初始化物件池管理器。箭是一個預設,我們在Unity編輯器中設置​​了它的字段。 AudioPooler初始化時需要傳入AudioSource類型(用於創建AudioSource組件)而AudioPooler可以獲取箭預設物件

※製作塔防遊戲 part 2
 
 
x