2018年4月26日 星期四

【Unity官方教學分享】玩家血量(單機)

網頁連結

同步玩家狀態是多人連線中一個重要的概念。
這次的教學中,我們將在子彈碰撞到玩家物件時,讓玩家的血量數值減少,而當前血量也屬於玩家狀態之一,所以我們也要對血量數值做同步。

新增子彈碰撞
讓子彈能影響目標物的第一步是新增能處理碰撞的元件,在本範例中,我們會在子彈碰到其他碰撞體時,將子彈銷毀。
  • 在Bullet物件下新增名為Bullet的腳本
  • 開啟此腳本,並刪除所有內容
  • 新增碰撞時的處理函式,內容是碰到其他碰撞體時,將自己銷毀
using UnityEngine;
using System.Collections;

public class Bullet : MonoBehaviour 
{
    
    void OnCollisionEnter()
    {
        Destroy(gameObject);
    }
}
編譯過Bullet腳本後,可以做第一次的測試,測試連線時結果的方法點我
現在當子彈會在碰撞時銷毀,而且子彈被NetworkManager管理,當子彈在伺服器中被銷毀時,所有用戶端看到的那顆子彈也會被銷毀。

玩家血量
要新增單機版本的血量,我們需要一個新的腳本來追蹤並顯示當前的血量。
  • 在Player prefab下新增名為Health的腳本
  • 開啟此腳本,並刪除所有內容
  • 新增以下內容,最大血量maxHealth,當前血量currentHealth,以及受到攻擊的函式TakeDamage
using UnityEngine;

public class Health : MonoBehaviour 
{
    public const int maxHealth = 100;
    public int currentHealth = maxHealth;

   public void TakeDamage(int amount)
   {
        currentHealth -= amount;
        if (currentHealth <= 0)
        {
            currentHealth = 0;
            Debug.Log("Dead!");
        }
   }
}
在Bullet物件碰撞到玩家物件時,需要呼叫TakeDamage,因此我們要對Bullet腳本做更新,在碰撞呼叫OnCollisionEnter時,需要一個collision變數存取那一個被子彈碰到的對象,之後要呼叫該對象Health腳本中的TakeDamage。
using UnityEngine;
using System.Collections;

public class Bullet : MonoBehaviour {
    
    void OnCollisionEnter(Collision collision)
    {
        GameObject hit = collision.gameObject;
        Health health = hit.GetComponent<Health>();
        if (health  != null)
        {
            health.TakeDamage(10);
        }

        Destroy(gameObject);
    }
}
現在子彈能對玩家的生命值造成傷害,但並不會顯示在畫面上,因此接下來我們要在玩家物件上新增簡易的血條。

新增簡易血條
為了做到此功能,我們需要新增簡單的UI物件,要注意的是,接下來的方法並不是一個好做法,但可以讓本範例在簡單的幾個步驟內就完成此功能。
  • 新增UI->Image(同時也會新增Canvas和EventSystem)
  • 將Canvas重新命名為HealthbarCanvas
  • 將Image重新命名為Background
  • 設定Background的數值...
  • RectTransform下的Width為100
  • RectTransform下的Height為10
  • Image下的Source Image設為InputFieldBackground
  • Image下的Color為紅色
  • 複製貼上一個Background並重新命名為Foreground
  • 讓Foreground成為Background的子物件
  • 將Player prefab拖曳至Hierchy
  • 讓HealthbarCanvas成為Player的子物件,最後會像這樣:
  • 選取Foreground,將Image下的Color設為綠色
  • 開啟Anchor Presets將Pivot和Position設為Middle Left
  • 接著選取HealthbarCanvas...
  • 將Render Mode預設的Overlay改為World Space
  • 點選RectTransform的齒輪reset數值
  • 將RectTransform的Scale設為(0.01, 0.01, 0.01)
  • 將RectTransform的Position設為(0.0, 1.5, 0.0)

現在要將HealthbarCanvas與前面的血量和損傷做連結,我們需要從Health腳本中改變血條圖來顯示當前血量。

開啟Health腳本,新增命名空間UnityEngine.UI,新增healthBar變數用來參照Foreground的RectTranform,最後在TakeDamage中血量減少後,將當前血量currentHealth作為healthBar的Width,如此一來就可以顯示血量減少的結果。
using UnityEngine;
using UnityEngine.UI;

public class Health : MonoBehaviour 
{
    public const int maxHealth = 100;
    public int currentHealth = maxHealth;
    public RectTransform healthBar;

   public void TakeDamage(int amount)
   {
        currentHealth -= amount;
        if (currentHealth <= 0)
        {
            currentHealth = 0;
            Debug.Log("Dead!");
        }

        healthBar.sizeDelta = new Vector2(currentHealth, healthBar.sizeDelta.y);
   }
}

  • 回到Hierarchy視窗中的Player物件,將Foreground拖曳到Healthbar變數上
  • 點選Player的apply更新Player prefab
  • 刪除Hierarchy視窗中的Player物件

最後我們需要讓血條能一直朝向攝影機。
  • 找到Player prefab下的HealthbarCanvas
  • 新增名為Billboard的腳本
  • 開啟Billboard,在Update中新增transform.LookAt(Camera.main.transform);
現在我們可以再一次測試結果,測試連線時結果的方法點我

目前,玩家物件的修改可以獨立運作於所有用戶端和伺服器上,當一名玩家射擊另一名玩家時,Bullet和Health腳本在本機上執行,並不會產生同步。

Bullet物件由NetworkManager管理,所以在碰撞產生時,會把伺服器和所有用戶端的該顆子彈給銷毀。子彈和玩家發生碰撞,玩家可能會造成傷害,但也有可能因為延遲或其他原因,在造成傷害這個判斷在發生前子彈就被銷毀,子彈同步但玩家血量狀態不同步,這個問題可能會造成各用戶端看到的當前血量完全不同,所以下一篇我們將同步玩家的血量狀態。


1 則留言:

  1. 您绝对熟悉开发人员用于开发项目的最佳编码语言 C#,并且他们可以正确回答所有查询,例如“oncollisionenter 已声明但从未使用”。

    回覆刪除

【自製小遊戲】水平思考猜謎(海龜湯)

遊戲連結 海龜湯的玩法是由出題者提出一個難以理解的事件,參與猜題者可以提出任何問題以試圖縮小範圍並找出事件背後真正的原因。但出題者僅能以「是」、「不是」或「沒有關係」來回答問題。 本遊戲蒐集各種論壇、平台的42個題目,提供給想玩海龜湯卻愁找不到題目的你們。 ...