提要
此篇是一個國外教程的翻譯,盡管有點老,可是適合新手入門。
自己去寫代碼。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!
資源下載
參考
Intermediate Unity 3D for iOS: Part 3/3 - http://www.raywenderlich.com/20420/beginning-unity-3d-for-ios-part-3
其它
原工程的代碼導入到unity3d4.3無法正常執行,個人又一次編寫了一個android和pc上都能執行的完整版本號,點我下載。