本文章是由機器翻譯。

Unity

使用 Unity 及 C# 開發您的第一款遊戲,第 3 部分

AdamTuliper

你仍與我在本系列中。 好。 第一篇文章,來掩蓋一些統一基礎知識 (msdn.microsoft.com/magazine/dn759441)。第二,集中統一的 2D (msdn.microsoft.com/magazine/dn781360)。現在我去我最喜歡的遊戲開發部分 — — 3D。3D 的世界是一個真正神奇的地方 — — 令人驚異的浸入式環境,豐富的聲音效果和美麗的視覺效果 — — 甚至只是一個簡單的益智遊戲與真實世界的物理可以讓你上癮幾個小時。

3D 遊戲無疑增加了一層複雜性,在 2D,但以它一片一片你可以建立一個很酷的 3D 遊戲。新專案設置為 2D 和 3D 團結支援 3D。你可以有三維物件在一個 2D 遊戲 (反之亦然)。

什麼構成了一個 3D 場景?

3D 場景主要包括三個主要的視覺元件 — — 燈光,網格渲染器和著色器。一盞燈是,嗯,一盞燈和團結支援四種不同類型。你可以發現他們都在遊戲功能表下。嘗試添加各種類型和更改其屬性。最容易的一個,照亮你的場景是定向的光,像是天空中的太陽。

網格 (或模型) 是彌補構成物件的多邊形的頂點的集合。著色器是一個包含代碼來控制您的物件將如何顯示或與光相互作用的編譯的常式。一些著色器簡單地以光和反映它像一面鏡子 ; 其他人採取一種紋理 (要應用於您的網格的圖像) 和可以啟用陰影和深度 ; 和有些甚至允許您通過您的模型,就像籬笆視覺打孔。

模式通常是從另一個建模軟體套裝程式匯出的 FBX 或 OBJ 檔。FBX 檔也可以包含動畫資料,所以您可能會收到一個 FBX 檔為您的模型和一個包含幾個動畫。此外支援了幾個協力廠商檔案格式,如瑪雅.ma 格式和攪拌機檔。通常,您需要協力廠商程式安裝在同一系統上,如果您要導入這些檔的統一,那麼就只需拖動並將它們放入到統一的專案,就像任何其他檔一樣。在幕後,統一將轉換的 FBX 檔案格式為其他檔案格式 (在導入或檢測檔更改)。

資產存儲庫

我的第一篇文章,談到資產存儲庫,但在 3D 遊戲中是不是真的派上用場。我不是演出者,因為這是一本專業雜誌,我猜你們中的大多數也不是。(如果你是,請接受我恭喜,你是一種罕見的群的一部分。)但如果我想要創建一個遊戲,與鬱鬱蔥蔥的環境和被破壞的古建築,例如,它不是一個問題。我可以買我從資產存儲庫的需要。有 15 個不同僵屍的想,我可以從 Mixamo 在資產存儲庫中購買一包。潛在的組合是幾乎是無止境的所以不要擔心別人的遊戲看起來像你的。最重要的是,資產存儲庫集成到統一。您可以通過按一下視窗升級您的包裹 |資產存儲庫,然後 bin 圖示。你也可以看看評論和注釋更容易確定某個特定專案是否適合您的專案,例如,其移動優化與否。桌面遊戲通常可以處理更多的物件/頂點/紋理/記憶體比手機的遊戲,雖然一些較新的晶片使行動裝置今天看起來像 Xbox 360。

在典型的 3D 遊戲中,很多相同的概念,從一個 2D 遊戲應用 — — 碰撞、 觸發器、 剛體、 遊戲的物件轉換、 元件和更多。無論類型的 3D 遊戲,您通常會希望控制輸入、 移動和字元 ; 使用動畫和粒子效果 ; 共建是幻想和現實充滿想像力的世界。我將討論一些方法統一也有助於此。

輸入、 運動和角色控制器

閱讀輸入運動變得稍微複雜的 3D 的因為,而不是只動中的 X 和 Y 平面,你現在可以動議在三個維度:X、 Y 和 Z。 三維運動的情況包括 (但不限於) 自上而下的運動,一個字元移動到哪裡只有水準和垂直方向 ; 旋轉觀景窗或字元,當讀取滑鼠輸入,是這樣做的很多第一人稱射擊遊戲 (FPS) 遊戲 ; 掃射左到右的時候閱讀水準輸入 ; 旋轉,轉過身,當閱讀水準的輸入 ; 或只是倒著走路。有大量優秀的移動選項可供選擇。

當移動一個物件時,你不給它一個位置來移動,正如你可能期望。請記住,你正在執行的代碼與每個幀,所以你仍然需要以小的增量移動該物件。要麼,你可以讓物理引擎處理這一支部隊加入你的剛體移動它,或者您可以補間的物件。補間基本上意味著過渡之間的價值觀 ; 那就,從 A 點搬到 b 點。 有很多種補間統一,包括免費的協力廠商庫,如 iTween 中的值。圖 1 演示一些手動的方式來移動物件的統一。請注意,為了簡單起見,他們還沒有進行優化 (要做到這一點,我認為在一個變數中,往往阻止去從託管代碼到本機代碼轉換的引用)。

圖 1 移動物件的各種方法

// Method 1
void Update()
{
  // Move from point a to point b by .2 each frame - assuming called in Update.
  // Will not overshoot the destination, so .2 is the max amount moved.
  transform.position =
    Vector3.MoveTowards(transform.position, new Vector3(10, 1, 100), .2f);
}
// Method 2
void Update()
{
  // Interpolate from point a to point b by a percentage each frame,
  // in this case 10 percent (.1 float).
  var targetPosition = new Vector3(10,0,15);
  transform.position = Vector3.Lerp(parentRig.position, targetPosition, .1f);
}
// Method 3
void Update()
{
  // Teleport the object forward in the direction it is rotated.
  // If you rotate the object 90 degrees, it will now move forward in the direction
  // it is now facing. This essentially translates local coordinates to 
  // world coordinates to move object in direction and distance
  // specified by vector. See the Unity Coordinate Systems section in the 
  // main article.
  transform.Translate(Vector3.forward * Time.deltaTime);
}
// Method 4
void FixedUpdate()
{
  // Cause the object to act like it's being pushed to the
  // right (positive x axis). You can also use (Vector.right * someForce)
  // instead of new Vector().
  rigidbody.AddForce( new Vector3(7, 0, 0), ForceMode.Force);
}
// Method 5
void FixedUpdate()
{
  // Cause the object to act like it's being pushed to the positive
  // x axis (world coordinates) at a speed of approx 7 meters per second.
  // The object will slow down due to friction.
  rigidbody.velocity = new Vector3(7,0,0);
}
// Method 6
// Move the rigidbody's position (note this is not via the transform).
// This method will push other objects out of the way and move to the right in
// world space ~three units per second.
private Vector3 speed = new Vector3(3, 0, 0);
void FixedUpdate()
{
  rigidbody.MovePosition(rigidbody.position + speed * Time.deltaTime);
}
// Method 7
void FixedUpdate()
{
  // Vector3.forward is 0,0,1. You could move a character toward 0,0,1, but you
  // actually want to move the object forward no matter its rotation.
  // This is used when you want a character to move in the direction it's
  // facing, no matter its rotation. You need to convert the meaning of
  // this vector from local space (0,0,1) to world space,
  // and for that you can use TransformDirection and assign that vector
  // to its velocity.
  rigidbody.velocity = transform.TransformDirection(Vector3.forward * speed);
}

每種方法各有優點和缺點。可以移動只是變換的性能命中 (方法 1-2),雖然它是一個非常簡單的方法,做運動。統一假定,如果一個物件在它沒有剛體元件,它可能不是一個移動的物體。它生成一個靜態碰撞矩陣內部要知道物件在哪裡,這可提高性能。當你移動物件移動變換時,這個矩陣已被重新計算,這將導致性能下降。對於簡單的遊戲,你可能從未注意到的命中和它可能是最簡單的事情要做,雖然作為你的遊戲變得更加複雜,就必須移動剛體本身,方法 4-6 的那樣。

旋轉物件

旋轉物件是相當簡單的就像移動的物件,除了向量現在代表度而不是位置或一個歸一化的向量。歸一化的向量是只是一個向量,一個對於任何值的最大值,你只是想簡單地通過使用向量引用一個方向時,可以使用。有一些向量關鍵字可説明,如 Vector3.right,回來了,向前,下來,起來,左,右,零和一。將移動或旋轉在水準方向上積極的東西可以使用 Vector.right,只是一個快捷方式 (1,0,0),或者向右的一個單位。為旋轉物件時,這將是一個學位。在圖 2,只是旋轉一點點每一幀中的物件。

圖 2 方法旋轉物件

// Any code below that uses _player assumes you
// have this code prior to it to cache a reference to it.
private GameObject _player;
void Start()
{
  _player = GameObject.FindGameObjectWithTag("Player");
}
// Method 1
void Update () {
  // Every frame rotate around the X axis by 1 degree a
  // second (Vector3.right = (1,0,0)).
  transform.Rotate(Vector3.right * Time.deltaTime);
}
// Method 2
void Update () {
  // No matter where the player goes, rotate toward him, like a gun
  // turret following a target.
  transform.LookAt(_player.transform);
}
// Method 3
void Update()
{
  Vector3 relativePos = _player.transform.position - transform.position;
  // If you set rotation directly, you need to do it via a Quaternion.
  transform.rotation = Quaternion.LookRotation(relativePos);
}

這些技術都是次要的細微差別。您應該使用哪一個?不適用於部隊的剛體,如果可能的話試試我已經大概只被迷惑了你有點與該選項。好消息是幾乎所有這些都能為您的現有代碼。

你注意到四元數方法 3 中的嗎?統一內部使用四元數來表示所有的旋轉。四元數是防止叫做萬向節鎖,如果你使用普通的歐拉角旋轉可能會發生效應的有效結構。萬向節鎖兩個軸旋轉在同一平面上,然後不能分離時發生。(在視頻 bit.ly/1mKgdFI 提供了一個很好的解釋。)若要避免此問題,統一使用四元數,而不是歐拉角,雖然在統一編輯器中您可以指定歐拉角,它會轉換成一個在後端的四元數。很多人從來沒有經歷萬向節鎖,但我想指出一點,如果你想要直接在代碼中設置一個旋轉,你必須通過一個四元數,您可以從使用 Quaternion.Euler 的歐拉角轉換。

現在,您已經看到很多選項,我應該注意到發現最簡單的方法是使用一個剛體,並簡單地套用。AddForce 的字元。我更喜歡重用代碼時,並幸運地統一供應預置的數目。

讓我們不重新發明輪子

Unity 提供了資產存儲庫中的示例資產包 (bit.ly/1twX0Kr),其中包含一個跨平臺輸入管理器移動操縱杆控制,一些動畫和粒子,與大多數重要的是,一些預建的角色控制器。

有一些舊的資產 (撰寫本文時,版本 4.6) 附帶統一。現在,這些資產分佈作為一個單獨的套裝軟體,團結可以單獨更新。不必寫的所有代碼來創建一個第一人稱人物在你的遊戲,三人的性格或甚至是自動駕駛的汽車,你可以只使用示例資產從預置。拖和放入你的場景和瞬間你有查看具有多個動畫和完全訪問原始程式碼,第三人稱,如中所示圖 3

第三個人預置
圖 3 第三個人預置

Animations

一整本書可能是專用 (,) Mecanim 動畫系統的統一。在 3D 動畫是一般比二維空間中比較複雜。在 2D、 動畫檔通常改變精靈渲染器中每個關鍵幀,給動畫的外觀。在 3D 動畫資料是很多更複雜。記得從我的第二條動畫檔包含關鍵幀。在 3D 中,可以有很多的關鍵幀,每個都有很多的資料點,改變一根手指,一隻手臂或一條腿,移動,或執行任何數目和類型的運動。網格可以也已定義骨頭,並且可以使用的元件稱為皮膚網格渲染器變形網格基於骨骼是如何移動的一樣的活的動物會。

雖然您可以創建他們在團結、 以及動畫檔通常創建在一個協力廠商建模/動畫系統。

3D 動畫系統中的字元的基本姿勢是 T 構成,這就是它聽起來就像 — — 人物,站直的一面張開了雙臂,和它適用于幾乎任何人形機器人形狀模型。然後可以通過 Mecanim 將幾乎任何動畫檔分配給它來搞活的基本特徵。你可以有一個僵屍,精靈和人類所有跳舞以同樣的方式。你可以混合和匹配的動畫檔,然而你認為合適,並將它們分配通過國家多就可以在 2D。若要執行此操作,您使用動畫控制器,如中所示圖 4

動畫控制器用於控制字元動畫狀態
圖 4 動畫控制器用於控制字元動畫狀態

請記住,你可以從統一資產存儲 ; 字元和動畫 您可以創建它們帶有建模工具 ; 還有像 Mixamo 的保險絲的協力廠商產品,使您能夠快速生成您自己的自訂的字元。查閱我為入門動畫在統一的通道 9 視頻。

創造一個世界

統一有一個內置的地形系統,創造一個世界。您可以創建一種地形,然後使用包括的地形工具雕刻你的地形,使山,地方的樹木和草、 油漆紋理和更多。可以通過導入天空盒包向你們的世界添加一片天空 (資產 |導入包 |天空盒) 並將其分配在編輯 |渲染設置 |天空體材料。它我只花了幾分鐘來創建地形與反射,動態水、 樹木、 沙子、 山和草,如中所示圖 5

快速建立地形
圖 5 快速建立地形

統一座標系統

統一已指在遊戲中或在螢幕上的點,如中所示的四種不同方法圖 6。還有的螢幕空間,範圍為從 0 到的圖元數,通常用於獲取在使用者接觸,或按一下螢幕上的位置。視口空間是簡單值 0 到 1,這使得它容易說,例如,一半是.5,而不是以圖元為單位) 除以 2。 所以我可以輕鬆地放置在螢幕的中間物件通過使用 (。 5、.5) 作為它的位置。世界空間是指在基於三座標,一個遊戲物件的絕對位置 (0,0,0)。在一個場景中的所有頂級遊戲物件有他們列在世界空間中的座標。最後,局部空間始終是相對於父遊戲物件。一個頂級的遊戲物體,這是世界空間相同。所有的子遊戲物體列出中的編輯器中相對於其父級的座標,所以在一棟房子,例如,您的應用程式中的模型可能有全局座標 (200、 0、 35),同時它的前門 (假設它一個子遊戲物體的房子) 可能只有 (1.5,0,0),因為這是相對於父級。在代碼中,當您引用 transform.position,它老是在全局座標,即使它是一個子物件。在示例中,門會 201.5、 0 35),但如果您改為引用 transform.localPosition,你一定會回來 (1.5,0,0)。統一具有在不同的座標系統轉換的函數。

中統一座標
圖 6 中統一座標

在事先移動我大多使用世界空間移動,但在某些情況下使用本地空間的例子。回指中的方法 7 圖 1。在這個例子中我採取 Vector.forward,局部歸一化 (或單位) 向量即 (0,0,1)。這種情況本身沒有多大意義。然而,它顯示移動的東西,在 Z 軸上是向前的意圖。如果該物件從 0,0) 是旋轉 90 度嗎?轉發可以現在有兩個含義。它可以意味著原始絕對 Z 軸 (在全局座標系中) 或相對於旋轉物件,一直指向前為物件的 Z 軸。如果我想要一直前進不管其旋轉的物件,我可以簡單地翻譯之間本地轉發到真實世界前向向量的變換。TransformDirection(Vector3.forward * speed) 這個例子所示。

執行緒和協同

統一使用協同系統來管理它的執行緒。如果你想要什麼發生在你認為應該是一個不同的執行緒,你踢掉協程,而不是創建一個新執行緒。統一管理這一切的幕後。會發生什麼是協同暫停時它擊中的產量的方法。在示例中圖 7,攻擊動畫演奏為一個隨機長度而暫停,然後再次在攻擊中發揮。

圖 7 使用暫停行動的協同

void Start()
{
  // Kick off a separate routine that acts like a separate thread.
  StartCoroutine(Attack());
}
IEnumerator Attack()
{
  // Trigger an attack animation.
  _animator.SetTrigger("Attack");
  // Wait for .5 to 4 seconds before playing attacking animation, repeat.
  float randomTime = Random.Range(.5f, 4f);
  yield return new WaitForSeconds(randomTime);
}

物理和碰撞檢測

在 3D 中的物理現象和碰撞檢測功能則與 2D、 幾乎相同,除了形狀也不同、 碰撞和剛體元件具有幾個不同的屬性,比如能夠做到自由旋轉或在 X、 Y 和 Z 軸的運動。在 3D 現在是作為一個碰撞檢測區環繞整個形狀的一種模型的網格碰撞器。這可能聽起來很好,和碰撞是不錯,但它不是很好的性能。理想情況下,您想要簡化對撞機形狀和限制使用它們所需的處理能力。有一隻僵屍嗎?沒問題,使用一個膠囊碰撞器。一個複雜的物件嗎?使用多個碰撞器。請儘量避免網格碰撞器。

Unity 提供了大量的方法,要知道當發生碰撞情況下,或觸發觸發器。下面顯示了只是一個基本的例子:

void OnCollisionEnter(Collision collision)
{
  // Called when you have a physical collision.
  Debug.Log("Collided with " + collision.gameObject.name);
}
void OnTriggerEnter(Collider collider)
{
  // Called when another object comes within the trigger zone.
  Debug.Log("Triggered by " + collider.gameObject.name);
}

有更多的方法比在此處列出,如 OnTriggerExit 和 OnCollisionExit,他們就幾乎等同于 2D 的同行。

建立物件

當你想要在運行時創建新的基於遊戲的專案時,您不使用建構函式。相反,您使用具現化。你當然可以使用建構函式,只是不能直接在腳本從 MonoBehavior,碰巧被分配到任何遊戲物件的所有頂級腳本繼承中上課。這些腳本可以,然而,呼籲他們想要的所有其他物件建構函式:

// Assume this reference has been assigned in the editor.
[SerializeField]
private GameObject _zombie;
void Start()
{
  // Create a new instance of that game object. This can be
  // a prefab from your project or object already in scene.
  Instantiate(zombie, transform.position, Quaternion.identity);
}

粒子效果

如果你想要閃爍的星星、 灰塵、 雪、 爆炸、 火災、 霧從瀑布,血液影響或許多其他的影響,您使用了粒子效果。還有老的粒子系統在統一和更新,更優化一個叫做手裡劍。你可以用手裡劍在統一中,包括有支援碰撞你下落微粒的這麼多不可思議的事情。因為有很多教程,如在一個 bit.ly/1pZ71it,它們通常在編輯器中使用設計器創建,在這裡,我將只顯示如何當一個字元說,進入一個硬幣收集該觸發器區域,他們可以具現化。

若要開始與粒子,只是轉到遊戲物件 |粒子系統功能表,你會立即看到一個添加到你的場景,如圖 8

粒子效果
圖 8 粒子效果

我想從我的粒子系統創建預置 (是我所提及的第二篇文章),所以我可以輕鬆地重用它們,和我然後可以輕鬆地具現化它們通過代碼通過第一次將腳本指派到一個遊戲物體 (假設所有遊戲物件腳本元件都在 MonoBehavior,從派生的類),然後在編輯器從我的場景或預置在我的專案上,例如拖動了粒子效果,在公開的 SmokeEffect 屬性圖 9

圖 9 公開的 SmokeEffect 屬性

[SerializeField]
private ParticleSystem _smokeEffect;
void OnTriggerEnter(Collider collider)
{
  // Ensure you only show particles if the player comes within your zone.
  if (collider.gameObject.tag == "Player")
  {
    GameController.Score++;
    // Create particle system at the game objects position
    // with no rotation.
    Instantiate(_smokeEffect, transform.position, Quaternion.identity);
    // Don’t do: Destroy(this) because "this"
    // is a script component on a game object, so use
    // this.gameObject, that is, just gameObject.
    Destroy(gameObject);
  }
}

創建一個使用者介面

統一 4.6 添加全新的使用者介面系統用於創建平視顯示器中使用文本、 面板、 小部件和更多的遊戲元素。將文本添加到您的遊戲的顯示器是只需點擊遊戲 |UI |文本和設置中的字體和文本。如果你想要控制,後來通過代碼也許更新分數,您只需使用:

// Gets the UnityEngine.UI.Text component.
  var score = GetComponent<Text>();
  score.text = "Score:0";

如果希望圖像在 UI 中,只需點擊遊戲 |UI |圖像,並分配一個 2D sprite 圖像到此新元件。我可以只是因為與其他任何遊戲物件設置這些值。我希望你現在看到一種模式。若要創建一個簡單的 GUI,創建通過遊戲物體的 UI 物件 |使用者介面功能表編輯器中設置的初始值和控制他們後來通過獲取對這些 UI 元件的引用和設置的值,或甚至對值進行動畫處理。建立了一個基本的 GUI,示圖 10,通過創建下一個新的畫布元件元素。新的統一 4.6 UI 系統包含大量的基本物件類型如面板、 按鈕、 文本、 圖像、 滑塊、 捲軸和切換,並非常容易錨定它們,縮放,和拖放它們來創建一個使用者介面。

圖像和單挑文本的使用者介面
圖 10 圖像和單挑文本的使用者介面

在你的遊戲的 AI

它不會公平而不應提及 AI,雖然我不會陷入在這裡創建 AI,(即使它的構造塊是在前面的代碼示例為查找移動旋轉)。不過,我要提到幾個可供您使用的選項。我毫不猶豫地呼籲 AI 人工智慧遊戲,因為它不是只是一個非常基本的動作如此多的情報。我向您展示了如何向另一個物件的旋轉和移動該物件的變換。這是基本的 AI 在很多比賽。統一具有一些內置的尋找路徑功能,其 NavMesh 的支援,提前計算物件周圍的所有路徑。NavMesh 運作相當良好,現已列入統一,免費版雖然很多人選擇相反使用 A * 尋路專案 (arongranberg.com/astar),這是一種演算法,您可以自己實現,或救救你自己的時間通過購買資產打包為它。截至這寫作,2D 的尋路支援內置不統一,只有 3D,雖然 A * 不會有那樣的能力。行為從 AngryAnt 2.0 是一個受歡迎的 AI 外掛程式為統一與一些真正強大的功能,而且還有雨,從一個免費的 AI 工具組 rivaltheory.com,也是相當不錯並且有內置行為進行跟蹤,發現,Mecanim 集成。

總結

3D 世界增添了額外的複雜性層 2D 因為它涉及全網格和增加一個維度。資產存儲庫是絕對關鍵的初學者和先進的一樣,你可以真的下車一個快速的開始通過使用預先創建的資產。

開始開發遊戲,瘋狂地將在互聯網上找到很多的模型和貼圖。有一些偉大的資產市場在那裡,但你很快就會發現他們並非全是好的遊戲。我一旦下載了 100,000 在其模型中頂點附近的小一塊 !尋找的移動優化的資產或簽出的多邊形頂點/計數,以確保你找到那些能為你的遊戲。否則,他們可以大大拖慢你的表現。有您可以使用在模型,其中包括一個用於統一稱為徹的優化工具。在下一篇文章中,我將討論如何接管一個遊戲或應用程式從統一到 Windows 平臺。簽出我的頻道 9 博客 (aka.ms/AdamChannel9) 的一些視頻和下載內容的連結。


AdamTuliper 是與生活在陽光明媚的南加州的微軟高級技術福音傳教士。他是獨立遊戲開發的奧蘭治縣團結 Meetup co 管理員和 Pluralsight.com 作者。他和他的妻子都約有三個孩子,所以向他伸出雖然他仍然有時間,就在 adamt@microsoft.com 或在 Twitter 上 twitter.com/AdamTuliper

衷心感謝以下技術專家對本文的審閱:Matt紐曼 (潛科學工作室) 和 Tautvydas Žilys (統一)