前言¶
- 什么是UUID 可以理解为全球唯一标识
- 为什么要使用UUID标识实体 = 此节目的 为了点击运行场景,实体发生位置等变化复原而要实现的标识功能
- 为什么不简单的使用increment递增 假设从0开始,游戏分发到两个电脑,他们创建实体标识id,需要知道从多少开始递增,需要服务器提供权威多少开始递增,这样的设计不太好,不想多一个服务器功能,所以只需使用uuid。 uuid发生的冲突很小,所以不用担心两个电脑创建实体的id一样
- 如何实现 定义UUID类,使用cpp的随机函数,随机ID
实现细节¶
- uuid一般是128位,16字节,但是Cherno只用了64位,8字节实现表示UUID
- uuid作为key对应实体存储在map中时
- 若是unordered_map<uint64_t, std::string> m_Map;
这种结构,UUID类只需提供类型转换函数
operator uint64_t() const {return m_UUID;} m_Map[(uint64_t)UUID()] = "Cherno";
- 若是unordered_map<UUID, std::string> m_Map;
需要为UUID类提供哈希函数
不然使用UUID类作为key会有bug
namespace std { template<> struct hash<Hazel::UUID>{ std::size_t operator()(const Hazel::UUID& uuid) const{ return hash<uint64_t>()((uint64_t)uuid); } }; }
- 若是unordered_map<uint64_t, std::string> m_Map;
这种结构,UUID类只需提供类型转换函数
UUID.h¶
#pragma once
#include <xhash>
namespace CryDust {
class UUID
{
public:
UUID();
UUID(uint64_t uuid);
UUID(const UUID&) = default;
operator uint64_t() const { return m_UUID; }
private:
uint64_t m_UUID;
};
}
namespace std {
template<>
struct hash<CryDust::UUID>
{
std::size_t operator()(const CryDust::UUID& uuid) const
{
return hash<uint64_t>()((uint64_t)uuid);
}
};
}
UUID.cpp¶
#pragma once
#include "cdpch.h"
#include "UUID.h"
#include <random>
#include <unordered_map>
namespace CryDust {
static std::random_device s_RandomDevice;
static std::mt19937_64 s_Engine(s_RandomDevice());
static std::uniform_int_distribution<uint64_t> s_UniformDistribution;
UUID::UUID()
: m_UUID(s_UniformDistribution(s_Engine))
{
}
UUID::UUID(uint64_t uuid)
: m_UUID(uuid)
{
}
}
Components¶
附加UUID的component以及可脚本化的组件的component(但是不实现,只声明)
struct IDComponent
{
UUID ID;
IDComponent() = default;
IDComponent(const IDComponent&) = default;
};
// Forward declaration
class ScriptableEntity;
Entity¶
UUID GetUUID() {
return GetComponent<IDComponent>().ID;
}
Scene¶
添加IDComponent的模板添加特化,
template<>
void Scene::OnComponentAdded<IDComponent>(Entity entity, IDComponent& component)
{
}
Entity Scene::CreateEntity(const std::string& name)
{
return CreateEntityWithUUID(UUID(), name);
}
Entity Scene::CreateEntityWithUUID(UUID uuid, const std::string & name)
{
Entity entity = { m_Registry.create(), this };
entity.AddComponent<IDComponent>(uuid);
entity.AddComponent<TransformComponent>();
auto& tag = entity.AddComponent<TagComponent>(); //创建tag组件
tag.Tag = name.empty() ? "Entity" : name; //设置tag组件为名字
return entity;
}
SceneSerialize¶
序列化场景的时候,需要拿到实体uiid
out << YAML::Key << "Entity" << YAML::Value << entity.GetUUID();
反序列化的时候,通过uuid创建实体
uint64_t uuid = entity["Entity"].as<uint64_t>();//这里通过运算符重载完成了uuid的创建
//
Entity deserializedEntity = m_Scene->CreateEntityWithUUID(uuid, name);
C++小知识¶
写一下巩固一下:可以通过运算符重载,将一个类转成其他类型的函数
namespace std {
template<>
struct hash<CryDust::UUID>
{
std::size_t operator()(const CryDust::UUID& uuid) const
{
return hash<uint64_t>()((uint64_t)uuid);
}
};
}