使用Multiplayer Networking做一个简单的多人游戏例子-3/3(Unity3D开发之二十七)

时间:2022-09-14 17:35:14

使用Multiplayer Networking做一个简单的多人游戏例子-1/3

使用Multiplayer Networking做一个简单的多人游戏例子-2/3

使用Multiplayer Networking做一个简单的多人游戏例子-3/3

上一篇中血条还没有同步到所有客户端,下面添加血条同步。主要用到[SyncVar]同步变量。

13. 网络同步血条

  • 打开Health脚本
  • 添加命名空间UnityEngine.Networking
using UnityEngine.Networking;
  • 添加继承关系为NetworkBehaviour
public class Health : NetworkBehaviour
  • 为currentHealth添加[SyncVar]
[SyncVar]
public int currentHealth = maxHealth;
  • 在TakeDamage函数中增加仅在Server端执行
if (!isServer)
{
    return;
}

最终Health脚本如下:

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);
    }
}

此时运行测试,你会发现只有客户端的血条会变化,所以我们需要修改血条变化方法

  • 打开Health脚本
  • 将血条修改的代码移到新函数OnChangeHealth中
void OnChangeHealth (int currentHealth)
{
    healthBar.sizeDelta = new Vector2(health, currentHealth.sizeDelta.y);
}
  • 修改currentHealth的SyncVar属性
[SyncVar(hook = "OnChangeHealth")]

最终Health代码:

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 = 0;
            Debug.Log("Dead!");
        }
    }

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

14. 死亡和再出生

这里会用到[ClientRpc],让函数在服务端调用,执行却在客户端

  • 打开Health脚本
  • 添加出生函数Respawn
[ClientRpc]
void RpcRespawn()
{
    if (isLocalPlayer)
    {
        // move back to zero location
        transform.position = Vector3.zero;
    }
}
  • 修改TakeDamage函数中,currentHealth = maxHealth;
  • 在服务端执行RpcRespawn();

    最终Health代码如下:
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;
        }
    }
}
  • 保存脚本
  • Build新版本,测试

15. 增加敌人

增加敌人发射器用于创建系统玩家

  • 创建一个empty GameObject
  • 修改名称为“Enemy Spawner”
  • 保持Enemy Spawner选中
  • 添加组件Network > NetworkIdentity
  • 勾选NetworkIdentity中the Server Only选中,仅在服务端中执行
  • 给Enemy Spawner添加新脚本“EnemySpawner”

    EnemySpawner内容如下:
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);
        }
    }
}
  • 保存脚本
  • 回到Unity

下面开始利用Player Prefab创建Enemy

  • 将Player prefab拖到场景中
  • 修改Player名称为“Enemy”
  • 将Enemy拖到Project面板中创建一个新的Enemy prefab预制体
  • 选中Enemy
  • 删除Enemy中的Gun,点击继续
  • 选中Enemy
  • 删除Bullet Spawn
  • 移除PlayerController组件
  • 修改Enemy材质为Black Material
  • 修改Enemy Visor的材质为Default-Material
  • Apply Enemy Prefab
  • 删除场景中的Enemy Prefab
  • 保存场景

将 Enemy prefab 注册到NetworkManager中

  • 选中NetworkManager
  • 展开Spawn Info foldout
  • Registered Spawnable Prefabs点击+号
  • 将Enemy prefab添加到Registered Spawnable Prefabs list
  • 选中场景中的Enemy Spawner
  • 将Enemy prefab拖到Enemy prefab输入框中
  • 设置Number Of Enemies为4

    使用Multiplayer Networking做一个简单的多人游戏例子-3/3(Unity3D开发之二十七)
  • 保存工程

build新版本,测试

16. 销毁死亡Enemy

  • 打开Health脚本
  • 添加destroyOnDeath变量
  • TakeDamage中判断销毁或者respawning

    代码如下:
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 prefab
  • 将Health中Destroy On Death 属性勾选

    使用Multiplayer Networking做一个简单的多人游戏例子-3/3(Unity3D开发之二十七)

增加出身点位置

由于Player每次出生位置一样,我们用NetworkStartPosition来增加一个出身点

  • 创建一个empty GameObject
  • 修改名称为“Spawn Position 1”
  • 选中Spawn Position 1
  • 增加组件Network > NetworkStartPosition
  • 设置Transform Position (3, 0, 0)
  • 复制一份Spawn Position 1 GameObject
  • 将复制这份修改名称为“Spawn Position 2”
  • 选中Spawn Position 2
  • 设置Transform Position (-3, 0, 0)
  • 选中Network Manager
  • 展开Spawn Info foldout
  • 修改Player Spawn Method 为 Round Robin

设置出生点位置

  • 打开Health脚本
  • 增加NetworkStartPosition变量
private NetworkStartPosition[] spawnPoints;
  • 增加Start方法
void Start ()
{
    if (isLocalPlayer)
    {
        spawnPoints = FindObjectsOfType<NetworkStartPosition>();
    }
}
  • 修改RpcRespawn中出生点坐标

    最终Health代码如下:
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;
        }
    }
}

使用Multiplayer Networking做一个简单的多人游戏例子-3/3(Unity3D开发之二十七)

Download sample code on github

OK,以上就是全部内容。关于详细的使用请看 Networking Overview