Unity-2D

Unity-2D

1.Unity中的2D模式:

1)遊戲在二維上展示

啟用 2D 模式時將會設置正交(即無透視)視圖:攝像機沿 Z 軸觀察,而 Y 軸向上增加。因此可以輕鬆可視化場景並放置 2D 對象。

2)設置項目默認模式:Edit > Project Settings > Default Behavior Mode

在 2D 項目模式下:

  • 所有影像(images)都會被當做 2D 圖片,並設置成 sprite mode 精靈模式

  • Sprite Packer 會被啟動

  • Scene 視圖默認為 2D

  • 默認遊戲對象沒有實時方向光。

  • 攝像機的默認位置為 0,0,–10。(在 3D 模式下為 0,1,–10。)

  • The camera projection is set to be Orthographic. (In 3D Mode it is Perspective.)攝像機投射模式被設置為正交(沒有遠小近大,沒有距離之分),而在 3D 模式下,是透視(遠小近大,有距離之分)

  • 在 Lighting 窗口中:

    • Skybox is disabled for new scenes:天空盒默認關閉

    • Ambient Source is set to Color. (With the color set as a dark grey: RGB: 54, 58, 66.) 保圍光源設置為 color ,默認為灰色

    • Realtime Global Illumination (Enlighten) is set to off.關閉實時光照

    • Baked Global Illumination is set to off.關閉全局光照烘焙

    • Auto-Building set to off.自動創建關閉

2.在Unity中創建2D遊戲

1)Player的創建與控制:
  • 使用靜態精靈創建Player:
    • 精靈 Sprite 是 Unity 中 2D 素材的默認存在形式,是 Unity 中的 2D 圖形對象。

    • 在 2D 遊戲中,使用 2D 素材的過程: PNG(JPG 等)—-> Sprite —-> GameObject

2)Player的移動腳本:
  • 鋪墊

    • Vector2 二維向量
      • 在數學中,Vector 向量/矢量指的是帶方向的線段

      • 在 Unity 中,Transform 值使用 x 表示水平位置,使用 y 表示垂直位置,使用 z 表示深度。這 3 個數值組成一個坐標。由於此遊戲是 2D 遊戲,你無需存儲 z 軸位置,因此你可以在此處使用 Vector2 來僅存儲 x 和 y 位置。

      • Transform 中 position 的類型,也是 Vector2。C# 這種強類型語言,賦值時,左右必須是同一類型才能進行

    • Unity 默認 Input Manager 設置
      • 在 Unity 項目設置中,可以通過 Input Manager 進行默認的遊戲輸入控制設置 Edit > Project Settings > Input

      • 鍵盤按鍵,以 2 個鍵來定義軸:

        • 負值鍵 negative button,被按下時將軸設置為 -1

        • 正值鍵 positive button ,被按下時將軸設置為 1

      • Axis 軸 Axes 是它的負數形式

        • Horizontal Axis: 水平軸 對應 X 軸

        • Vertical Axis:縱軸 對應 Y 軸

    • Input類
      • 使用該類來讀取傳統遊戲輸入中設置的軸/滑鼠/按鍵,以及訪問移動設備上的多點觸控/加速度計數據。若要使用輸入來進行任何類型的移動行為,請使用 Input.GetAxis。 它為您提供平滑且可配置的輸入 – 可以映射到鍵盤、遊戲桿或滑鼠。 請將 Input.GetButton 僅用於事件等操作。不要將它用於移動操作。Input.GetAxis 將使腳本程式碼更簡潔。

    • 時間和幀率
      • 當前的程式碼中,幀數越高,同一時間內,執行 Update 的次數越多,角色移動速度越快。如果遊戲以每秒 60 幀的速度運行,那麼 Ruby 將移動 0.1 _ 60,因此每秒移動 6 個單位。但是,如果遊戲以每秒 10 幀的速度運行,就像剛剛讓遊戲運行的那樣,那麼 Ruby 僅移動 0.1 _ 10,因此每秒移動 1 個單位!

      • 如果一個玩家的電腦非常陳舊,只能以每秒 30 幀的速度運行遊戲,而另一個玩家的電腦能以每秒 120 幀的速度運行遊戲,那麼這兩個玩家的主角的移動速度會有很大差異。這樣就會使遊戲的難易程度提高或降低,具體取決於運行遊戲的電腦。

      • 而幀數是由硬體水平影響的(越好越高),不同電腦中,會導致遊戲效果完全不同

  • 新建Player後,選中Player,在Inspector窗口中Add Component,自定義一個腳本,移動腳本的程式碼如下:

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class RubyMover : MonoBehaviour
    {
       // Start is called before the first frame update
       void Start()
      {
           
      }
       public float speed = 0.1f;
       // speed訪問許可權設置為public,在Unity中可修改屬性。
       // Update is called once per frame
       void Update()
      {
           float x = Input.GetAxis("Horizontal");
           float y = Input.GetAxis("Vertical");

           Vector2 position = transform.position;
           position.x += 0.1f * x * speed * Time.deltaTime;
           position.y += 0.1f * y * speed * Time.deltaTime;
           transform.position = position;

      }
    }
    3)2D遊戲中瓦片地圖的創建和使用:
    • 瓦片地圖工作流程
      1. 預處理 sprite 資源:將圖片資源拖拽到 project 中,生成 sprite;然後一般需要進行切割 slice ,將其配置成需要的各個 tile;

      2. 創建要在其上繪製瓦片的瓦片地圖。此過程中還會自動創建 Grid 遊戲對象作為瓦片地圖的父級。

      3. 直接創建瓦片資源,或者通過將用作瓦片素材的精靈帶入 Tile Palette 窗口自動生成瓦片。

      4. 創建一個包含瓦片資源的 Tile Palette,並使用各種筆刷來繪製到瓦片地圖上。

    • 瓦片地圖的高級使用
      • 使用普通的瓦片地圖,構建整個世界,一個一個格子用筆刷來填充,非常費時,Unity 在不斷地升級中,添加了很多種快速構建瓦片地圖的方式,掌握了這些方法,能夠極大減少繪製地圖所用的時間。

        • 編程瓦片 Scriptable Tile

          • Unity 支援用程式碼創建自己的 Tile 類,自己書寫瓦片的繪製規則。還可以為瓦片創建自定義編輯器。這與腳本化對象的自定義編輯器的工作方式相同。創建一個繼承自 TileBase(或 TileBase 的任何有用子類,如 Tile)的新類。重寫新的 Tile 類所需的所有方法。

        • 編程畫筆 Scriptable Brush

          • Unity 也支援創建自己的 Brush 類,設計適合自己遊戲的網格畫筆。

            創建一個繼承自 GridBrushBase(或 GridBrushBase 的任何有用子類,如 GridBrush)的新類。重寫新的 Brush 類所需的所有方法。創建可編程畫筆後,畫筆將列在 Palette 窗口的 Brushes 下拉選單 中。默認情況下,可編程畫筆腳本的實例將經過實例化並存儲在項目的 Library 文件夾中。對畫筆屬性的任何修改都會存儲在該實例中。如果希望該畫筆有多個具備不同屬性的副本,可在項目中將畫筆實例化為資源。這些畫筆資源將在 Brush 下拉選單中單獨列出。

    • 2D Tilemap Extras (2D 瓦片地圖擴展)
      • Animated Tile 動畫瓦片

        • 動畫瓦片在遊戲運行時,按順序顯示 Sprite 列表以創建逐幀動畫

      • Rule Tile 規則瓦片

        • 可以為每個瓦片創建規則,在繪製時,unity 會自動響應這些規則,繪製地圖時更加智慧

        • RuleTile 使用步驟:

          • 準備 Tile 素材,配置素材屬性,分割素材;

          • 新建 RuleTile,為其添加規則,設置每個 Tile 的相鄰規則;

          • 將設置好的 RuleTile 拖拽到 Tile Palette 中,就可以使用了。

      • Rule Override Tile / Advanced Rule Override Tile 規則覆蓋瓦片

        • 可以用已經生成好的 Rule Tile,作為 Rule Override Tile 的規則來源,只替換對應的瓦片素材,而沿用原先的規則,可以快速的創建規則瓦片的方法。

4)場景中的圖形順序:
  • 偽透視圖
    • 透視圖指的是有深度、距離感的圖,一般要三維中的深度軸來表現場景的深度,而二維遊戲中沒有這個深度,只能通過前後來仿造深度效果,稱為「偽透視圖」

    • 先前通過調整瓦片的 Order in Layer 屬性來解決了瓦片地圖的排序問題,但並非總是希望一個遊戲對象在另一個遊戲對象之上,比如,在同一個瓦片地圖中,玩家角色在一個物體之前(比如一棵樹)時,應該是玩家遮擋樹,而玩家移動到樹後時,應該是樹遮擋玩家,這就需要「偽造」透視圖。

    • 在 2D 遊戲中,場景里的 「前後」 是由 Y 軸決定的,需要讓 Unity 根據遊戲對象的 y 坐標來繪製遊戲對象Y 軸 y 坐標值越小,越靠前,應該遮擋 y 坐標值較大的遊戲對象,也就是 y 坐標較小的遊戲對象後繪製,就會位於上層

    • 在遊戲中,如果要設置 2D 偽透視試圖,需要在項目設置中進行更改:

      • Edit > Project Settings > Graphics > Camera Settings > Transparency Sort Mode = Custom Axis > Transparency Sort Axis x = 0 / y = 1 / z = 0

      • 此設置告訴 Unity 在 y 軸上基於精靈的位置來繪製精靈。

    img

    • 按 Play 以進入運行模式並測試你的更改。現在,你的角色比箱子高時,角色應該會繪製在箱子的後面;而角色比箱子低時,繪製在箱子的前面。

    • Sprite 軸心 pivot

      • 每個 Sprite 都有一個軸心(中心點),Unity 根據 pivot 對 sprite 進行定位,這個 pivot 可以在 sprite editor 中調整,可以將其設置到 sprite 上任意位置

      • 在 2D Rpg 遊戲場景中的遊戲對象,如果想要實現較為真實的 「偽透視」 效果,最好將遊戲對象的 sprite 中 pivot 都設置到素材的最下方正中。

      • 然後將遊戲對象的 Sprite Sort Point 由 Center 改為 Pivot 即可.

5 )物理系統:
  • 鋪墊:
    • Unity中內置的物理系統可以模仿地球上的物理系統,使Unity中創建的一切精靈都具有類似地球上的物理屬性。

  • 物理系統中比較重要的腳本組件:
    • Rigidbody :項目中的剛體組件

      • 定義了對象收到外力後,如何模擬其行為,如翻滾,掉落。當為一個對象添加了 Rigidbody 組件後,就會模擬受重力而掉落。如果再加上collider組件,則會響應外部的力,而運動。添加 Rigidbody後,我們就不能通過 transform 組件來移動物體了,只能由物理系統來模擬驅動。當然這不是絕對的,某些特定情境,需要關掉物理模擬,將物體擺放到指定位置,再重新打開物理模擬。有時,我們希望對象參與物理模擬,但是其行為還是由邏輯控制,比如,對於遊戲內的NPC,我們需要由程式碼控制其運動,同時又需要添加rigidbody,這樣才能被Trigger檢測到,對於這種情況,可以將 Rigidbody 設置為動力學物體(Is Kinemiac)。

    • ···collider : 項目中碰撞體組件:

      • 定義了物體的形狀來進行碰撞模擬。物理碰撞體的形狀不需要嚴格和物體一致,只要能表示其物理形狀即可。比如一個複雜的人,我們可以用一個膠囊體來定義其碰撞形狀。

      • Unity內建了很多碰撞體,以下時常用的碰撞體:

        • BoxCollider 立方體碰撞體 SphereCollider 球形碰撞體 CapsuleCollider 膠囊碰撞體 MeshCollider 從對象的網格創建碰撞體。MeshCollider之間不支援碰撞檢測,效率太低。可以將MeshCollider的Convex選項打開,則能支援MeshCollider之間的碰撞檢測,同時提高性能。 WheelCollider 創建載具的輪子的碰撞體 TerrainCollider 處理Unity地形系統的碰撞

        • 2D中相應的BoxCollider2D,CircleCollider2D,CapsuleCollider2D,以及其它轉為2D建立的碰撞體類型,如PolygonCollider2D。

        • Box,Sphere,Circle,Capsule這些簡單碰撞體的效率相對都是較高的,建議使用這些簡碰撞體。

      • 當一個精靈比較複雜時(如創建的一個人物角色),可以採用組合碰撞體的方式,對角色不同的身體部位分別採用不同的碰撞體組件。

    • 觸發器:
      • 為碰撞體組件選中is Trigger複選框,會發現碰撞體不再組織移動了。觸發器用於檢測碰撞但不產生碰撞。在2D項目中要使用OnTriggerEnter2D方法。

      • code:

      • private void OnTriggerEnter2D(Collider2D collision)
          {
               Debug.Log($"與{collision}發生碰撞了!");
          }
    • OnColliderEnter(2D) 和OnTriggerEnter(2D):

      • OnCollisionEnter方法必須是在兩個碰撞物體都不勾選isTrigger的前提下才能進入,反之只要勾選一個isTrigger那麼就能進入OnTriggerEnter方法。

      • OnCollisionEnter和OnTriggerEnter是衝突的不能同時存在的。

      • OnTriggerEnter和OnCollisionEnter的選擇。

        • 如果想實現兩個剛體物理的實際碰撞效果時候用OnCollisionEnter,Unity引擎會自動處理剛體碰撞的效果。

        • 如果想在兩個物體碰撞後自己處理碰撞事件用OnTriggerEnter。

6)Unity中的世界交互:
  • 可收集的對象:
    • 例如玩家通過觸發器實現回血或者扣血的操作:

    • //扣血的類

      using System.Collections;
      using System.Collections.Generic;
      using UnityEngine;

      public class collectibleHealth : MonoBehaviour
      {
         public int healther = -1;

         private void OnTriggerEnter2D(Collider2D collision)
        {
             Debug.Log($"與{collision}發生碰撞了!");

             //獲取主角遊戲對象
             RubyMover RM = collision.GetComponent<RubyMover>();
             if(RM!=null)
            {
                 //調用改變血量的方法時,建議再加一層判斷,血滿時無法調用這個方法。
                 RM.ChangeHealth(healther);
            Destroy(gameObject);
            }
             
        }  
      }

      //玩家類
      //玩家類中的一些屬性(例如血量或者其他的一些私有屬性)建議設置為私有,並提供get/set方法。
      public class RubyMover : MonoBehaviour
      {
         public float speed = 0.1f;
         Rigidbody2D rigidbody;
         float x;
         float y;
         int CurHealth;
         int MaxHealth;

         // Start is called before the first frame update
         void Start()
        {
             rigidbody = GetComponent<Rigidbody2D>();
             MaxHealth = CurHealth = 5;
        }
         
         // Update is called once per frame
         void Update()
        {
              x = Input.GetAxis("Horizontal");
              y = Input.GetAxis("Vertical");
        }

         private void FixedUpdate()
        {
             Vector2 position = rigidbody.position;
             position.x += 0.1f * x * speed * Time.deltaTime;
             position.y += 0.1f * y * speed * Time.deltaTime;
             rigidbody.MovePosition(position);
        }

         //有關血量更改的程式碼
         public void ChangeHealth(int HCer)
        {
             CurHealth = Mathf.Clamp(CurHealth + HCer, 0, MaxHealth);
             Debug.Log("當前生命值:" + CurHealth + "/" + MaxHealth);
        }
      }
  • 設置攝像機跟隨Player移動(使用自帶腳本的的方式):
7)U2D中的精靈動畫:
  • 參考網頁教程,百度資源。

8)一些問題及解決方式:
  • 創建的角色在於環境中的一些組件發生碰撞時角色發生旋轉、抖動的問題:
    • 旋轉:在2D項目中,選中創建的角色,在Inspector面板中找到添加的Rigidbody 2D腳本,在Constrains勾選Freeze Rotation Z。

    • 抖動:引起抖動的原因可能是你在角色移動腳本里寫程式碼是利用的transform來移動角色。此時腳本移動角色的位置會和碰撞體矛盾從而引起抖動。(說白了就是你在腳本中強行將角色移到一個碰撞體的碰撞範圍內,這個碰撞體又將角色彈了回來)。解決的方法是:利用剛體的移動代替transfoem的移動方式。程式碼腳本如下:

    • //原transform移動方式見上


      using System.Collections;
      using System.Collections.Generic;
      using UnityEngine;

      public class RubyMover : MonoBehaviour
      {
         public float speed = 0.1f;
         Rigidbody2D rigidbody;
         float x;
         float y;
         // Start is called before the first frame update
         void Start()
        {
             rigidbody = GetComponent<Rigidbody2D>();
        }
         
         // Update is called once per frame
         void Update()
        {
              x = Input.GetAxis("Horizontal");
              y = Input.GetAxis("Vertical");

             

        }
      //使物理計算保持穩定,定期更新。只要你想直接影響物體組件或剛體,就要使用這個函數。
         private void FixedUpdate()
        {
             Vector2 position = rigidbody.position;
             position.x += 0.1f * x * speed * Time.deltaTime;
             position.y += 0.1f * y * speed * Time.deltaTime;
             rigidbody.MovePosition(position);
        }
      }
Tags: