我正在开发一个小型游戏引擎项目,我想制作一个实体分配系统。
想法是这样的:引擎DLL是“不可更改的”,由我提供。 开发人员将能够使用自定义实体类型自行编译游戏 DLL。
每个实体都派生自引擎的
CBase
类。实体具有类名称,在读取地图时将使用这些名称。实体将使用这些类名“生成”。它基本上是该实体的别名。
但是有一个问题:
“生成”实体的分配应在引擎中完成。引擎应该能够调用基本函数,而不能调用附加函数。
但是引擎不会从游戏DLL中导入这些类,所以我不能只使用
new EntityClass
如何从其他 DLL 分配未知类(从已知类派生)?有没有办法通过函数“传递类型”?
我之前的解决方法就像这样的伪代码:
发动机:
class DLLEXPORT CBase { std::string className; virtual void Init(); virtual void OtherMethod(); } CBase* CreateNewEntity(const char* classname) { CBase* newentity = static_cast<CBase*>(SERVER_DLL_GETNEWOBJECT(classname)); newentity->Init(); entitylist.addentity(newentity); }
游戏DLL:
class DLLEXPORT CBaseEntity : public DLLIMPORT CBase { //Called by engine virtual void Init() override; virtual void OtherFunction() override; //Inaccesible by engine virtual void AnotherFunction() override; } CBaseEntity* SERVER_DLL_GETNEWOBJECT(const char* classname) { if (strcmp(className, "Entity_Weapon_Test") == 0) { return static_cast<CBaseEntity*>(new CTestWeapon); } else return nullptr; }
SERVER_DLL_GETNEWOBJECT 从 Game DLL 中导出,并导入到 Server DLL 中。这将是一个预定义的函数。
但在此解决方法中,分配是由游戏 DLL 完成的,而不是由引擎完成的。所以我不能用它。
我期待这样的事情:
#include <memory>
#include <string>
#include <functional>
#include <map>
#include <stdexcept>
#include <iostream>
// Declare an "interface" (abstract baseclass)
// this is the only knowledge your engine
// and client code will share
//
// But make sure client code compiles with an ABI
// compatible compiler!
//
class EntityItf
{
public:
virtual ~EntityItf() = default;
virtual void DoSomething() = 0;
};
using creation_function_t = std::function<std::unique_ptr<EntityItf>()>;
class /*dllexport */ EntityFactory
{
public:
// Meyers singleton
inline static EntityFactory& Instance()
{
static EntityFactory factory;
return factory;
}
void RegisterCreationMethod(const std::string& entity_name, creation_function_t creation_function)
{
if ( m_factory_functions.find(entity_name) != m_factory_functions.end())
{
throw std::runtime_error{"An entity with that name has already been registered"};
}
m_factory_functions[entity_name] = creation_function;
}
std::unique_ptr<EntityItf> CreateEntity(const std::string& entity_name)
{
if ( m_factory_functions.find(entity_name) == m_factory_functions.end())
{
throw std::runtime_error{"Unknown entity name"};
}
return m_factory_functions[entity_name]();
}
private:
EntityFactory() = default;
~EntityFactory() = default;
std::map<std::string,creation_function_t> m_factory_functions;
};
// Developer code
class MyEntity :
public EntityItf
{
public:
void DoSomething() override
{
std::cout << "Hello World!\n";
}
};
int main()
{
auto& factory = EntityFactory::Instance();
factory.RegisterCreationMethod("MyEntity", []{ return std::make_unique<MyEntity>(); });
auto my_entity = factory.CreateEntity("MyEntity");
my_entity->DoSomething();
return 0;
}