网络ID对象
NetworkIDObject 和NetworkIDManager类允许使用普通的ID查询指针
NetworkIDOjbect类是一个可选类,可以将自己的类从这个类派生,那么你的类就自动赋值标识数字(NetworkID)。这种方法对于多玩家游戏特别有用,否则你必须有自己的方法动态的访问远端系统上分配的对象。
在RakNet 4中,NetworkID是8字节长的全局唯一数字,随机选择。旧版本的RakNet要求中心授权者(服务器)赋值NetworkIDs。这种方法已经废弃了,因为如果游戏是端到到(p2p)的形式,或者有多个分布式的服务器存在,那么编程人员创建这个ID号就非常困难。如果客户端要创建一个对象,也要增加额外的很多工作。因为客户端必须先赋值一个临时的ID,然后从服务器请求真实的NetworkID,然后才可以将它赋值给这个客户端创建的对象。
NetworkIDObject类提供了如下的函数:
SetNetworkIDManager( NetworkIDManager * manager)
NetworkIDManager保存了一个NetworkID的列表用于查询。因此,这就要求你在调用GetNetworkID()或SetNetworkID() 之前调用SetNetworkIDManager()。List不能简单设置为静态的,原因是你或许想要多个NetworkIDManager,例如如果你想要启动多个游戏,它们之间没有任何的交互,如果设置为静态,就会出现错误。
NetworkID GetNetworkID(void)
如果 SetNetworkID() 在前面调用了,这个函数就会返回NetworkID值。否则它为对象生成一个新的,推测是唯一的NetworkID。本质上说,对象只有在调用了GetNetworkID()时才会真正为这个对象赋值一个NetworkID。在客户端/服务器应用下,如果所有的对象依旧是由服务器创建的,那么就不需要客户端生成NetworkID。
SetNetworkID( NetworkID id)
给对象赋值一个NetworkID。用到这个方法的例子就是服务器创建新的游戏对象,广播对象的数据到客户端。客户端会创建一个相同时间的类,读取在用户消息中编码的NetworkID,在同一个对象上调用SetNetworkID()方法。
NetworkIDManager类仅仅有一个用户函数:
template < class returnType>
returnType GET_OBJECT_FROM_ID(NetworkID x);
这是一个模板函数,因此你可以如下一样写代码:
Solider * solider = networkIDManager.GET_OBJECT_FROM_ID<Solider *>(networkId);
如下是一个将指针存储到类的一个例子,重新检索出来,使用Assert确保有效工作(不出现错误):
class Solider : public NetworkIDObject{}
int main(void)
{
NetworkIDManager networkIDManager;
Solider * solider = new Solider;
solider->SetNetworkIDManager(&networkIDManager);
NetworkID soliderNetworkID = solider->GetNetworkID();
Assert(networkIDManager.GET_OBJECT_FROM_ID<Solider ->(soliderNetworkID)== solider);
}
如下是一个例子,使用系统创建一个远端系统上的对象,将同一个ID值赋给两者:
Server:
void CreateSoldier(void)
{
Soldier *soldier = new Soldier;
soldier->SetNetworkIDManager(&networkIDManager);
RakNet::BitStream bsOut;
bsOut.Write((MessageID)ID_CREATE_SOLDIER);
bsOut.Write(soldier->GetNetworkID());
rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,UNASSIGNED_SYSTEM_ADDRESS,true);
}
Client:
Packet *packet = rakPeerInterface->Receive();
if (packet->data[0]==ID_CREATE_SOLDIER)
{
RakNet::BitStream bsIn(packet->data, packet->length, false);
bsIn.IgnoreBytes(sizeof(MessageID));
NetworkID soldierNetworkID;
bsIn.Read(soldierNetworkID);
Soldier *soldier = new Soldier;
soldier->SetNetworkIDManager(&networkIDManager);
soldier->SetNetworkID(soldierNetworkID);
}
静态对象:
有时候对象并不是动态创建的,而是在所有的系统上都已经存在了,所有的系统提前都知道了。例如,如果你在标记地图上有一个获取物,带有三个标记,这三个标记或许是硬编码进关卡设计中的,因此当游戏加载关卡信息的时候,这些数据就直接加载进了游戏。这些是静态对象,NetworkIDManager也是可以访问的。使得这些对象从NetworkIDObject对象派生出来,如同创建动态对象一样,调用SetNetworkIDManager。然后只需要给你的对象赋值一个唯一的ID即可。ID是什么并不重要,只要它是唯一的就好,那么你可以给flag1赋值ID 0,flag2 赋值 ID 1 以及给flag 3 赋值ID 2。这都没有问题。
// 所有的NetworkID在这里增加
enum StaticNetworkIDs
{
CTF_FLAG_1,
CTF_FLAG_2,
CTF_FLAG_3,
};
class Flag : public NetworkIDObject
{
// 关卡设计者给标记命名flag1, flag2, 或flag3都可以,
// 地图在其他系统也是以相似方式加载
Flag(std::string flagName, NetworkIDManager *networkIDManager) {
SetNetworkIDManager(networkIDManager);
if (flagName=="flag1")
SetNetworkID(CTF_FLAG_1);
else if (flagName=="flag2")
SetNetworkID(CTF_FLAG_2);
else if (flagName=="flag3")
SetNetworkID(CTF_FLAG_3);
};
By 北洋小郭
转载请注明出处,切勿用于商业。谢谢!