Code: include/managers/ResourceFactory.hpp, src/managers/ResourceFactory.cpp
The ResourceFactory is a static class that provides a centralized mechanism for creating Resource instances from JSON data. It uses a registry of creator functions to map JSON "type" fields to C++ resource class constructors, allowing for an extensible resource loading system.
// In your game's initialization code
#include "managers/ResourceFactory.hpp"
#include "MyCustomResource.hpp"
// Register a creator function for your custom resource type
ResourceFactory::registerCreator("MyCustomType",
[](const JsonValue& json) -> ResourcePtr {
auto handle = ResourceTemplateManager::Instance().generateHandle();
auto resource = std::make_shared<MyCustomResource>(
handle,
json["id"].tryAsString().value_or(""),
json["name"].tryAsString().value_or(""));
if (json.hasKey("damage")) {
resource->setDamage(
static_cast<float>(json["damage"].tryAsNumber().value_or(0.0)));
}
return resource;
});// Assuming you have a JsonValue object from a JsonReader
JsonValue resourceJson = ...;
// Create the resource using the factory
ResourcePtr resource = ResourceFactory::createFromJson(resourceJson);
if (resource) {
// Use the created resource
}The ResourceFactory is a static class, meaning you don't need to create an instance of it. It maintains a static map of resource type names to creator functions. When createFromJson is called, it looks up the appropriate creator function based on the "type" field in the JSON data and invokes it to create the resource instance.
The factory is initialized with a set of default resource creators for the engine's built-in resource types.
static ResourcePtr createFromJson(const JsonValue& json): Creates aResourceinstance from aJsonValue.static bool registerCreator(const std::string& typeName, ResourceCreator creator): Registers a new resource creator function.static bool hasCreator(const std::string& typeName): Checks if a creator is registered for a given type.static std::vector<std::string> getRegisteredTypes(): Returns a list of all registered resource types.static void initialize(): Initializes the factory with default resource creators.static void clear(): Clears all registered creators (for testing purposes only).
To add a new resource type to the engine, follow these steps:
Create a class that inherits from the Resource base class:
// include/resources/MyCustomResource.hpp
#pragma once
#include "resources/Resource.hpp"
namespace VoidLight {
class MyCustomResource : public Resource {
public:
MyCustomResource(VoidLight::ResourceHandle handle,
const std::string& id,
const std::string& name)
: Resource(handle, id, name,
ResourceCategory::Item,
ResourceType::QuestItem) {}
// Custom properties
float getDamage() const { return m_damage; }
void setDamage(float damage) { m_damage = damage; }
int getDurability() const { return m_durability; }
void setDurability(int durability) { m_durability = durability; }
private:
float m_damage{10.0f};
int m_durability{100};
};
} // namespace VoidLightCreate a factory function that parses JSON and creates the resource:
// In your initialization code or a dedicated registration file
#include "managers/ResourceFactory.hpp"
#include "resources/MyCustomResource.hpp"
#include "utils/JsonReader.hpp"
ResourcePtr createCustomResourceFromJson(const JsonValue& json) {
auto handle = ResourceTemplateManager::Instance().generateHandle();
auto resource = std::make_shared<MyCustomResource>(
handle,
json["id"].tryAsString().value_or(""),
json["name"].tryAsString().value_or(""));
// Parse custom properties
if (json.hasKey("damage")) {
resource->setDamage(
static_cast<float>(json["damage"].tryAsNumber().value_or(0.0)));
}
if (json.hasKey("durability")) {
resource->setDurability(json["durability"].tryAsInt().value_or(0));
}
return resource;
}Register your creator function during game initialization:
// In Game::init() or similar initialization function
void registerCustomResources() {
ResourceFactory::registerCreator(
"custom_type",
createCustomResourceFromJson
);
}Create resource definitions in a role-appropriate catalog, or in a custom catalog loaded after the built-in split catalogs:
{
"resources": [
{
"id": "fire_sword",
"type": "custom_type",
"category": "Item",
"name": "Fire Sword",
"description": "A sword wreathed in flames",
"damage": 25.0,
"durability": 150
},
{
"id": "ice_dagger",
"type": "custom_type",
"category": "Item",
"name": "Ice Dagger",
"description": "A dagger of frozen steel",
"damage": 15.0,
"durability": 80
}
]
}Load your resources using ResourceTemplateManager:
// Load all resources from file
auto& rtm = ResourceTemplateManager::Instance();
rtm.loadResourcesFromJson("res/data/custom_weapons.json");
// Access by stable JSON ID
ResourceHandle fireHandle = rtm.getHandleById("fire_sword");
ResourcePtr fireSword = rtm.getResourceTemplate(fireHandle);
// Cast to specific type if needed
auto customRes = std::dynamic_pointer_cast<MyCustomResource>(fireSword);
if (customRes) {
float damage = customRes->getDamage();
}Resources should be registered before loading any JSON files that use them:
void Game::init() {
// 1. Initialize managers
ResourceFactory::initialize(); // Registers built-in types
// 2. Register custom types
registerCustomResources();
// 3. Load resource files (now includes custom types)
ResourceTemplateManager::Instance().loadResourcesFromJson("res/data/items.json");
ResourceTemplateManager::Instance().loadResourcesFromJson("res/data/weapons.json");
ResourceTemplateManager::Instance().loadResourcesFromJson("res/data/equipment.json");
ResourceTemplateManager::Instance().loadResourcesFromJson("res/data/materials.json");
ResourceTemplateManager::Instance().loadResourcesFromJson("res/data/currency.json");
ResourceTemplateManager::Instance().loadResourcesFromJson("res/data/custom_weapons.json");
}- Register Early: Register all your custom resource creators during your game's initialization phase.
- Handle Null: Always check if
createFromJsonreturns anullptr, which indicates that the resource creation failed. - Extensibility: Use the factory to create all your resource types to maintain a consistent and extensible resource loading pipeline.
- Testing: When writing tests for your resources, you can use the
clear()method to isolate your tests from the default registered types. - Type Safety: Always validate JSON fields exist before accessing them to avoid runtime errors.
- Consistent Naming: Use snake_case for JSON type names (e.g., "custom_type", "fire_weapon").