Sure, though it's easier to just paste existing code.
class PlanetManager
{
public:
// Method(s)
static PlanetManager& get(); // Get a singleton of this class
Planet& createPlanet( const std::string& name );
void destroyPlanet( Planet& );
void killAll();
bool destroying( Planet& );
void update( f32 elapsed );
private:
// Constructor(s)/Destructor
PlanetManager();
PlanetManager( PlanetManager const& ); // Hide
~PlanetManager();
// Assignment operator(s)
PlanetManager& operator = ( PlanetManager const& ); // Hide
// Data member(s)
std::vector<Planet*> planetList_;
Planet* currentlyDestroying_;
gdk::DynamicIdHandler planetID_;
};
This class manages all planets in my game, you may notice I privatise the constructor/destructor, as well as hide the copy constructor and assignment operator(by declaring but not defining them). This is because all of my managers follow the basic(non-thread safe) singleton design pattern, that is, only one instance of this class can exist at any time. The implementation of this is otherwise simply:
PlanetManager& PlanetManager::get()
{
static PlanetManager singleton;
return singleton;
}
You'll notice you can create a planet using
'Planet& createPlanet( const std::string& name );' this returns a reference because I'm quite anal about having things be references unless their value can be NULL during standard(correct) execution, or if they're arguments using any of the basic types. If they're pointers then I get an overwhelming urge to do 'if ( whateverPtr )' before its use.
The actual code for the above method is pretty basic and is almost identical to all of the other managers:
Planet& PlanetManager::createPlanet( const std::string& name )
{
Planet* instance = new Planet( planetID_.getFreeID(), name );
gdk::autoResizeVector( planetList_, instance->getIndex() );
planetList_[instance->getIndex()] = instance;
return *instance;
}
The 'gdk:
ynamicIdHandler' class essentially returns an unused index, much like the object/image/whatever IDs GDK uses, except this is used to efficiently find holes in the vector I store my planets in, if there are no holes then it returns an index higher than the vector size, this is where 'gdk::autoResizeVector' comes in use because that just automatically resizes the vector and zeros the new indices if the target size is larger than the current.
So the Planet constructor takes an index to where it's stored in the Planets Vector, and its name, the name itself is used to reference the 'PlanetTemplate' instance this planet is based off of. I then write the planet instance to the vector.
The Planet class inherits from my 'gdk::RefCounter' class(well not directly, it actually inherits Entity) which begins with 1 reference and can only be destroyed from 'void destroyPlanet( Planet& );'. How do I achieve that? Using more fun code:
void PlanetManager::destroyPlanet( Planet& instance )
{
THROW_IF( instance.getRefCount() == 1, "Ref count must be 1 to destroy the planet" );
currentlyDestroying_ = &instance;
planetList_[instance.getIndex()] = nullptr;
planetID_.addFreeID( instance.getIndex() );
instance.release();
}
THROW_IF is a bit of a misnomer, it's pretty much assert(), except it writes to my log system first and I couldn't exactly call it ASSERT, and I also have THROW for when I don't need a condition check first. Anyway, the first line makes sure the ref count is 1 before you destroy it, this makes it very hard for me to make my code leak memory and slightly harder to mess up the ref counting. You'll then notice I set 'currentlyDestroying_' to the instance passed, I blank out the planet's entry in my vector, put the now unused vector index back into my IDHandler class and finally release the last reference to my Planet, this invokes the virtual destructor in my gdk::RefCounter.
The Planet destructor looks like this:
Planet::~Planet()
{
THROW_IF( PlanetManager::get().destroying( *this ), "Planet must be destroyed via PlanetManager::destroyPlanet" );
...blah blah, rest of cleanup code...
}
It throws an assertion if this wasn't previously called by PlanetManager, and yes I'm aware there are different/better ways to do this, but this made the most sense at the time.
As far as managers go, this one is pretty tame because it only handles my planets, my gdk:
bjectManager is a bit more complex:
class ObjectManager
{
public:
// Method(s)
static ObjectManager& get(); // Get a singleton of this class
// Creation/destruction
Object& createObject( u32 vertCount, u32 indexCount, u32 FVF ); // Create a raw object from an FVF format
Object& createObject( Object::PrimType = Object::PRIM_CUSTOM ); // Create a primitive object
Object& createObject( const std::string& meshPath, bool associatePath = false ); // Create an object from file
void destroyObject( Object* ); // Destroy an object via ptr, checks against 'nullptr' first - Requires: refCount == 1
void destroyObject( Object& ); // Destroy an object via reference - Requires: refCount == 1
// Set
void setObserver( const Camera&, bool update = false ); // Specify which camera is currently observing the objects
// Get
const Camera* getObserver() const;
const Camera& getObserverRef() const;
// Other
void updateObserver(); // Move all objects around the observer
Object* findObject( const std::string& handle );
Object& findObjectRef( const std::string& handle );
private:
// Constructor(s)/Destructor
ObjectManager();
ObjectManager( ObjectManager const& ); // Hide
~ObjectManager();
// Assignment operator(s)
ObjectManager& operator = ( ObjectManager const& ); // Hide
// Method(s)
void updateObserver( Object&, const Position& ); // Move a specific object around the camera
void registerObject( Object& );
void unregisterObject( Object& );
bool destroying( Object& ); // Check which object is currently being destroyed
void freeResources(); // Deallocate everything
// Data member(s)
std::vector<Object*> objectList_;
std::map<std::string,Object*> objectAssociationMap_;
gdk::DynamicIdHandler objectId_;
Object* currentlyDestroying_;
const Camera* observer_;
// Access
friend class Object;
friend class Application;
};
You'll notice I have overloads and methods that do the same thing but handle pointers and references, with the case of the ones that return references those will throw errors if they can't find what you search for, the pointer version will just return NULL. I also like to organize my class declarations by grouping everything
, and at least in VS with my 4 space tabs, these groups are mostly aligned.