整体概述参见:http://dreamyouxi.com:7129/blog/1203
服务器战斗端与客户端采用了类似的架构,共享相同的概念(GameObject、Prefab、GetComponent、BoxCollider 等),因此在资源层面可以做到很好的复用。
序列化分为两步:第一步是将 io 文件反序列化为内存中的 Prefab 对象;第二步是从 Prefab 对象生成各种 class 的数据。
第一步:从 io 文件到内存 Prefab
template<typename Class>
inline void DeSerializeRigidbody(Prefabs * pre, Class& obj)
{
bool ok = false;
do
{
PARSE_OBJECT_MEMBER(pre->hasRigidbody, "hasRigidbody");
PARSE_OBJECT_MEMBER(pre->rigidbody.useGravity, "useGravity");
PARSE_OBJECT_MEMBER(pre->rigidbody.isKinematic, "isKinematic");
PARSE_OBJECT_MEMBER(pre->rigidbody.mass, "mass");
PARSE_OBJECT_MEMBER(pre->rigidbody.drag, "drag");
PARSE_OBJECT_MEMBER(pre->rigidbody.angularDrag, "angularDrag");
} while (false);
}
template<typename Class>
inline void DeSerializeCapsuleColliders(Prefabs * pre, Class& obj2)
{
for (auto it = obj2.begin(); it != obj2.end(); ++it)
{
auto &obj = it->GetJsonObject();
pre->capsuleColliders.emplace_back(CapsuleCollider::Serialize());
auto &col = pre->capsuleColliders[pre->capsuleColliders.size() - 1];
bool ok = false;
PARSE_OBJECT_MEMBER(col.radius, "radius");
PARSE_OBJECT_MEMBER(col.height, "height");
PARSE_OBJECT_MEMBER(col.direction, "direction");
PARSE_OBJECT_MEMBER(col.isTrigger, "isTrigger");
}
}
第二步:从 Prefab 生成 GameObject 或 CppBehaviour
/*
* Author: caoshanshan
* Email: me@dreamyouxi.com
*/
#pragma once
#include "battle/BaseAPI.h"
#include "battle_app/BattleMisc.h"
class BallisticSimulater :public CppBehaviour
{
DEFINE_SUB_CLASS(CLASS_ID_BallisticSimulater, BallisticSimulater, CppBehaviour);
public:
class Serialize : public Super::Serialize
{
public:
float speed = 0.0f;
float damage = 0;
float lifetime = 6.0f;
public:
void DeSerialize(CppBehaviourSerializeData & data)
{
TryParseObjectMember(data, speed, "speed");
TryParseObjectMember(data, damage, "damage");
TryParseObjectMember(data, lifetime, "lifetime");
}
};
Serialize serialize;
void* GetSerializeField()override { return &serialize; }
//----------------------------end of 协议部分
virtual int GetEventCallbackMask()override { return BehaviourEventCallbackMaskUpdate; }
};
这里有几个与 Unity 实现不同的地方:
- 服务器内存充裕,可以通过内存换时间,因此目前在启动时会把所有 Prefab 全部加载进来。
- 构造 GameObject 时,直接使用 memcpy 而非逐个反序列化,以达到最高速度,示例如下:
int size = this->cppscripts.size();
for (int i = 0; i < size; i++)
{
auto &buffer = cppscripts[i];
if (buffer.classid > 0 && buffer.data && buffer.size > 0)
{
CppBehaviour::Ptr cls = CreateInternalCppBehiviourClassObject(buffer.classid);
if (cls)
{
//class which is build-in
}
else
{
if (Prefabs::CREATE_CPP_CLASS && Prefabs::SERIALIZE_CPPBEHAVIOUR)
{
//调用自定义初始化数据
cls = Prefabs::CREATE_CPP_CLASS(buffer.classid);
assert(cls);
if (!cls)
{
//未定义 的class 在序列文件中是不允许的
assert(false);
continue;
}
}
else
{
//invalid hook
assert(false);
continue;
}
}
void *target = cls->GetSerializeField();
if (target)
{
::memcpy(target, buffer.data, buffer.size);
ret->AddComponentCppBehaviourInternal(cls);
}
else
{
//there has not any data will be serialized
}
}
else
{
LOG_DEBUG("invalid cpp serialize data");
assert(false);
}
}
至此完成对象生成。配套还需要编写一系列工具,用于生成这些序列化文件。