譯者注:
目前移動設備的跨平台游戲開發引擎基本都是采用Cocos2d-x或者Unity。一般而言2d用cocos2d-x 3d用unity,但是對於Windows Phone開發者,
cocos2d-x for wp8是微軟維護的,版本升級十分緩慢,到現在還是 V2.0 preview,我們不可能拿一個不太穩定的版本去開發游戲。與之相反,Unity4.2發布之后,
支持WP8和Windows8,當然也包括其他平台,開發調試都是十分便捷,因此使用Unity來開發目標用戶在WP上的游戲,是個很好的選擇。
這是一篇譯文,原文很長,於是我把它分成了兩部分,而且加入了自己的一些修改和理解,點擊一下鏈接觀看第一部分效果。(需要安裝最新的unity插件)
http://jeekun.sinaapp.com/share/flash/flash.html
原文:http://catlikecoding.com/unity/tutorials/runner/
介紹
在這篇教程里,我們將學習如何去制作一款簡單的無盡跑酷游戲,在這里你將學到:
- 生成一個分層的背景
- 重用對象
- 使用物理引擎
- 通過檢測輸入來控制玩家跳躍
- 實現能量增加
- 一個簡單的事件管理器
- 按需控制物體的開關
- 制作一個簡單的GUI
游戲設計
在開始之前,我們應該先考慮一下在游戲里面加入什么。我們要做的是一個2D側卷軸游戲,但是還是太寬泛了,讓我們來縮小一下范圍。
游戲的玩法:我們將控制一個人物不斷的向屏幕右側奔跑,從一個平台跳到另外一個平台,要跑的盡可能的遠。這些平台有不同的特性,有的會讓你加速,
有的會讓你減速。我們還包含單一的能量球,可以讓你在空中跳躍。
游戲圖形:我們將使用純Cube和標准的粒子系統來制作(程序員的悲哀。。。) 玩家,平台,包括背景,統統都是Cube,而粒子系統將被用於制作運動軌跡,
很多漂浮物將會給人更好的速度和深度的感覺。
另外,游戲不包括特效音和背景音。
建立場景
打開Unity,創建一個新的工程,不要導入任何的包。
我們是要做的2D視角的游戲,但是還想有一點3D的效果,Orthographic攝像機是不能用於3D游戲的,因此我們得采用默認的Perspective類型。這樣的話,將物體放到離鏡頭不同
的距離,我們就能得到一個分層的滾動背景。
就讓我們假定,前景是在距離0,第一個背景在距離50,第二個背景在距離100。讓我們分別放三個Cube在這三個深度,並且將他們作為創建場景的引導者。我自己已經試過了一些角度和顏色組合,
覺得還可以,當然你也可以自己去實驗一些新的數值。
下面正式開始:
首先添加一個平行光(GameObject->Create Other->Directional Light),設置旋轉為(20,330,0)
然后設置攝像機的參數,顏色為(115,140,220)
其他數值見圖:
創建3個Cube, Position 分別是(0,0,0) (0,0,50) (0,0,100),名字對應的是 Runner, Skyline Close, Skyline Far Away,后兩個Skyline Cube 不需要
碰撞器(Box Collider),直接在屬性面板里,右鍵移除組件即可。 Runner是這次游戲的豬覺,在這里我稱之為 “奔跑者”,后面同之。
給3個Cube分別創建材質(Project 視圖,Create-> Material),命名為Runner Mat, Skyline Close Mat, Skyline Far Away Mat. 然后分別拖放到對應的Cube上。
我使用了默認的 diffuse Shader , 顏色分別設為, White, (100, 120, 220), (110, 140, 220), 設置完成后的值如下:
為了使項目的組織架構更好,我們在Project視圖里增加2個文件夾,Runner, Skyline, 然后把材質放到對應的文件夾里。
運行一下,可以看到3個Cube都出現在了視野里面。
開始跑步吧
到目前為止,我們做的還沒什么奇特的,場景里面什么也沒有發生,但是接下來就是見證奇跡的時刻了。
讓我們來讓 “奔跑者” 向右邊移動來模擬一下這個游戲吧。
在Runner文件夾里創建一個C#腳本,Runner.cs
using UnityEngine; using System.Collections; public class Runner : MonoBehaviour { // Use this for initialization void Start () { } // Update is called once per frame void Update () { transform.Translate(5f * Time.deltaTime, 0f, 0f); } }
把腳本拖到層次視圖的Runner 上面,運行,怎么樣,看到主角開始跑了吧。但是有個問題,攝像頭沒有跟隨它在運動,沒一會就跑出我們的視野了,
這樣的游戲根本沒法玩啊,也不是我們想要的。解決的方法很簡單,把攝像機對象拖到Runner里,讓他成為Runner的Child.
現在再運行的話就沒有問題了,而且我們可以發現,遠處的Cube要比進出的Cube移動的好像快一些,當然這就是視差的問題了。
生成天際線
現在我們已經有了基本的運動了,接下來讓我們生成一行的Cube來實現無止境的天際線。首先有件事情我們必須意識到,名為無盡的,其實只需要一部分存在地圖中就可以了,
因為隨着鏡頭不斷的右移,左側的天際線不斷消失在視野中,他們是可以被銷毀的,沒有必要再繼續占用資源了。或者,我們可以將它移動位置,在右側重建場景,省去了重復的
創建開支。
通過這個特性,我們只需要很少的資源就可以創建無止盡的天際線了。
在Skyline 文件夾中創建一個SkylineManager的腳本,因為要通過一個腳本創建兩個天際線,所以需要傳一個值進來,知道是要創建具體哪種。
public Transform prefab;
同樣的,為了場景層次視圖中的組織結構友好,我們需要設置一下層次。 首先新建一個空對象,起名Managers, 作為父容器。
然后給它創建一個子對象 Skyline Close Manager,然后把腳本拖到這個對象上來。
接下來把Skyline Close和Skyline Far Away 這兩個Cube拖拽到Project中的Skyline文件夾下,使之成為Prefab,然后將層次視圖中的兩個對象刪除,
設置好之后,層次視圖和Project視圖如下所示:
現在我們需要一個起點指示我們從哪里開始構建天際線,我們可以通過Manger對象本身的位置,而且我們需要一個數量值來決定多少個Cube組合起來才能填充完屏幕,
新建一個變量叫做 numberOfObjects ,另外聲明一個變量 nextPosition ,指示左邊的對象無效之后,應該在哪里重建。
public Transform prefab; public int numberOfObjects; private Vector3 nextPosition; void Start() { nextPosition = transform.localPosition; }
下一步是創建初始行的Cube,通過一個for循環來實現,實例化我們之前創建為prefab的Cube,每一個cube的位置都是nextPosition,而且隨着沒添加一個動態修改這個變量的值
void Start() { nextPosition = transform.localPosition; for (int i = 0; i < numberOfObjects; i++) { Transform o = (Transform)Instantiate(prefab); o.localPosition = nextPosition; nextPosition.x += o.localScale.x; }
}
現在設置Skyline Close Manager 的Position 為(0, -1, 0) 並且設置 numberOfObjects 變量的值為10 ,運行
可以看到出現了一連串的Cube了,有點“地平線”的影子了,但是有個問題就是“天際線”本身不會隨着鏡頭運動,去往左側就消失不見了。
我們回收重用Cube的策略是只回收那些離玩家超過一定距離的,因此一定要知道玩家跑了有多遠,為了達到這個目的,我們在 Runner.cs 里
添加一個靜態變量,而且時時更新它。
public static float distanceTraveled; // Use this for initialization void Start () { } // Update is called once per frame void Update () { transform.Translate(5f * Time.deltaTime, 0f, 0f); //因為起始位置是 0 distanceTraveled = transform.position.x; }
現在我們將要把我們的組成地平線的物體放到一個隊列里,並且不斷的檢查是否要回收,因為是隊列,只需要檢查第一個即可,如果是需要回收,
從隊列中移除,修改位置,然后放到隊列最后邊就可以了。
我們用一個recycleOffset變量來配置具體物體落后玩家多遠的話 可以回收,這里設置為 60 .
public class SkylineManager : MonoBehaviour { public Transform prefab; public int numberOfObjects; public float recycleOffset; private Vector3 nextPosition; private Queue<Transform> objectQueue; void Start() { objectQueue = new Queue<Transform>(numberOfObjects); nextPosition = transform.localPosition; for (int i = 0; i < numberOfObjects; i++) { Transform o = (Transform)Instantiate(prefab); o.localPosition = nextPosition; nextPosition.x += o.localScale.x; objectQueue.Enqueue(o); } } // Update is called once per frame void Update () { if (objectQueue.Peek().localPosition.x + recycleOffset < Runner.distanceTraveled) { Transform o = objectQueue.Dequeue(); o.localPosition = nextPosition; nextPosition.x += o.localScale.x; objectQueue.Enqueue(o); } } }
現在運行,在Scene視圖里,可以看到Cube行 是隨着 “奔跑者” 不斷的重新修正自己的位置的。但是他現在看起來還不像天際線,為了生成更像實際的
沒有規則的天際線,讓我們在它生成或者充值的時候隨機放大它。
首先考慮到會有重復的代碼產生,首先寫個函數Recycle ,在start或者update的時候都調用它。
public class SkylineManager : MonoBehaviour { public Transform prefab; public int numberOfObjects; public float recycleOffset; private Vector3 nextPosition; private Queue<Transform> objectQueue; void Start() { objectQueue = new Queue<Transform>(numberOfObjects); for (int i = 0; i < numberOfObjects; i++) { objectQueue.Enqueue((Transform)Instantiate(prefab)); } nextPosition = transform.localPosition; for (int i = 0; i < numberOfObjects; i++) { Recycle(); } } void Update() { if (objectQueue.Peek().localPosition.x + recycleOffset < Runner.distanceTraveled) { Recycle(); } } private void Recycle() { Transform o = objectQueue.Dequeue(); o.localPosition = nextPosition; nextPosition.x += o.localScale.x; objectQueue.Enqueue(o); } }
下一步,讓我們加入兩個變量分別表示允許的放大最大和最小值,當放大一個物體之后,我們要確定可以保證后面的物體和前面的
都是底部對齊的,
public class SkylineManager : MonoBehaviour { public Transform prefab; public int numberOfObjects; public float recycleOffset; public Vector3 minSize, maxSize; private Vector3 nextPosition; private Queue<Transform> objectQueue; void Start() { objectQueue = new Queue<Transform>(numberOfObjects); for (int i = 0; i < numberOfObjects; i++) { objectQueue.Enqueue((Transform)Instantiate(prefab)); } nextPosition = transform.localPosition; for (int i = 0; i < numberOfObjects; i++) { Recycle(); } } void Update() { if (objectQueue.Peek().localPosition.x + recycleOffset < Runner.distanceTraveled) { Recycle(); } } private void Recycle() { Vector3 scale = new Vector3( Random.Range(minSize.x, maxSize.x), Random.Range(minSize.y, maxSize.y), Random.Range(minSize.z, maxSize.z)); Vector3 position = nextPosition; position.x += scale.x * 0.5f; position.y += scale.y * 0.5f; Transform o = objectQueue.Dequeue(); o.localScale = scale; o.localPosition = position; nextPosition.x += scale.x; objectQueue.Enqueue(o); } }
為了得到一個漂亮的天際線效果,把這個Manager放到(-60,-60, 50),並且將MinSize 設置為(10,20,10) MaxSize設置為(30,60,10)Recycle Offset 設置為60
讓我們繼續添加第二個天際線層,選中Skyline Close Manager "CTRL+D " 復制一份,並且改名為 Skyline Far Away Manager, 將它的prefab屬性改為 Skyline Far Away 這個預設。
它的位置設置為(-100,-100, 100) 它的Recycle Offset 為 75,MinSize為(10,50,10) MaxSize為(30,100,10) 當然你也可以照你自己的喜好來設置新的值
產生平台
生成平台跟生成天際線很像,稍微有點不同的是平台的高度需要隨機設定,而且他們之間需要有溝壑。並且我們需要約束平台的高度,好讓它不會影響天際線的顯示,
如果平台超出這個范圍了,我們需要給它糾正過來。
在Project視圖里新建一個文件夾,取名字叫做Platform, 然后在里面創建一個C#腳本,叫做 PlatformManager, 然后把SkylineManager的腳本拷貝到這里來,
修改一部分源碼如下:
public class PlatformManager : MonoBehaviour { public Transform prefab; public int numberOfObjects; public float recycleOffset; public Vector3 minSize, maxSize, minGap, maxGap; public float minY, maxY; private Vector3 nextPosition; private Queue<Transform> objectQueue; void Start() { objectQueue = new Queue<Transform>(numberOfObjects); for (int i = 0; i < numberOfObjects; i++) { objectQueue.Enqueue((Transform)Instantiate(prefab)); } nextPosition = transform.localPosition; for (int i = 0; i < numberOfObjects; i++) { Recycle(); } } void Update() { if (objectQueue.Peek().localPosition.x + recycleOffset < Runner.distanceTraveled) { Recycle(); } } private void Recycle() { Vector3 scale = new Vector3( Random.Range(minSize.x, maxSize.x), Random.Range(minSize.y, maxSize.y), Random.Range(minSize.z, maxSize.z)); Vector3 position = nextPosition; position.x += scale.x * 0.5f; position.y += scale.y * 0.5f; Transform o = objectQueue.Dequeue(); o.localScale = scale; o.localPosition = position; nextPosition.x += scale.x; objectQueue.Enqueue(o); nextPosition += new Vector3( Random.Range(minGap.x, maxGap.x) + scale.x, Random.Range(minGap.y, maxGap.y), Random.Range(minGap.z, maxGap.z)); if (nextPosition.y < minY) { nextPosition.y = minY + maxGap.y; } else if (nextPosition.y > maxY) { nextPosition.y = maxY - maxGap.y; } } }
然后就要創建一個用於顯示平台的形狀了,在Skyline文件夾里,選中Skyline Close Mat ,Ctrl+D復制一份,顏色改為(255,60,255)然后拖放到Platform里,
改名為Platform Regular Mat, 然后創建一個Cube,把這個Meterial 拖放到Cube上,然后把Cube拖回到Platform文件夾里,使之成為預設,改名為Platform
在層次視圖里創建一個 Empty GameObject,命名為Platform Manager, 同樣也放到Managers 下面,成為它的子對象。把Platform Manager這個腳本拖放到對象上,成為它的
組件。參考下圖設置一下腳本的各個變量的值。
跳躍和墜落
現在有平台了,是時候升級一下我們的“奔跑者”了,我們使用物理引擎來實現跑,跳,墜落等效果。選中在層次視圖里選中“奔跑者”,然后Compnent->physics->rigid body,
給它添加物理引擎支持,因為我們不想讓它旋轉也不想跳出我們的視野,因此要給他添加 Z軸約束並且鎖定所有的旋轉方向。
因為運動將會在平台上平滑的移動來完成,讓我們來創建一個沒有摩擦的材質(Project 視圖上 Platform文件夾上 Create->Physics Material)。 具體設置如下圖所示。
將這個物理材質設置為層次試圖里的 Runner的 Box Collider 組件的 Material 屬性
把Runner的Position 設置為(0,2,0)這樣的話 一開始 “跑步者” 就會降落在平台上,然后開始運動。
運行一下,可以看到“跑步者”落到平台,然后向右側運動,跟設想的一模一樣,但是假如它蹭到平台的邊緣,就會發現運動有些奇怪。這是因為即使在這個情況下,Update也不會不斷修改奔跑者”位置的,
讓我們換一種方式,使用物理引擎,采用添加“力”來使物體位置改變。
把Runner.cs代碼修改如下:
public class Runner : MonoBehaviour { public static float distanceTraveled; public float acceleration; private bool touchingPlatform; void Update() { distanceTraveled = transform.localPosition.x; } void FixedUpdate() {
//碰撞的時候不會繼續作用力 if (touchingPlatform) { rigidbody.AddForce(acceleration, 0f, 0f, ForceMode.Acceleration); } } void OnCollisionEnter() { touchingPlatform = true; } void OnCollisionExit() { touchingPlatform = false; } }
設置acceleration的值為5
在Platform文件夾里,像剛才那樣給Platform也創建物理材質,命名為 Platform Regular PMat, 設置如下,然后把它復制給Platform的預設(prefab)
現在平台有了一些摩擦力,但是因為有持續作用力,所以我們的“跑步者”能夠克服這些阻力。下面再給Runner.cs 添加一個變量來控制“跑步者”的跳躍速度。
添加變量 jumpVeclocity 設置為 (1,7,0)
將 Runner.cs 代碼改為如下:
public class Runner : MonoBehaviour { public static float distanceTraveled; public Vector3 jumpVelocity; public float acceleration; private bool touchingPlatform; void Update() { if (touchingPlatform && Input.GetKeyDown(KeyCode.A)) { Debug.Log(1); rigidbody.AddForce(jumpVelocity, ForceMode.VelocityChange); } distanceTraveled = transform.localPosition.x; } void FixedUpdate() { if (touchingPlatform) { rigidbody.AddForce(acceleration, 0f, 0f, ForceMode.Acceleration); } } void OnCollisionEnter() { touchingPlatform = true; } void OnCollisionExit() { touchingPlatform = false; } }
現在運行,然后按下鼠標左鍵,就可以讓我們的“跑步者”跳躍了。
但是玩一會就發現,假如“奔跑者”碰到平台的邊緣,我們可以通過不斷的按鼠標左鍵來防止墜落。
這是一個bug,需要修復一下。
解決方案很簡單,一旦玩家按下鼠標左鍵,就將touchingPlatform變量的值設為false,就不可以連續的跳躍了。
void Update() { if (touchingPlatform && Input.GetKeyDown(KeyCode.A)) { touchingPlatform = false; rigidbody.AddForce(jumpVelocity, ForceMode.VelocityChange); } distanceTraveled = transform.localPosition.x; }
部署到Windows Phone
因為Unity的跨平台做的實在是太好了,我們只需要簡單兩步就可以生成Windows Phone的工程,在菜單欄 File-> Build Setting彈出對話框
然后選擇 Player Setting
修改一下 Orientation ,連上你的WP8手機,然后點擊 Build & Run ,會彈出一個選擇文件夾的對話框,因為Unity會幫我們生成工程,所以這也就是工程的所在地,
新建文件夾 Projects->WP8 然后選擇,等待一會編譯完成,會發現游戲開始了,很興奮吧!這么簡單就可以做一款游戲,so easy, 老板再也不擔心項目進度了。
但是!!!
還有問題,因為沒有處理回退事件,所以現在的游戲是無法通過后退按鈕退出的,找到編譯出來的項目工程,打開,然后注釋后退事件的代碼:
private void PhoneApplicationPage_BackKeyPress(object sender, CancelEventArgs e) { //e.Cancel = UnityApp.BackButtonPressed(); }
OK,至此,第一部分項目完成了,請等待 后續 的到來。
附項目下載地址:點擊下載