2018年4月30日 星期一

【Unity官方教學分享】生成和重生

網頁連結

設定玩家生成的初始位置
目前玩家物件在遊戲中,生成及重生的位置為原點;也就是(0, 0, 0)的位置。每次遊戲開始,除非玩家移動過自己的玩家物件,否則當其他玩家進入遊戲,這些物件會重疊在同一個位置。
在理想狀況下,這些玩家物件應該要生成在不同的地方。NetworkStartPosition元件可以做到這點,因為它內建生成位置處理的功能。

接下來我們要新增兩個生成位置,首先要新增兩個帶有NetworkStartPosition元件的新物件。

  • 新增一個空物件並命名為SpawnPosition1
  • 選擇SpawnPosition1,並且...
  • 加上Network->NetworkStartPosition元件
  • 設定Position數值為(3, 0, 0)
  • 複製貼上一個SpawnPosition1,並重新命名為SpawnPosition2
  • 將SpawnPosition2的位置設為(-3, 0, 0)
  • 選擇Hierarchy視窗下的NetworkManager
  • 將 Spawn Info屬性展開
  • 將Player Spawn Method改為Round Robin
因為這兩個SpawnPosition物件下有NetworkStartPosition元件,所以NetworkManager會自動去尋找SpawnPosition,接著使用SpawnPosition的位置作為玩家連上伺服器時,物件生成的起始點。

2018年4月29日 星期日

【Unity官方教學分享】銷毀敵人物件

網頁連結

雖然敵人物件可以被子彈攻擊並且造成傷害,但是當敵人的血量歸零時,該物件應該要被銷毀。
要做到這件事,我們需要調整Health腳本,最簡單的方法是使用一個布林變數DestroyOnDeath,來調整該物件是否在死亡時銷毀。

開啟Health腳本,修改以下內容,新增布林變數DestroyOnDeath,接著用if (destroyOnDeath)判斷是否要銷毀該物件
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Networking;
using System.Collections;

public class Health : NetworkBehaviour {

    public const int maxHealth = 100;

    public bool destroyOnDeath;

    [SyncVar(hook = "OnChangeHealth")]
    public int currentHealth = maxHealth;

    public RectTransform healthBar;

    public void TakeDamage(int amount)
    {
        if (!isServer)
            return;
        
        currentHealth -= amount;
        if (currentHealth <= 0)
        {
            if (destroyOnDeath)
            {
                Destroy(gameObject);
            } 
            else
            {
                currentHealth = maxHealth;

                // called on the Server, will be invoked on the Clients
                RpcRespawn();
            }
        }
    }

    void OnChangeHealth (int currentHealth)
    {
        healthBar.sizeDelta = new Vector2(currentHealth, healthBar.sizeDelta.y);
    }

    [ClientRpc]
    void RpcRespawn()
    {        if (isLocalPlayer)
        {
            // Set the player’s position to origin
            transform.position = Vector3.zero;
        }
    }
}

【Unity官方教學分享】處理非玩家角色物件

網頁連結

目前我們的範例主要都集中在玩家物件上,但是大部分的遊戲會有非玩家物件存在於遊戲世界中。這次我們忽略環境物件,先集中在非玩家角色物件上。
玩家物件會在用戶端連上伺服器時生成,但敵人物件需要由伺服器主動生成。

在本次教學中,我們會新增一個敵人生成器(Enemy Spawner),用來創建可以被玩家攻擊的敵人物件。

  • 新增空物件命名為EnemySpawner,並在EnemySpawner上...
  • 新增Network->NetworkIdentity元件
  • 將NetworkIdentity中的Server Only設為true
將Server Only設為true,可以防止EnemySpawner發送給用戶端。

2018年4月28日 星期六

【Unity官方教學分享】死亡和重生

網頁連結

目前當玩家血量歸零時,除了在console視窗中顯示文字外,不會發生任何事,現在我們要將此範例修改的更像遊戲,當玩家的血量歸零,將會被傳送到起始位置並恢復血量。

接下來我們會使用[ClientRpc]屬性,這是另一個狀態同步工具。
加上ClientRpc的函式會從;帶有NetworkIdentity元件,並且是從伺服器生成的任意物件執行,即使在伺服器上呼叫帶有ClientRpc的函式,它也會在用戶端上執行。
先前提過的Command和ClientRpc相反,Command是在用戶端上呼叫,伺服器上執行。

要在一個函式前加上ClientRpc,要使用 [ClientRpc]變數,而且函式名稱前必須加上Rpc。此函數會在伺服器呼叫它時,於用戶端上執行。任何需要的變數會自動傳送給用戶端,更多關於[ClientRpc]變數的資訊,可以參考這裡

2018年4月27日 星期五

【Unity官方教學分享】連線時的玩家血量

網頁連結

玩家的血量狀態在伺服器上做變化,之後再讓這些更動給所有用戶端,這稱作伺服器權限(Server Authority),更多關於伺服器權限的資訊可以點這裡
為了讓我們的目前血量和傷害系統能夠在伺服器權限下運作,需要使用SyncVars這個特殊變數,讓各個數值可以同步於網路環境。此變數在腳本中寫做[SyncVar],更多關於SyncVars的資訊可以點這裡

首先開啟Health腳本,修改成以下內容,新增命名空間UnityEngine.Networking,把繼承改成NetworkBehaviour,在currentHealth前加上[SyncVar],最後增加if (!isServer)判斷式讓TakeDamage函式只會在伺服器上執行:
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Networking;
using System.Collections;

public class Health : NetworkBehaviour 
{

    public const int maxHealth = 100;

    [SyncVar]
    public int currentHealth = maxHealth;
    public RectTransform healthBar;

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

        healthBar.sizeDelta = new Vector2(currentHealth, healthBar.sizeDelta.y);
    }
}
編譯後可以進行測試,測試連線時結果的方法點我

2018年4月26日 星期四

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

網頁連結

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

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

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

2018年4月25日 星期三

【工具分享】Excel轉JSON

今天在研究Unity如何讀取Excel時不小心找到的工具;Excel轉JSON的程式,雖然跟一開始的目的不同,但感覺之後會用的到,就當作分享順便做個紀錄。
以下是原作者使用教學+下載連結:
https://neil3d.github.io/app/excel2json.html
最新版本是v1.1.1,點擊excel2json-111.zip連結下載,解壓縮後執行excel2json.exe就可以使用,介面如下:
※5月3日補充:
Export Type:輸出類型,有Array和Dict Object
Encoding:輸出JSON檔的文字編碼
Low case:是否要轉換小寫
Header:留空行數,預設最小是2。(鍵值名稱固定第一行,變數類型固定第二行)

2018年4月24日 星期二

【Unity官方教學分享】新增多人連線時的射擊功能

網頁連結

在本教學中將在子彈物件上新增網路功能,我們必須對Bullet prefab及腳本做更新。
首先,Bullet prefab需要NetworkIdentity元件讓網路系統能辨識它,接著需要NetworkTransform同步移動和旋轉,最後必須在NetworkManager的Spawnable Prefab上註冊。
  • 在project視窗下點擊Bullet prefab
  • 新增元件Network->NetworkIdentity
  • 新增元件Network->Network Transform
  • 將Network Transform下的Network Send Rate設為0
子彈在射出後並不會改變方向或速度,所以不需要做移動資訊的更新,每個用戶端可以確實地計算出子彈位置,透過將Network Send Rate設為0,可以讓網路資料流量變少以達到提高效能的結果。

2018年4月21日 星期六

【Unity官方教學分享】射擊(單機)

網頁連結


多人連線遊戲的一個常見特徵是玩家可以發射子彈,並且讓這些子彈在遊戲中的每一個用戶端上執行。
本次的教學將會增加射擊功能,但是會先製作單機版本,非連線模式,下一篇的教學才會包含網路環境下的射擊。

  • 新增一個Sphere物件
  • 將它重新命名為Bullet
  • 選擇Bullet物件,修改scale數值為(0.2, 0.2, 0.2)
  • 在Bullet下新增元件Physics->Rigidbody
  • 將Rigidbody下的Use Gravity設為false
  • 拖曳Bullet至Project視窗下的prefab資料夾,將Bullet設為prefab物件
  • 刪除場經中的Bullet

PlayerController腳本先在需要新增射擊功能,此腳本需要參考Bullet prefab,並由一段程式碼完成射擊功能。
添加Bullet prefab的GameObject變數:
public GameObject bulletPrefab;
添加Bullet生成位置變數:
public Transform bulletSpawn;
在Update中添加以下的判斷式:
if (Input.GetKeyDown(KeyCode.Space))
{
    Fire();
}
新增一個Fire函式來發射子彈:
void Fire()
{
    // Create the Bullet from the Bullet Prefab
    GameObject bullet = (GameObject)Instantiate (
        bulletPrefab,
        bulletSpawn.position,
        bulletSpawn.rotation);

    // Add velocity to the bullet
    bullet.GetComponent<Rigidbody>().velocity = bullet.transform.forward * 6;

    // Destroy the bullet after 2 seconds
    Destroy(bullet, 2.0f);
}

【Unity官方教學分享】識別本地玩家物件

網頁連結

目前各個玩家物件看起來是相同的,這會讓玩家無法辨識哪個角色是自己的,為了讓玩家可以辨識自己的角色,我們將玩家所屬的物件變為藍色。

  • 開啟PlayerController腳本
  • 新增OnStartLocalPlayer函式來改變玩家物件的顏色
using UnityEngine;
using UnityEngine.Networking;

public class PlayerController : NetworkBehaviour
{
    void Update()
    {
        if (!isLocalPlayer)
        {
            return;
        }

        float x = Input.GetAxis("Horizontal") * Time.deltaTime * 150.0f;
        float z = Input.GetAxis("Vertical") * Time.deltaTime * 3.0f;

        transform.Rotate(0, x, 0);
        transform.Translate(0, 0, z);
    }

    public override void OnStartLocalPlayer()
    {
        GetComponent<MeshRenderer>().material.color = Color.blue;
    }
}
這個函式只能由用戶端上的LocalPlayer呼叫,所以只會看到自己的玩家物件變成藍色,如果某功能只適用於本機,那麼OnStartLocalPlayer函式是做初始化的好地方,如攝影機設定或各種輸入等等。NetworkBehaviour類別中還有許多有用的虛擬函式,更多資訊可以參考這裡這裡

完成後像上一篇教學一樣啟動兩邊的實例測試結果,我們可以看到由本機控制的角色變為藍色,而另一個角色則保持白色


上一篇:測試多人連線的移動
下一篇:射擊(單機)

建立一個簡單的多人連線範例

2018年4月20日 星期五

【Unity官方教學分享】測試多人連線的移動

網頁連結

為了測試多人連線的移動,我們必須再一次同時啟動兩個實例,從Unity編譯環境下執行的實例,會隨著接下來的教學內容做更新,但已經輸出成遊戲的那一方記得要重新建置一次。
  • 點擊File->Build Settings,選擇PC, Mac....,按下Build And Run輸出檔案並執行
  • 點擊LAN Host按鈕,以主機端身分開始遊戲

接著回到Unity編譯環境:
  • 執行Unity
  • 點擊LAN Client按鈕,以用戶端身分開始遊戲

這時在Hierchy視窗應該會看到兩個玩家物件,一個是主機端的玩家,定一個則是用戶端的玩家,它們都是由同一個物件複製後生成,所以名稱都為Player(clone)。
這時可以使用WASD移動用戶端和主機端的玩家物件,並查看兩邊的移動結果,應該可以發現,現在玩家物件的移動已經是同步狀態。

2018年4月19日 星期四

【Unity】非固定式虛擬方向鍵製作Part.1

這次來談談如何製作非固定式的虛擬方向鍵,完成後結果會像這樣:
首先我們開啟一個空白的Unity專案,Hierarchy視窗中生成一個空物件,並重新命名為Controller,並在這個Controller下新增一個名為Controller的腳本(本文使用C#)。

【Unity官方教學分享】連線時玩家物件的移動腳本

網頁連結


要讓網路系統能知道玩家物件的移動,並且確定只有本機的玩家才能操控自己的物件,我們需要更新PlayerController腳本。
在PlayerController腳本中,我們將會做兩個重大的改變:首先需要新的命名空間UnityEngine.Networking,接著要將PlayerController的繼承從MonoBehaviour改為NetworkBehaviour 。
開啟PlayerController腳本,並新增紅字的部分,除了上面提到的兩點外,我們還需要在執行Update時做一個是否為本機玩家的判斷,也就是if(!isLoacalPlayer)這個判斷式。
using UnityEngine;
using UnityEngine.Networking;

public class PlayerController : NetworkBehaviour
{
    void Update()
    {
        if (!isLocalPlayer)
        {
            return;
        }

        float x = Input.GetAxis("Horizontal") * Time.deltaTime * 150.0f;
        float z = Input.GetAxis("Vertical") * Time.deltaTime * 3.0f;

        transform.Rotate(0, x, 0);
        transform.Translate(0, 0, z);
    }
}

2018年4月18日 星期三

【Unity官方教學分享】測試連線時玩家物件的移動腳本

網頁連結

測試玩家物件在主機(Host)的移動
在上一篇中,我們讓玩家物件可以在單機的環境下移動,但在連線的狀況下,是無法有正確的結果。
首先執行Unity,在執行狀態下,NetworkManagerHUD會顯示以下的使用者介面,點擊LAN Host按鈕,遊戲就會將該玩家視為主機開始遊戲。

接下來NetworkManager會使用註冊的player prefab,生成一個新的玩家物件,這時NetworkManagerHUD會顯示出伺服器是正在運作的。
主機(Host)表示伺服器和用戶端在同一個程序,更多資訊可以參考這裡
接著可以使用WASD鍵或方向鍵做角色的移動和旋轉,按下介面中的Stop按鈕會回復成離線狀態,在主機端的移動測試到這裡就完成了。

【Unity官方教學分享】製作玩家物件的移動腳本

網頁連結

我們將在本教學中製作第一個像是遊戲的功能;在場景中移動玩家物件,這裡會用名為PlayerController的腳本來做到這一點。
PlayerController腳本在這次的教學中不會有任何關於網路的程式碼,所以這次的功能只能在單機的環境下執行。
  • 在Player prefab下新增名為PlayerController的腳本
  • 開啟PlayerController腳本並刪除所有內容
  • 將以下的腳本複製貼上到PlayerController中

using UnityEngine;

public class PlayerController : NetworkBehaviour
{
    void Update ()
    {
        float x = Input.GetAxis("Horizontal") * Time.deltaTime * 150.0f;
        float z = Input.GetAxis("Vertical") * Time.deltaTime * 3.0f;

        transform.Rotate(0, x, 0);
        transform.Translate(0, 0, z);
    }
}
這個PlayerController腳本可以讓玩家控制畫面中的角色物件。
在預設的狀況下,Input.GetAxis(“Horizontal”)和Input.GetAxis(“Vertical”)讓玩家可以使用WASD和方向鍵輸入數值,來達到移動的效果,關於Input的更多資訊可以看這裡
最後編譯此腳本就完成角色的單機移動,可以試著把Player prefab拖曳到場景中,並執行Unity測試看看移動結果。
(測試完記得刪除場景中的Player prefab)

上一篇:註冊玩家預制物件
下一篇:測試連線時玩家物件的移動腳本

建立一個簡單的多人連線範例

2018年4月17日 星期二

【Unity官方教學分享】註冊Player prefab

網頁連結

要讓Player prefab能夠生成,就必須在網路系統上註冊。
Network Manager,也就是網路管理器,可以使用prefab來生成新的玩家物件,但在生成之前需要將prefab註冊在網路管理器中。

  • 選擇Hierarchy視窗下的NetworkManager物件
  • 在Network Manager元件下找到Spawn Info屬性並將它展開
  • 將預制物件拖曳上去

NetworkManager元件用於管理網路上的物件(也包含玩家操控的物件),由於大多數的遊戲都是以相同的prefab給所有玩家使用,所以NetworkManager特別為了玩家物件保留一個欄位,當玩家從客戶端登入伺服器時,就會生成該欄位的物件,也就是我們這次拖曳上去Player prefab。
關於NetworkManager的更多資訊,可以參考這裡

上一篇:建立Player prefab

【Unity官方教學分享】建立Player prefab

網頁連結

在本範例中所製作的Player prefab,將會在之後的教學中,成為使用者進入遊戲後操控的角色。
在預設的情況下,NetworkManager元件會幫每一個連上此網路遊戲的玩家,複製一個Player prefab並生成在遊戲中。網路的生成,及經由用戶端及伺服器同步玩家物件資料這兩件事,將在下一篇作介紹。
※補充:在Unity中,使用Instantiate()生成物件稱為"spawning",而在網路HLAPI中"spawn"一詞有更明確的定義,更多細節請看這裡,上面所提的"生成"可能無法完全的表達原意,請見諒。

在本範例中,我們會使用一個Capsule物件,加上辨識方向的"太陽眼鏡",作為Player prefab。製作完成後它在場景中大致上是長這樣:

【Unity官方教學分享】網路管理器

網頁連結

在本教學中,我們將會新增一個網路管理器。網路管理器將會控制這個多人連線專案的狀態;包含遊戲狀態管理、生成管理、場景管理、匹配及允許存取除錯資訊。
如果你是進階使用者,也可以繼承網路管理器衍生出子類別,自訂出新的功能元件,但這個部分本次的教學不會提到。

要生成新的網路管理器,我們需要新增一個物件,然後在該物件下新增NetworkManager和NetworjManagerHUD這兩個元件。

  • 新增一個空物件(Create Empty)
  • 將該物件重新命名為NetworkManager
  • 點選NetworkManager物件,在該物件下...
  • 新增Network > NetworkManager
  • 新增Network > NetworkManagerHUD

NetworkManager元件是用來管理網路狀態。
NetworkManagerHUD和NetworkManager配合使用,提供簡單的使用者介面,以便在遊戲執行時控制網路狀態。


現在可以試著執行看看Unity,NetworkManagerHUD應該會顯示以下的介面,另外關於NetworkManagerHUD和NetworkManager的更多細節,可以參照這裡

2018年4月16日 星期一

【Unity官方教學分享】多人連線範例簡介

網頁連結

多人連線技術本身是精密而且複雜的,多人連線通常會在不一樣的裝置上運行,而且它們可能位在世界上各個不同的地方,所以在同步和傳遞資料上有特殊的問題和困難。
在Unity中有內建的多人連線而且是高階的API,我們希望使用者在建立多人連線的專案時,可以輕鬆地使用它。
透過這個範例,我們將示範如何使用簡單的物件和腳本,建立一個多人連線專案。我們希望能在這個範例中,可以快速介紹Unity內建的多人連線系統和高階API的重要部分。
接下來將一步一步地示範如何使用Unity內建的多人連線系統和高階API,從頭開始建立一個多人連線的專案。本教學的步驟較為通用,目的在於傳達多人連線的基本概念,同時可用於各種不同的連線遊戲。
當本範例完成時,我們可以讓兩個玩家在不同的裝置中,操作自己的遊戲角色,而且伺服器會同步他們的操作結果,玩家應該可以互相攻擊對方或NPC角色,而當自己的角色死亡時,角色會在重生點復活。
本教學適合中階使用者,開始前最好稍微了解多人連線的內容,特別是網路概述高階API的部分。

下一篇:網路管理器

建立一個簡單的多人連線範例

2018年4月1日 星期日

非固定式虛擬方向鍵移動測試

其實我也不知道這東西的正式名稱叫什麼,總之就是之前任天堂告可樂普的那個。
因為一直對手機遊戲上人物移動操控頗有興趣,而且這方法應該可以用在不少東西上,就來實作看看了,效果就和白貓的移動相同。
另外要如何這篇不會提到要如何實作,只是結果的展示,製作過程下一篇再開始。
如上圖所示,用滑鼠或手指按著螢幕中任意位置然後拖曳,畫面中的角色就會朝著拖曳的方向移動。
而畫面中的兩個圓是UI的Image,人物則是2D物件,另外有針對解析度做移動速度的調整以及對移動速度最大值做限制,大概就是這樣,這次就到這裡。
任天堂不要告我。

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

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