To let the game define its own components and allow the engine to create them without knowing them, we use the factory method pattern. It consists in inheriting from a ComponentFactory interface, which declares create and destroy methods. Every Component has to define a ComponentFactory.
The create function receives a Parameters map, used to set the values of the component, and returns the created Component. This is the TransformFactory, for example:
Component* FactoryTransform::create(Parameters& params)
{
Transform* transform = new Transform();
transform->setPosition(Vector3(Value(params, "position_x", 0.0f),
Value(params, "position_y", 0.0f), Value(params, "position_z", 0.0f)));
transform->setRotation(Vector3(Value(params, "rotation_x", 0.0f),
Value(params, "rotation_y", 0.0f), Value(params, "rotation_z", 0.0f)));
transform->setScale(Vector3(Value(params, "scale_x", 1.0f),
Value(params, "scale_y", 1.0f), Value(params, "scale_z", 1.0f)));
std::string transformParent = Value(params, "parentname", std::string());
transform->setParentName(transformParent);
return transform;
}
void me::FactoryTransform::destroy(Component* component)
{
delete component;
}
We use the value functions to check if the value is defined. If it is, we check if its type is the one we expected and then return it. Otherwise, it returns the default value for that parameter.
float FactoryComponent::Value(Parameters& params, const ParameterName& parameter, float defaultValue)
{
if (params.count(parameter) > 0) {
for (char c : params[parameter]) {
if (!std::isdigit(c) && c != '.' && c != '-') {
errorManager().throwMotorEngineError("Invalid parameter for " + parameter + " set.",
"Value is not a float.");
sceneManager().quit();
return defaultValue;
}
}
return std::stof(params[parameter]);
}
else
return defaultValue;
}
Finally, the game has to export a initFactories function to match the component name expected in the .lua scene with the ComponentFactory.
__VROOMVROOM_API void initFactories()
{
componentsFactory().addFactoryComponent("camerafollow", new FactoryCameraFollow());
componentsFactory().addFactoryComponent("vehiclecontroller", new FactoryVehicleController());
componentsFactory().addFactoryComponent("circuitinfo", new FactoryCirtuitInfo());
componentsFactory().addFactoryComponent("gamemanager", new FactoryGameManager());
componentsFactory().addFactoryComponent("checkpoint", new FactoryCheckpoint());
componentsFactory().addFactoryComponent("uibuttonscene", new FactoryUIButtonScene());
componentsFactory().addFactoryComponent("uibuttonquit", new FactoryUIButtonQuit());
componentsFactory().addFactoryComponent("powerupuiwheel", new FactoryPowerUpUIWheel());
componentsFactory().addFactoryComponent("powerupobject", new FactoryPowerUpObject());
componentsFactory().addFactoryComponent("oil", new FactoryOil());
componentsFactory().addFactoryComponent("nerf", new FactoryNerf());
}