游戏开发这个领域相对比较神秘一些,会用一些技巧性的编程技术,当然也不难。
今天我们就来实现游戏开发领域中的读档与存档功能,并剖析其中的技术点:
1、首先我们要知道结构体0数组元素的用途。建议看这篇博客:
https://blog.csdn.net/Think88666/article/details/89302555
2、new和malloc的本质区别,博客:
https://blog.csdn.net/Think88666/article/details/89684582
3、具体实现方法
其实就是将玩家对象序列化成二进制,然后发送到存档服务器保存起来。当然实现方法有很多种,但重要的是稳定与高效
以下是我手动实现序列化玩家对象的代码:
//一次存档数据结构
struct SerializeBinary { DWORD nVersion; //版本 用处详见 https://blog.csdn.net/Think88666/article/details/90523017 DWORD nDataSize; //data的大小 SerializeBinary():nVersion(0),nDataSize(0){} DWORD size() { return sizeof(SerializeBinary) + nDataSize; } char data[0]; //玩家对象的所有数据 }; //数据块 保存玩家每一个属性的值 struct BinaryBlock { DWORD nType; //块的类型 DWORD nDataSize; //块大小 BinaryBlock() :nType(0), nDataSize(0) {} DWORD size() { return sizeof(BinaryBlock) + nDataSize; } char data[0]; //块的实际内容 };
实现思路是我们把每个玩家属性转化成BinaryBlock结构体,再通过内存拷贝存入到SerializeBinary.data中去,并通过SerializeBinary.nDatasize记录所有的BinaryBlock的大小,而每个BinaryBlock又有自己的大小nDataSize,这样我们便可以轻松的通过解析SerializeBinary得到我们想要的数据
完整代码如下:
有一些坑要说一下:
1、指针加1,参考博客
https://blog.csdn.net/Think88666/article/details/90437703
大家可以在此基础之上进行一些优化。
#include "stdafx.h" #include <vector> #include <iostream> using namespace std; typedef unsigned int DWORD; //存档数据块集合 #pragma pack(1) struct SerializeBinary { DWORD nVersion; //版本 DWORD nDataSize; //data的大小 SerializeBinary():nVersion(0),nDataSize(0){} DWORD size() { return sizeof(SerializeBinary) + nDataSize; } char data[0]; }; //数据块 struct BinaryBlock { DWORD nType; //块的类型 DWORD nDataSize; //块大小 BinaryBlock() :nType(0), nDataSize(0) {} DWORD size() { return sizeof(BinaryBlock) + nDataSize; } char data[0]; //块的实际内容 }; //装备 struct Equip { DWORD nID; DWORD nNumber; Equip() :nID(0), nNumber(0) {} }; #pragma pack() class CPlayer { //为了简写,所有属性都公开 public: vector<Equip> m_goods; //玩家背包物品列表 DWORD m_equip[5]; //身上五个位置对应的装备 id DWORD m_pet; //宠物索引 CPlayer() { memset(m_equip, 0, sizeof(m_equip)); m_pet = 0; } ~CPlayer() {} //存档函数 char *Save() { //一般缓冲区的大小是20kb左右 可以自行进行溢出检测,这里是后话,一般20kb足够了! char *buff = new char[1024 * 20]; memset(buff, 0, sizeof(buff)); //placement new 手动构造 SerializeBinary* pbinary = new (buff)SerializeBinary; pbinary->nVersion = 10000; //版本控制 BinaryBlock *pblock = reinterpret_cast<BinaryBlock*>(pbinary->data); //依次序列化所有的内容 vector<unsigned char> _bytes; int nSize = 0; int nTotalSize = 0; //玩家背包物品列表 if (m_goods.size() > 0) { nSize = m_goods.size() * sizeof(Equip); memcpy(pblock->data, &m_goods[0], nSize); pblock->nType = 111; //物品列表 pblock->nDataSize = nSize; nTotalSize += pblock->size(); } //装备 pblock = reinterpret_cast<BinaryBlock*>(pbinary->data+nTotalSize); //pblock += pblock->size(); nSize = sizeof(m_equip); memcpy(pblock->data, m_equip, nSize); pblock->nType = 222; //装备 pblock->nDataSize = nSize; nTotalSize += pblock->size(); //宠物 pblock = reinterpret_cast<BinaryBlock*>(pbinary->data+nTotalSize); //pblock += pblock->size(); nSize = sizeof(m_pet); memcpy(pblock->data, &m_pet, nSize); pblock->nType = 333; //宠物 pblock->nDataSize = nSize; nTotalSize += pblock->size(); pbinary->nDataSize = nTotalSize; //现在只需要把pbinary存起来即可 return buff; } //加载存档 void Load(char *buff) { SerializeBinary * pbinary = reinterpret_cast<SerializeBinary*>(buff); int nTotalSize = 0; while (pbinary->nDataSize - nTotalSize > 0) { //数据不为空 BinaryBlock *pblock = reinterpret_cast<BinaryBlock*>(pbinary->data+nTotalSize); switch (pblock->nType) { case 111: //物品 m_goods.resize(pblock->nDataSize / sizeof(Equip)); memcpy(&m_goods[0], pblock->data, pblock->nDataSize); break; case 222: //装备 memcpy(m_equip, pblock->data, pblock->nDataSize); break; case 333: //宠物 memcpy(&m_pet, pblock->data, pblock->nDataSize); break; default: //error break; } nTotalSize += pblock->size(); } } }; int main() { CPlayer player; player.m_goods.push_back(Equip()); //装备 player.m_equip[0] = 123; //宠物id player.m_pet = 123456; char *data = player.Save(); CPlayer player2; player2.Load(data); cout << "goods:"<<player2.m_goods.size() << endl; cout << "pet:" << player2.m_pet << endl; for (int i = 0; i < 5; ++i) { cout << "equip:" << player2.m_equip[i] << endl; } delete data; return 0; }