Unity3D遊戲開發從零單排(四) - 制作一個iOS遊戲
提要
? ? ? ?此篇是一個國外教程的翻譯。盡管有點老,可是適合新手入門。自己去寫代碼。debug,布置場景。能夠收獲到非常多。
遊戲邦上已經有前面兩部分的譯文。這裏翻譯的是遊戲的最後一個部分。
歡迎回來
在第一篇中,我們學會了怎麽在Unity中搭建遊戲的場景,而且設置模型的物理屬性。
在第二篇中,我們學會了怎麽在unity中使用腳本,而且創建了大部分的遊戲邏輯,包括投球和得分!
在這最後一節中。我們將會為用戶創建一個菜單系統,而且和GameController進行交互,我們開始吧。
在設備上測試
到眼下為止。我們僅僅在unity內建的模擬器中進行了測試。
如今你的項目能夠正常執行了,是時候讓他在真實的設備裏跑跑測試了。
點擊菜單條 File->Build Setting.你懊悔看到以下的對話框:
首先。確定你選擇對了平臺(iOS旁邊應該有一個unity的標誌,假設沒有,選擇iOS,然後點Switch Platform)
選擇Player Settings,在inspector面板中就能夠對遊戲進行編譯設置。
(譯註:Android平臺的執行能夠參考 - Unity3D遊戲開發從零單排(一) - 真機執行)
這裏有一大堆能夠設置的東西。可是如今你真正須要關心的是保證遊戲的屏幕方向是正確的。
在Resoluion and Presentation面板中的devices orientation的下拉選框中選擇Landscape-Left。其它保持默認。接下來是Other Settings。
在這個部分你須要輸入你的developer Bundle Identifier(和在XCode裏面一樣),余下的部分保持默認。
是時候動真格的了
當你設置好編譯的依稀選項之後,回到Build Setting對話框,點Build。
Unity將會彈出一個選擇項目位置的對話框。當你選擇好存儲位置後,Unity將會啟動XCode執行項目。而且準備好了編譯和執行項目了。
註意:不要僅僅在模擬器裏跑你的遊戲,由於Unity僅僅提供了iOS的設備。在你的移動設備上去跑遊戲。點這裏查看跟多相關的內容。
執行成功之後。接下來就是為你的遊戲制作一個簡單的菜單了。
保存分數
首先下載這個資源,裏面包括了一些工具類來處理數據的存儲和讀取。
解壓壓縮包,將裏面的.cs文件拖到項目的Scripts目錄中。
數據存儲的這些類的實現並不在教程範圍內,以下我們寫一個小的測試來學習怎樣使用它。
在場景中創建一個空的GameObject,將LeadeboardController腳本拖上去。
再在這個GameObject上加入一個腳本組件,命名為LeaderboardControllerTest,測試的內容是存儲一些分數,然後再取回來。
你須要在測試類中索引LeaderboardController。加入一個LeaderboarfController的公有數據成員,代碼例如以下:
using UnityEngine;
using System.Collections.Generic;
using System.Collections;
public class LeaderboardControllerTest : MonoBehaviour {
public LeaderboardController leaderboard;
void Start () {
}
void Update () {
}
}
註:註意 using System.Collections.Generic;放在類的最上面。這是引入相關的包,在以下的包中你就能夠使用Generric中的特有的collections了。關於Generic包的文檔能夠參見這裏。
使用LeaderboardController中的AddPlayerScore方法來測試:
void Start () {
leaderboard.AddPlayersScore( 100 );
leaderboard.AddPlayersScore( 200 );
}
這樣就能夠將分數保存在閃存中。即使關掉應用。數據還是能夠取回來。為了取回數據,須要註冊LeaderboardControllers的OnScoresLoaded方法。同一時候還要實現相應的handler函數,代碼例如以下.順便提一下 - 異步調用的方式能夠同意你去擴展LeaderboardController,假設你想的話,讓它能夠處理一個遠程的leaderboard。
void Start () {
leaderboard.OnScoresLoaded += Handle_OnScoresLoaded;
leaderboard.AddPlayersScore( 100 );
leaderboard.AddPlayersScore( 200 );
leaderboard.FetchScores();
}
public void Handle_OnScoresLoaded( List<ScoreData> scores ){
foreach( ScoreData score in scores ){
Debug.Log ( score.points );
}
}
參數List傳回的是ScoreData對象的一個list。ScoreData是一個簡單數據對象,將分數進行了了一個簡單的封裝。
在Handle_OnScoresLoaded方法中將會遍歷每一個每一個分數對象,然後將分數打印輸出。這就是我們想要的。就是這樣。
接下來要執行看看了:
創建一個新的GameObject,命名為LeaderboardControllder,將LeaderboardController.cs加入上去。
選定LeaderboardContrillerTest對象,將LeaderboardControllder賦給腳本中相應的公有成員。
點執行,看分數是不是在終端現實了!
創建一個簡單的菜單
是時候做點讓人興奮的新東西了 —— 你將會學到怎樣創建一個遊戲菜單!
以下是我們將要做成的樣子:
在Unity3D中實現用戶界面有3種方式,每一種都各有優缺點,以下就細致討論一下。
1)GUI
Unity提供了一套自己定義的用戶界面,通過MonoBehaviour的回調函數OnGUI來處理事件,Unity支持用皮膚來改變這些界面的外觀。
對於那些不是非常在意性能的設備,這是一個非常理想的解決方式。它提供了非常豐富的預設控制。可是考慮到性能問題,自帶的GUI不應該在遊戲進行時出現。
2)GUITexture和GUIText
Unity提供了兩個組件,GUITexture和GUIText,這兩個組件能夠讓你能夠在屏幕上呈現2D的圖像和文字。
你能夠非常方便地通過擴展這兩個組件來創建自己的用戶界面,相比於GUI 組件,性能上優秀非常多。
3)3D Planes/Texture Altas
假設你要創建一個在遊戲頂層現實的菜單(比方HUD),那麽這個就是最好的選擇。即使這個最麻煩!:] 可是一旦你創建好了頂層顯示的相關類。你就非常easy將它們適用在其它的新的項目中。
3D planes即用一套3D 平面來實現HUD,這些3D平面關聯著同一個紋理集合,紋理集合就是將一些細小的紋理拼接在一起。組成一張大的紋理圖片(譯註:分辨率通常為2
的n次方,方面一次性載入到內存)。這個和Cocos2D中的sprite sheet的概念相似!:]
由於各個HUD共享的是同一個材質(指向同一個紋理),通常僅僅須要一個調用就能夠將HUD全部渲染出來。
在大部分情況下,你須要為HUD創建一個專用的攝像機。讓它們看起來是正交投影的,而不是透視投影的(指的是攝像機的種類)。
在這個遊戲中,我們的選擇是第一種。Unity自帶GUI。
除了上面我們提到的它的一些缺點,它最大的優點就是有預置的控制,能夠讓這篇教程更簡單一些。
以下我們首先為主菜單創建皮膚。然後你完畢渲染主菜單的代碼,最後將它鏈接到GameController上。
聽起來是不是非常棒。那即可動吧,少年!:]
皮膚
Unity提供了一種叫Skin的東西來裝飾GUI。這個東西能夠簡單的類比成Html的CSS。
我已經創建好了兩個Skin(在第一部分的教程中已經導入到工程裏面了),一個是480*320的分辨率,還有一個是960*640的用於是視網膜屏幕的。以下的圖片是480*320的Skin的屬性。
Skin的屬性文件有非常多的選項,讓你能夠為你的項目創建獨一無二的屬性。在這個項目中,你僅僅須要關心字體。
接下來打開GameMenuSmall,將scoreboard字體拖拽到Font屬性而且將字體設置到16. 打開GameMenuNormal,將scoreboard字體拖拽到Font屬性而且將字體設置到32.下一步就是制作真真的主菜單了!
主菜單
編譯執行
像之前做的一樣,File->Build Settings.點擊buildbutton。開始測試你的第一個遊戲吧!
編譯並執行XCode項目。你就能夠在你的設備上看到一個美麗的而且能夠work的菜單了。
主菜單
這個部分主要是GameMenuController的代碼,負責渲染主菜單而且處理用戶的輸入。以下是代碼中比較重要的片段,終於都會和遊戲連接起來。
創建一個名為GameMenuController的腳本,創建以下的一些變量。
using UnityEngine;
using System.Collections;
[RequireComponent (typeof (LeaderboardController))]
public class GameMenuController : MonoBehaviour {
public Texture2D backgroundTex;
public Texture2D playButtonTex;
public Texture2D resumeButtonTex;
public Texture2D restartButtonTex;
public Texture2D titleTex;
public Texture2D leaderboardBgTex;
public Texture2D loginCopyTex;
public Texture2D fbButtonTex;
public Texture2D instructionsTex;
public GUISkin gameMenuGUISkinForSmall;
public GUISkin gameMenuGUISkinForNormal;
public float fadeSpeed = 1.0f;
private float _globalTintAlpha = 0.0f;
private GameController _gameController;
private LeaderboardController _leaderboardController;
private List<ScoreData> _scores = null;
public const float kDesignWidth = 960f;
public const float kDesignHeight = 640f;
private float _scale = 1.0f;
private Vector2 _scaleOffset = Vector2.one;
private bool _showInstructions = false;
private int _gamesPlayedThisSession = 0;
}
首先。裏面有一系列的公有成員。能夠在unity中通過拖拽對象來設置。這些變量是渲染主菜單的各個元素。接下來的兩個變量變量來索引之前創建的那兩個Skin。
再以下的變量時用來淡入淡出主菜單。我們也須要用私有變量來索引GameController和LeaderboardController來獲得分數對象。
接下來是一系列用於處理屏幕分辨率的變量,比方iPhone 3GS(480*420)和iPhone4(960*360)。最後是用來管理GameMenuController的組件狀態的變量。
加入Awake()和Start()方法,例如以下:
void Awake(){
_gameController = GetComponent<GameController>();
_leaderboardController = GetComponent<LeaderboardController>();
}
void Start(){
_scaleOffset.x = Screen.width / kDesignWidth;
_scaleOffset.y = Screen.height / kDesignHeight;
_scale = Mathf.Max( _scaleOffset.x, _scaleOffset.y );
_leaderboardController.OnScoresLoaded += HandleLeaderboardControllerOnScoresLoaded;
_leaderboardController.FetchScores();
}
在Start方法中。從LeaderboardController中獲得score對象。同一時候,計算出和屏幕分辨率相關的一些比例,讓Gui能夠自適應,
代碼中scale offsets用來保證GUI元素能夠正常地縮放。比方。假設一個菜單是960*640的,當前設備的分辨率是480*320。然後你須要做的就是將菜單縮小50%,那麽scaleOffset就是0.5. 這麽做在簡單的多分辨率設備的適配中會非常不錯,你不須要反復創建資源。
一旦scores載入完畢,在本地存儲起來,將會用來渲染GUI.
public void HandleLeaderboardControllerOnScoresLoaded( List<ScoreData> scores ){
_scores = scores;
}
測試測試!
讓我們略微歇息一下,測試測試眼下為止我們所做的東西。
在GameMenuController中加入以下的code:
void OnGUI () {
GUI.DrawTexture( new Rect( 0, 0, Screen.width, Screen.height ), backgroundTex );
if (GUI.Button( new Rect ( 77 * _scaleOffset.x, 345 * _scaleOffset.y, 130 * _scale, 130 * _scale ), resumeButtonTex, GUIStyle.none) ){
Debug.Log( "Click" );
}
}
上面的代碼片段主要是OnGUI函數的寫法,這個函數的行為相似於Update(),而且提供了對GUI Component的讀取,GUI Component提供了一系列的靜態方法來實現標準的用戶控制。點我去官方站點學習很多其它的OnGui和GUI類。
第一句話是用一張紋理繪制整個屏幕。
GUI.DrawTexture( new Rect( 0, 0, Screen.width, Screen.height ), backgroundTex );
接下來用的推斷語句中,GUI.Button方法在指定的一個地方渲染一個Button(用之前我們計算的縮放比例來定位)。這種方法的返回值和用戶是否點擊這個Button有關。點了就是true,沒點是false.
if (GUI.Button( new Rect ( 77 * _scaleOffset.x, 345 * _scaleOffset.y, 130 * _scale, 130 * _scale ), resumeButtonTex, GUIStyle.none) ){
Debug.Log( "Click" );
}
上面的代碼中,假設用戶點擊了button,console中就會打印Click。
為了測試,將GameMenuController腳本附加到GameController上。將相應的公有變量拖拽上去。例如以下圖:
如今測試吧,點擊執行,你會開發出現了一個button。點擊它你就能夠在終端看到打印的信息。
還不賴吧?完畢菜單的第一步就搞定了!:]
使用skins
如今你確定了你的方向找對了。接下來依據屏幕的尺寸來設置skin。
用以下的代碼來替換OnGUI函數:
if( _scale < 1 ){
GUI.skin = gameMenuGUISkinForSmall;
} else{
GUI.skin = gameMenuGUISkinForNormal;
}
skins能夠確保你使用正確的字體大小(依據屏幕尺寸);使用哪一個skin是依據前面計算的_scale值。
假設_scale小於1.0就使用small skin,不是的話就是使用normal skin。
顯示和隱藏
相比於粗魯地彈出菜單,用淡入淡出來處理是更好的選擇。為了實現淡入淡出,我們須要處理GUI的static 變量 content Color (這回影響到GUI類裏繪制的全部內容)。
為了處理淡入。你應該慢慢地將_globalTintAlpha的值從0添加到1.然後將它賦給GUI.contenColor變量。將西面的代碼加入OnGUI函數:
_globalTintAlpha = Mathf.Min( 1.0f, Mathf.Lerp( _globalTintAlpha, 1.0f, Time.deltaTime * fadeSpeed ) );
Color c = GUI.contentColor;
c.a = _globalTintAlpha;
GUI.contentColor = c;
你須要一些方法來顯示和隱藏菜單,創建以下兩個方法,Show和Hide:
public void Show(){
// ignore if you are already enabled
if( this.enabled ){
return;
}
_globalTintAlpha = 0.0f;
_leaderboardController.FetchScores();
this.enabled = true;
}
public void Hide(){
this.enabled = false;
}
代碼沒啥好說的,
菜單顯示的內容依據遊戲的狀態不同,內容也不同,比方當遊戲結束的時候菜單的內容就和暫停時顯示的菜單不一樣。
在OnGUI函數中加入以下的代碼:
GUI.DrawTexture( new Rect( 0, 0, Screen.width, Screen.height ), backgroundTex );
if( _gameController.State == GameController.GameStateEnum.Paused ){
if (GUI.Button( new Rect ( 77 * _scaleOffset.x, 345 * _scaleOffset.y, 130 * _scale, 130 * _scale ), resumeButtonTex, GUIStyle.none) ){
_gameController.ResumeGame();
}
if (GUI.Button( new Rect ( 229 * _scaleOffset.x, 357 * _scaleOffset.y, 100 * _scale, 100 * _scale ), restartButtonTex, GUIStyle.none) ){
_gameController.StartNewGame();
}
} else{
if (GUI.Button( new Rect ( 77 * _scaleOffset.x, 345 * _scaleOffset.y, 130 * _scale, 130 * _scale ), playButtonTex, GUIStyle.none) )
{
if( _showInstructions || _gamesPlayedThisSession > 0 ){
_showInstructions = false;
_gamesPlayedThisSession++;
_gameController.StartNewGame();
} else{
_showInstructions = true;
}
}
}
你應該非常熟悉這些代碼,就是依據遊戲的狀態渲染相應的紋理和button。
暫停狀態下的兩個button分別同意玩家返回和又一次開始:
if (GUI.Button( new Rect ( 77 * _scaleOffset.x, 345 * _scaleOffset.y, 130 * _scale, 130 * _scale ), resumeButtonTex, GUIStyle.none) ){
_gameController.ResumeGame();
}
if (GUI.Button( new Rect ( 229 * _scaleOffset.x, 357 * _scaleOffset.y, 100 * _scale, 100 * _scale ), restartButtonTex, GUIStyle.none) ){
_gameController.StartNewGame();
}
註:想知道我是怎麽知道那些精確的尺寸的?答案是使用GIMP!
還有一個狀態是GameOver,這個時候你須要渲染開始button。
註:你可能註意到了兩個變量 _showInstructions 和 _gamesPlayerdThisSession 。_gamesPlayerThisSession是用來記錄當前你玩了多少場遊戲的,假設是一次,那麽 _showInstructions 就為真。那麽我們就能夠在玩家開始玩遊戲之前給出一些遊戲提示。
(譯註:這兩個變量都作為GameMenuController的私有成員)
是時候測試了
在完畢GameMenuController之前。確保如今的每一個功能都如預期那樣工作。一切都設置好了之後。你就能夠看到和以下相似的畫面了:
完畢GameMenuController
最後要完畢的是標題,提示還有分數。
繪制標題還是提示是依據上面提到的?_showInstructions 標誌位。將以下的代碼加入在OnGUI方法的最以下:
if( _showInstructions ){
GUI.DrawTexture( new Rect( 67 * _scaleOffset.x, 80 * _scaleOffset.y, 510 * _scale, 309 * _scale ), instructionsTex );
} else{
GUI.DrawTexture( new Rect( 67 * _scaleOffset.x, 188 * _scaleOffset.y, 447 * _scale, 113 * _scale ), titleTex );
}
註意,最後的代碼是處理分數板的,OnGUI提供了groups。你能夠通過group將東西放在一起,至於某個層(豎著的或者橫著的)。以下的代碼是繪制leaderboard和一些
簡單的button,用於關聯facebook和Twitter,然後將全部分數一個個加起來。將以下的代碼加入在OnGUI方法的最以下:
GUI.BeginGroup( new Rect( Screen.width - (214 + 10) * _scale, (Screen.height - (603 * _scale)) / 2, 215 * _scale, 603 * _scale ) );
GUI.DrawTexture( new Rect( 0, 0, 215 * _scale, 603 * _scale ), leaderboardBgTex );
Rect leaderboardTable = new Rect( 17 * _scaleOffset.x, 50 * _scaleOffset.y, 180 * _scale, 534 * _scale );
if( _leaderboardController.IsFacebookAvailable && !_leaderboardController.IsLoggedIn ){
leaderboardTable = new Rect( 17 * _scaleOffset.x, 50 * _scaleOffset.y, 180 * _scale, 410 * _scale );
GUI.DrawTexture( new Rect( 29* _scaleOffset.x, 477* _scaleOffset.y, 156 * _scale, 42 * _scale ), loginCopyTex );
if (GUI.Button( new Rect ( 41 * _scaleOffset.x, 529 * _scaleOffset.y, 135 * _scale, 50 * _scale ), fbButtonTex, GUIStyle.none) )
{
_leaderboardController.LoginToFacebook();
}
}
GUI.BeginGroup( leaderboardTable );
if( _scores != null ){
for( int i=0; i<_scores.Count; i++ ){
Rect nameRect = new Rect( 5 * _scaleOffset.x, (20 * _scaleOffset.y) + i * 35 * _scale, 109 * _scale, 35 * _scale );
Rect scoreRect = new Rect( 139 * _scaleOffset.x, (20 * _scaleOffset.y) + i * 35 * _scale, 52 * _scale, 35 * _scale );
GUI.Label( nameRect, _scores[i].name );
GUI.Label( scoreRect, _scores[i].points.ToString() );
}
}
GUI.EndGroup();
GUI.EndGroup();
}
這就是GameMenuController了,最後還要將GameMenuContrller關聯到GameController類上面。將GameController腳本打開,然後實現關聯。
聲明幾個變量:
private GameMenuController _menuController;
private LeaderboardController _leaderboardController;
public Alerter alerter;
在Awake函數中初始化一下:
void Awake() {
_instance = this;
_menuController = GetComponent<GameMenuController>();
_leaderboardController = GetComponent<LeaderboardController>();
}
最明顯的變化時GameController中State的處理,用以下的代碼替代UpdateStatePlay的部分,後面我們會具體說代碼。
public GameStateEnum State{
get{
return _state;
}
set{
_state = value;
// MENU
if( _state == GameStateEnum.Menu ){
player.State = Player.PlayerStateEnum.BouncingBall;
_menuController.Show();
}
// PAUSED
else if( _state == GameStateEnum.Paused ){
Time.timeScale = 0.0f;
_menuController.Show();
}
// PLAY
else if( _state == GameStateEnum.Play ){
Time.timeScale = 1.0f;
_menuController.Hide();
// notify user
alerter.Show( "GAME ON", 0.2f, 2.0f );
}
// GAME OVER
else if( _state == GameStateEnum.GameOver ){
// add score
if( _gamePoints > 0 ){
_leaderboardController.AddPlayersScore( _gamePoints );
}
// notify user
alerter.Show( "GAME OVER", 0.2f, 2.0f );
}
}
}
代碼的意思就如它寫的那樣,當State為Menu或者Pause,你僅僅要用GameMenuController的show方法來讓自己顯示就能夠了,當state設為Play的時候,用hide方法來隱藏。終於state被設為GameOver的時候。將玩家的分數加入到leaderboard中(就如demo中你寫的代碼那樣)。
最後,註意這段代碼有依賴於一個Alerter對象。
所以為了讓它跑起來。創建一個空對象。將Alert腳本加入上去。然後將Alerter拖拽到GameController的相應屬性上。
編譯執行
就像之前的一樣,File->Build Setting打開編譯對話框。點擊編譯button來測試終於的遊戲!
編譯並執行XCode項目,你將會在你的設備上看到一個美膩的菜單!
優化
優化方面的內容能夠寫成一本書了!即使你認為遊戲的表現還能接受。你有考慮過那一大堆ipod touch和iPhone 3G的感受麽?你已經花了非常大的力氣去完畢一個遊戲,你應該不願意那些持有老設備的玩家認為你的遊戲非常卡吧。
以下的一些條款在開發的時候最好牢記在心:
最小化調用繪制函數 —— 盡可能少地調用繪制函數,你能夠共享紋理和材質。避免使用透明的shader - 使用mobile shader來替代。限制場景的光源數量。使用貼圖組合來實現HUD.
註意有些場景的復雜度 —— 使用優化的模型,意味著模型具有更少的圖元。
你通常能夠將模型的細節烘培到紋理之中,而不是使用高精度的模型,烘培對於光照相同適用。記住玩家玩的遊戲是在非常小的一個屏幕上,非常多細節都會被忽略。
適用假的陰影 —— 動態陰影在iOS中並不能適用,可是能夠使用projectors來做成一個假的陰影。唯一可能引起的問題就是projector會調用繪制函數。所以假設可能的話,盡可能用一個帶陰影紋理的平面加上一個特粒子Shader來模擬。
警惕在Update/FixexUpdate方法中的不論什麽代碼 —— 理想情況下,Update和FixedUpdate函數須要每秒跑30到60次,所以在調用這兩個函數之前,預處理好不論什麽能夠做的事情。
同一時候也要註意你加在當中的邏輯,特別是和物理相關的!
關閉全部不使用的東西 —— 假設一個腳本不須要執行,就關掉它,盡管它看起來沒那麽重要 —— app中全部的一切都會消耗CPU!
盡可能使用最簡單的組件 —— 假設你僅僅須要一個組件中非常小的一部分功能。其它大部分都用不著。那麽你能夠自己實現你最須要的那部分功能而不是直接拿來用。比方CharacterController就是一個非常誘人的組件,所以最好用Rigidbody來實現你自己的解決方式。
整個開發過程都使用真機來測試 —— 當執行遊戲的時候。打開console的debug log窗體,那樣就能夠看你的app消耗了多少cpu。
你須要這樣做:在XCode中找到iPhone_Profiler.h文件。而且將ENABLE_INTERNAL_PROFILER 設為1.這樣就能夠更加具體地看到你的app的執行情況。
若果你有Unity3D Advance版本號,裏面有個profiler能夠查看腳本中每一個方法消耗的時間。profiler的信息就像以下這樣:
幀率能夠表示遊戲執行的速度,默認的速度哦要麽是30,要麽60.遊戲的平均幀率應該接近這兩個值。
draw-call表示當前渲染調用的次數 - 就像前面提到的,通過共享紋理和材質。將這個數字保持得越低越好。
(譯註:一個 Draw Call,等於呼叫一次 DrawIndexedPrimitive (DX) or glDrawElements (OGL),等於一個 Batch。盡可能地降低Drawcall的數量。IOS設備上建議不超過100。降低的方法主要有例如以下幾種:Frustum Culling,Occlusion Culling,Texture Packing。
Frustum Culling是Unity內建的,我們須要做的就是尋求一個合適的遠裁剪平面;Occlusion Culling,遮擋剔除,Unity內嵌了Umbra,一個非常好OC庫。但Occlusion Culling也並非放之四海而皆準的。有時候進行OC反而比不進行還要慢,建議在OC之前先確定自己的場景是否適合利用OC來優化;Texture Packing,或者叫Texture Atlasing,是將同種shader的紋理進行拼合。依據Unity的static batching的特性來降低draw call。建議使用,但也有弊端,那就是一定要將場景中距離相近的實體紋理進行拼合。否則,拼合後非常可能會添加每幀渲染所需的紋理大小。加大內存帶寬的負擔。這也就是為什麼會出現“DrawCall降了,渲染速度也變慢了”的原因)
verts表示當前須要渲染多少頂點。
player-detail能夠告訴我們遊戲引擎的每一個部分消耗了多少時間。
你還能夠做非常多
你能夠下載完整的項目。然後再unity中打開。
(譯註:有點坑爹。非常多bug)
到如今為止。你已經做得非常好了。但我們的旅程遠不會到此為止!
:] 保持這股勢頭。你將會成為一個unity高手,當然,這也須要很多其它的各方面的技能。
以下是一些擴展遊戲的建議:
●?加入音效。聲音是交互內容的非常重要的一個內容。所以花些時間去找些音樂和音效加到遊戲中去吧。
● 關聯Facebook。
● 添加多玩家模式,讓玩家們能夠同一個設備上競技,輪流投球。
● 添加角色讓玩家選擇。
● 支持多種手勢,不同的手勢代表不同的投籃方式;
● 加入帶獎勵的籃球。這種籃球有不一樣的物理屬性,比方不同的重量;
● 加入新的關卡。每一個關卡有新的挑戰。
這些足夠讓你忙一陣了!
希望你喜歡這個系列的教程。而且能夠學習一些unity的僅僅是。我希望看到你們將來制作出屬於自己的unity app!
資源下載
Project part3
Data Control Resources
參考
Intermediate Unity 3D for iOS: Part 3/3 -?http://www.raywenderlich.com/20420/beginning-unity-3d-for-ios-part-3
其它
原工程的代碼導入到unity3d4.3無法正常執行。個人又一次編寫了一個android和pc上都能執行的完整版本號,點我下載。
Unity3D遊戲開發從零單排(四) - 制作一個iOS遊戲