携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第13天,点击查看活动详情
Mirror的同步容器
上篇中,我们使用[SyncVar]这个自定义属性,为单个变量添加同步功能。但有时候,我们会在脚本中使用容器来容纳一组对象,容器中的对象我们也需要同步功能,为此Mirror提供了同步容器,包括SyncList , SyncDictionary, SyncHashSet和SyncSortedSet。
SyncList
- SyncList是一个泛型容器,其内部的数据会自动同步。
- SyncList是类似于C# List那样的基于数组的列表。
- SyncList可以包含任意Mirror支持的类型。关于Mirror支持的类型,会单独总结一篇。
- SyncList必须是
readonly的,这意味着该变量在构造函数之后不能被修改,因此SyncList只能在定义时赋值或者在所属对象的构造函数中赋值。 - SyncList是和其他SyncVars一起序列化的,SyncList中的变量和其他SyncVars变量被合并到一条消息中。
使用示例
public struct Item
{
public string name;
public int amount;
}
public class Player : NetworkBehaviour
{
public readonly SyncList<Item> inventory = new SyncList<Item>();
public int coins = 100;
[Command]
public void CmdPurchase(string itemName)
{
if(coins > 10)
{
coins -= 10;
Item item = new Item
{
name = "Sword",
amout = 3;
};
inventory.Add(item);
}
}
}
这个例子中,当player执行CmdPurchase时,服务器上,该player获得了一个Item,添加到 inventory这个SyncList中,在下次同步时,所有客户端将会知道这件事。
SyncList回调
使用 SyncList.Callback这个event注册SyncList改变时的回调函数,通常在Start,OnClientStart或OnServerStart中进行注册。如果注册时,SyncList中已经有数据,你不会得到初始数据添加的回调,只有这之后再有数据更新时才会回调。因此如果想得到已有数据的更新回调,需要手动调用回调函数。
SyncList回调的示例
接着上面的Player的例子,有一个SyncList为inventory,给它注册一个回调。
class Player : NetworkBehaviour
{
public override void OnStartClient()
{
//Callback是个event,因此使用 += 注册回调函数
inventory.Callback += OnInventoryUpdated;
//处理SyncList中的已有数据,手动调用回调
for(int i=0; i < inventory.Count; i++)
{
OnInventoryUpdated(SyncList<Item>.Operation.OP_ADD, i, new Item(), inventory[index]);
}
}
void OnInventoryUpdated(SyncList<Item>.Operation op, int index, Item oldItem, Item newItem)
{
switch(op)
{
case SyncList<Item>.Opeartion.OP_ADD:
//Add操作,index是add的索引位置,newItem是add的Item
break;
case SyncList<Item>.Operation.OP_INSERT:
//Insert操作,index是插入的位置,newItem是插入的Item
break;
case SyncList<Item>.Operation.OP_REMOVEAT:
//删除操作,index是删除的位置,oldItem是被删除的Item
break;
case SyncList<Item>.Operation.OP_SET:
//设置操作,index是修改的Item的索引,oldItem是修改前的Item值,newItem是修改后的新Item值
break;
case SyncList<Item>.Operation.OP_CLEAR:
//清除操作,list被清空
break;
}
}
}
SyncDictionary
同步字典容器,当服务器上该容器内容改变时,改动会同步到各个客户端,回调会调用。只有增量被传输。 基本用法和SyncList类似,一个例子:
public struct Item
{
public string name;
public int hitPoints;
}
public class Player : NetworkBehaviour
{
public readonly SyncDictionary<string, Item> Equipment = new SyncDictionary<string, Item>();
public override void OnStartServer()
{
Equipment.Add("head", new Item { name = "Helment", hitPoints = 20});
Equipment.Add("body", new Item { name = "Armor", hitPoints = 50});
}
public override void OnStartClient()
{
Equipment.Callback += OnEquipmentChange;
//处理初始数据的回调
foreach(KeyValuePair<string, Item> kvp in Equipment)
{
OnEquipmentChange(SyncDictionary<string, Item>.Operation.OP_ADD, kvp.Key, kvp.Value);
}
}
void OnEquipmentChange(SyncDictionary<string, Item>.Operation op, string key, Item item)
{
switch(op)
{
case SyncIDictionary<string, Item>.Operation.OP_ADD:
//添加Item
break;
case SyncIDictionary<string, Item>.Operation.OP_SET:
//修改Item
break;
case SyncIDictionary<string, Item>.Operation.OP_REMOVE:
//删除Item
break;
case SyncIDictionary<string, Item>.Operation.OP_CLEAR:
//清除字典
break;
}
}
}
SyncDictionary内部数据结构
SyncDictionary默认使用Dictionary存储数据,如果需要使用其他的IDictionary实现,比如SortedList或SortedDictionary,需要使用SyncIDictionary,然后将字典容器的实例传入。例如:
public class Player : NetworkBehaviour
{
public readonly SyncIDictionary Equipment = new SyncIDictionary(new SortedList());
}
SyncHashSet
支持同步的哈希集合,定义方法示例:
public readonly SyncHashSet<string> buffs = new SyncHashSet<string>();
增加回调:
public override void OnStartClient()
{
//在OnStartClient内部注册,将添加客户端的委托
//如果想在服务器上调用委托,需要在 OnStartServer中注册
buffs.Callback += OnBuffsChanged;
//同样要处理已有数据
foreach(string buff in buffs)
{
OnBuffsChanged(SyncSet<string>.Operation.OP_ADD, buff);
}
}
void OnBuffsChanged(SyncSet<string>.Operation op, string buff)
{
//op包括 OP_ADD, OP_REMOVE, OP_CLEAR
}
SyncSortedSet
SyncSortedSet是类似于C# SortedSet的集合,具有自动同步功能。和SyncHashSets不同,SyncSortedSet在插入元素时会进行排序。 定义方法示例:
public readonly SyncSortedSet<string> buffs = new SyncSortedSet<string>();
使用回调:
public overrid void OnStartClient()
{
buffs.Callback += OnBuffsChanged;
foreach(string buff in buffs)
OnBuffsChanged(SyncSortedSet<string>.Operation.OP_ADD, buff);
}
void OnBuffsChanged(SyncSortedSet<string>.Operation op, string buff)
{
//op包括 OP_ADD, OP_REMOVE, OP_CLEAR
}