Networking_FpsDemo

一、創建場景。
命名場景爲Main
二、創建NetWorkManager空對象。
並添加腳本NetWorkManagerHUD
NetWorkManager
The High Level API
三、創建遊戲同步Player三維對象。
並添腳本 NetworkIdentity,並將其 Local Player Authority參數設置爲True。將Player製作成預製體,然後將其從場景中刪除(Player Objectshttps://docs.unity3d.com/Manual/UNetPlayers.html?_ga=1.58169839.433409425.1476685957
四、將Player預製體註冊到NetWorkManager上
拖動Player到NetWorkManager的SpawnInfo的PlayerPrefab屬性上
五、爲Player添加運動操作腳本
using UnityEngine;

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

        transform.Rotate(0, x, 0);
        transform.Translate(0, 0, z);
    }
}
六、測試本地以 LAN Host啓動遊戲。
Network System Conceptshttps://docs.unity3d.com/Manual/UNetConcepts.html?_ga=1.124244047.433409425.1476685957),然後打包客戶端以client模式加入運行。
七、實現只控制本地對象。
將MonoBehaiver替換爲NetWorkBehaiver,並判斷是否是本地對象,否則返回。(NetWorkBehaiver:https://docs.unity3d.com/ScriptReference/Networking.NetworkBehaviour.html?_ga=1.56679535.433409425.1476685957)
然後在Player預製體上添加NetWorkTranform(NetWorkTransform:https://docs.unity3d.com/Manual/class-NetworkTransform.html?_ga=1.157733375.433409425.1476685957
八、多人在線測試。
修改 Network Send Rate並再次測試,觀察流暢度
九、識別本地玩家。
在腳本中添加:
public override void OnStartLocalPlayer()
{
    GetComponent().material.color = Color.blue;
}
可以在本地本地玩家創建後將其顏色轉換爲藍色(Object Spawninghttps://docs.unity3d.com/Manual/UNetSpawning.html?_ga=1.94715225.433409425.1476685957
十、創建本地射擊功能。製作子彈預製體,並添加剛體組件,修改PlayerController腳本,添加生成子彈的功能:using UnityEngine;
using UnityEngine.Networking;

public class PlayerController : NetworkBehaviour
{
    public GameObject bulletPrefab;
    public Transform bulletSpawn;

    void Update()
    {
        if (!isLocalPlayer)
        {
            return;
        }

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

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

        if (Input.GetKeyDown(KeyCode.Space))
        {
            Fire();
        }
    }


    void Fire()
    {
        // Create the Bullet from the Bullet Prefab
        var 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);        
    }

    public override void OnStartLocalPlayer ()
    {
        GetComponent<MeshRenderer>().material.color = Color.blue;
    }
},將子彈預製體和子彈生成點拖入到腳本上
十一、多人在線射擊。
在子彈上添加NetwordIdentity,NetworkTransform.並將NetWorkTranform中的NetworkSendRate
設置爲一,NetWorkIdentity上LocalPlayerAuthorit設置爲True.
此時進一步修改PlayerController腳本,實現只創建本地。
using UnityEngine;
using UnityEngine.Networking;

public class PlayerController : NetworkBehaviour
{
    public GameObject bulletPrefab;
    public Transform bulletSpawn;

    void Update()
    {
        if (!isLocalPlayer)
        {
            return;
        }

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

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

        if (Input.GetKeyDown(KeyCode.Space))
        {
            CmdFire();
        }
    }

    // This [Command] code is called on the Client …
    // … but it is run on the Server!
    [Command]
    void CmdFire()
    {
        // Create the Bullet from the Bullet Prefab
        var bullet = (GameObject)Instantiate(
            bulletPrefab,
            bulletSpawn.position,
            bulletSpawn.rotation);

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

        // Spawn the bullet on the Clients
        NetworkServer.Spawn(bullet);

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

    public override void OnStartLocalPlayer ()
    {
        GetComponent<MeshRenderer>().material.color = Color.blue;
    }
}
十二,本地人物血量。
添加子彈邏輯代碼,實現碰撞後銷燬子彈
using UnityEngine;
using System.Collections;

public class Bullet : MonoBehaviour {
   
    void OnCollisionEnter()
    {
        Destroy(gameObject);
    }
}

添加人物血量腳本,並有受到攻擊掉血的方法。
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!");
        }
    }
}
當子彈和人物發生碰撞時,需要調用人物Heath腳本的TackDamage方法,修改子彈腳本爲:
using UnityEngine;
using System.Collections;
public class Bullet : MonoBehaviour
{
    void OnCollisionEnter(Collision collision)
    {
        var hit = collision.gameObject;
        var health = hit.GetComponent<Health>();
        if (health != null)
        {
            health.TakeDamage(10);
        }
        Destroy(gameObject);
    }
}
添加世界座標中血量條,增加一個3維canvas,作爲Player的子物體,命名爲HealthBarCanvas.將大小設置爲(0.01,0.01,0.01)座標設置爲(0,1.5,0).添加兩個Image分別命名爲Background和Foreground,顏色設置爲紅色和藍色,設置父子關係,將Forground的中心點設置爲(0,0.5)錨點設置爲(0,0.5)(0,0.5)。
修改Health腳本更新血量顯示
using UnityEngine;
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);
    }
}
記得將血條foreground賦予腳本。
血條需要看到攝像機才行,在血條上添加腳本Billboard
using UnityEngine;
using System.Collections;

public class Billboard : MonoBehaviour {

    void Update () {
        transform.LookAt(Camera.main.transform);
    }
}
此時由於血量沒有進行同步,子彈雖然在每個客戶端裏者存在,但由於碰撞的不同血量可能會完全不同
十三、血量同步
修改Health腳本修改爲掉血只在服務端運行,然後通過    [SyncVar]  來進行變量同步到客戶端。

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);
    }
}
當作服務端的客戶端運行時可以看到血量的變化,因爲currentHealth雖然同步了,但並沒有觸發血量變化的事件。利用 SyncVar hook 可以在變量發生變化後,同步到UI.
Health腳本修改爲如下 :
十四,重生
在服務端調用 ClientRpc 同步到客戶端(和Command相反)
實現當血量小於0時,在原點滿血復活效果(如果服務端僅僅將某個Player重置到原點,客戶端會因爲NetWorkTransform重寫這個Player的座標)

using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Networking;
using System.Collections;
public class Health : NetworkBehaviour
{
    public const int maxHealth = 100;
    [SyncVar(hook = "OnChangeHealth")]
    public int currentHealth = maxHealth;
    public RectTransform healthBar;
    public void TakeDamage(int amount)
    {
        if (!isServer)
            return;
        currentHealth -= amount;
        if (currentHealth <= 0)
        {
            currentHealth = maxHealth;
            // called on the Server, but invoked on the Clients
            RpcRespawn();
        }
    }
    void OnChangeHealth(int currentHealth)
    {
        healthBar.sizeDelta = new Vector2(currentHealth, healthBar.sizeDelta.y);
    }
    [ClientRpc]
    void RpcRespawn()
    {
        if (isLocalPlayer)
        {
            // move back to zero location
            transform.position = Vector3.zero;
        }
    }
}

十四,非Player對象的同步
非Player的遊戲對象由Server來控制,下面以ai敵人爲例。
創建一個EnemySpawner空物體,NetworkIdentity 設置 Server Only爲True,添加EnemySpawner 腳本來隨機生成敵人 重寫 OnStartServer方法
using UnityEngine;
using UnityEngine.Networking;
public class EnemySpawner : NetworkBehaviour
{
    public GameObject enemyPrefab;
    public int numberOfEnemies;
    public override void OnStartServer()
    {
        for (int i = 0; i < numberOfEnemies; i++)
        {
            var spawnPosition = new Vector3(
                Random.Range(-8.0f, 8.0f),
                0.0f,
                Random.Range(-8.0f, 8.0f));
            var spawnRotation = Quaternion.Euler(
                0.0f,
                Random.Range(0, 180),
                0.0f);
            var enemy = (GameObject)Instantiate(enemyPrefab, spawnPosition, spawnRotation);
            NetworkServer.Spawn(enemy);
        }
    }
}
製作敵人,將場景中的Player去掉PlayerController並適當修改造型後,拖回到Project面版中並命名爲Enemy。
然後將Enemy賦予EnemySpawner並註冊到NetWorkManager中的SpawnInfo的Spanwnableprefab中(注意Enemy的NetWorkIdentity還是需要選擇LocalPlayerAuthority,才能同步到每個場景中)
十五、銷燬敵人
攻擊敵人到血量爲0是,要上敵人銷燬,只需要將Health做適當修改:(提供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;
        }
    }
}
十六、生成與再生成
遊戲對象生成點的控制可以利用 NetworkStartPosition 來實現,只需要在從預先設置好座標點,當Player生成時,就可以選擇性的在這些點之間進行生成。
同時,NetworkManager身上有 Random and Round Robin兩和生成方式,分別是隨機在生成點之間進行生成,和順序在這些點之間生成。
當需要重新設置生成點,則需要用用戶自行找到生成點。
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;
    private NetworkStartPosition[] spawnPoints;
    void Start()
    {
        if (isLocalPlayer)
        {
            spawnPoints = FindObjectsOfType<NetworkStartPosition>();
        }
    }
    public void TakeDamage(int amount)
    {
        if (!isServer)
            return;
        currentHealth -= amount;
        if (currentHealth <= 0)
        {
            if (destroyOnDeath)
            {
                Destroy(gameObject);
            }
            else
            {
                currentHealth = maxHealth;
                // called on the Server, invoked on the Clients
                RpcRespawn();
            }
        }
    }
    void OnChangeHealth(int currentHealth)
    {
        healthBar.sizeDelta = new Vector2(currentHealth, healthBar.sizeDelta.y);
    }
    [ClientRpc]
    void RpcRespawn()
    {
        if (isLocalPlayer)
        {
            // Set the spawn point to origin as a default value
            Vector3 spawnPoint = Vector3.zero;
            // If there is a spawn point array and the array is not empty, pick one at random
            if (spawnPoints != null && spawnPoints.Length > 0)
            {
                spawnPoint = spawnPoints[Random.Range(0, spawnPoints.Length)].transform.position;
            }
            // Set the player’s position to the chosen spawn point
            transform.position = spawnPoint;
        }
    }
}
發佈了32 篇原創文章 · 獲贊 12 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章