The object manager

The M5ObjectManager is a singleton class that is responsible for, among other things, loading archetypes and creating objects. There are a lot of members and methods in this class so looking at everything would take too long. In this section, we will only go over the methods specifically related to loading and creating an object from an Archetype file. Remember that since the class is a singleton, we have global access. For that reason, every member and method is static:

class M5ObjectManager 
{
public:
static M5Object* CreateObject(M5ArcheTypes type);
static void AddArcheType(M5ArcheTypes type,
const char* fileName);
static void RemoveArcheType(M5ArcheTypes type);

//Plus other methods

private:
typedef M5Factory<M5ComponentTypes,
M5ComponentBuilder,
M5Component> ComponentFactory;
typedef std::unordered_map<M5ArcheTypes,
M5Object*> ArcheTypeMap
static ComponentFactory s_componentFactory;
static ArcheTypesMap s_archetypes;

//Plus other members
};

Here we have the most important members and methods to show how objects are loaded from a file. What we haven't shown here are methods related to destroying or searching for specific objects. If you are interested in those functions, feel free to review the full source code that comes with this book.

In the public section, the AddArcheType method will be used to read an archetype file, create the object, and store it for later. The RemoveArcheType method is used to delete the object when it is no longer needed. Finally, the CreateObject method will be used to clone one of the previously loaded archetypes. In the private section, we have a few types defined for creating easier names. You can see we are using the templated Dynamic Factory that we created in Chapter 5, Decoupling Code via the Factory Method Pattern. We also have a map of the loaded Archetype objects.

Let's take a closer look at these methods:

void M5ObjectManager::AddArcheType(M5ArcheTypes type, 
const char* fileName)
{
MapItor found = s_archetypes.find(type);
M5DEBUG_ASSERT(found == s_archeypes.end(),
"Trying to add a prototype that already exists");

M5IniFile file;
file.ReadFile(fileName);
M5Object* pObj = new M5Object(type);
pObj->FromFile(file);

std::string components;//A string of all my components
file.GetValue("components", components);

//parse the component string and create each component
std::stringstream ss(components);
std::string name;

//Loop through the stream and get each component name
while (ss >> name)
{
M5Component* pComp = s_componentFactory.Build(
StringToComponent(name));
pComp->FromFile(file);
pObj->AddComponent(pComp);
}
//Add the prototype to the prototype map
s_archeypes.insert(std::make_pair(type, pObj));
}

This might seem like a difficult function, but this is where the magic happens. Let's start at the beginning. This function takes two parameters, an enumeration ID specifying the type to create, and a file name to associate with that enum ID. Next, we need to check if this M5Archetypes ID has been loaded before. If it has, there must be an error. After checking for the enum error, we read the .ini file. If the file does not exist, the ReadFile method will assert.

If there haven't been any errors, we create a brand new M5Object, and pass the M5ArcheTypes ID to the constructor. This simply sets the type of the object, but doesn't do anything else. To set the data for the object, we call the FromFile method to read the global section from the .ini file. This will set the position, scale, rotation, and everything else in the object except the actual component, which needs to be handled differently.

The trouble with the components is that the file contains the component names as strings but, for the sake of performance during the game, we want to avoid doing string comparisons. This means we need to somehow convert these strings to an enum value. This is the purpose of the StringToComponent function. This function is an if/else chain that will return the correct enum based on the parameter. Functions like this can be a problem to maintain. We will discuss in a later chapter about how to use Windows batch files to automate this process.

After we read the object data from the file, we read the component list from the file. This is a list of component names separated by a space. There are lots of ways we could extract each individual component name, but one of the easiest ways is to use an STL stringstream object. This allows us to extract separate strings from the stream, just like std::cin.

After creating our stringstream object, we loop through the stream and extract the name. We then use s_componentFactory to build the correct component, after it has been converted to a M5ComponentTypes enum. After the correct component is built, we pass the .ini file to the component's FromFile method to let the derived component read its own data. Then we make sure to add the component to the object. Finally, after all components have been read, we add the type and object pointer to our s_archetypes map.

This may seem like a complicated way of loading objects. However, this function doesn't need to know about any derived component types, or which components go with a specific object type. If our archetype .ini files change, we don't need to recompile this code. We are free to add, remove, or change objects in our game and our high-level module, the M5ObjectManager, doesn't need to change:

void M5ObjectManager::RemoveArcheType(M5ArcheTypes type) 
{
MapItor found = s_archetypes.find(type);
M5DEBUG_ASSERT(found != s_archetypes.end(),
"Trying to Remove a prototype that doesn't exist");

delete found->second;
found->second = 0;
s_archetypes.erase(found);
}

The RemoveArcheType method is much simpler than the AddArcheType. All we need to do here is make sure the type to delete exists in the map, which we do by first finding and using a debug assert if it isn't there. Then we delete the prototype object and erase the iterator within the map.

The RemoveArcheType method doesn't need to be called since all archetype objects will be deleted when the game exits. However, this could be used if the user wanted to minimize which archetypes existed throughout the game. By default, the Mach5 Engine automatically loads all archetype .ini files before the game begins:

M5Object* M5ObjectManager::CreateObject(M5ArcheTypes type) 
{
MapItor found = s_archetypes.find(type);
M5DEBUG_ASSERT(found != s_archetypes.end(),
"Trying to create and Archetype that doesn't exist");

M5Object* pClone = found->second->Clone();
s_objects.push_back(pClone);//A std::vector<M5Object*>
return pClone;
}

Finally, we have the method that allows the user to create Archetype objects. Here, the user supplies the M5ArcheTypes type that they want to create. First, the method does the standard error checking that we are familiar with. Then, after finding the correct iterator, we make use of the Prototype pattern's Clone method to copy all data and components from the Archetype object. After creating the object, we automatically add it to the list of active game objects and return the pointer to the user so they can modify things such as position and velocity if needed.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.139.97.202