使用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
- 保存工程
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 属性勾选
增加出身点位置
由于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;
}
}
}
Download sample code on github
OK,以上就是全部内容。关于详细的使用请看 Networking Overview