SFML community forums
Help => General => Topic started by: e_barroga on January 10, 2009, 03:04:08 am
-
I need help writing an object manager.
I'm trying to make it extremely flexible so that content can be added easily.
Basically, I want to make an instance list so that SFML can loop through it and draw the instances at their coordinated places. I want to make a base object class, which is what SFML will be using for reference. This base object will have three virtual functions:
Create()
Step()
Destroy()
This way, I can easily write new object classes without messing with the engine in its entirety.
class baseObject {//The base Game Object
virtual Create();
virtual Step();
virtual Destroy();
};
class objBall: public baseObject {
void Create();
void Step();
void Destroy();
};
Then, I would just loop through the baseObject and it'll perform the Create, Step, and Destroy methods the way they're stated in the childs.
I'm having difficulty getting this to work, could I get some assistance?
Thanks.[/code]
-
For looping though you could use std::vector (http://www.cplusplus.com/reference/stl/vector/)
-
Besides BaseObject you need a manager class like ObjectManager. This one holds and manages all your objects derived from BaseObject and has an interface for at least adding, removing/deleting and processing all added objects.
Like klusark said you can use std::vector as a storage. I'd suggest to use std::vector<BaseObject*>. This manages pointers to BaseObjects (and compatibles).
-
One thing that stumps is how to properly call the specific object when I want to create it:
instance_create( <name of object> ) {
<name of object> newObj;
instances.push_back( newObj );
}
I want to use it in the main loop like so:
instance_create( test );
Also.... when initializing the instance list, it doesn't seem to recognize the child objects. I did the following:
std::vector <base> instances;
I've created a child object:
class test: public base {
public:
void step() {
std::cout << "Testing\n";
}
};
Then I have the following in my main loop:
for (i=0; i<instances.size(); i++) {
instances[i].step();
}
But it doesn't seem to work, so I changed the vector type for testing:
std::vector <test> instances;
And it outputs the text repeatedly, as I wanted.
-
Normally you would have a manager class to manage these objects. Let's do a simple example without such a class.
Let's assume BaseObject is the base class and ObjectA and ObjectB are derived from it.
std::vector<BaseObject*> objects;
// Create some objects.
objects.push_back( new ObjectA );
objects.push_back( new ObjectB );
// Iterate through objects.
for( std::vector<BaseObject*>::iterator iter = objects.begin(); iter != objects.end(); ++iter ) {
iter->step();
}
You have to make sure that all objects get deleted properly when you don't need them anymore, since the vector only holds pointers, thus not destroying the objects they point to.
Everything regarding the objects (create, destroy, step through etc) should take place in a manager class to encapsulate it.
-
So you're saying that instead of writing virtual functions in the base class.... write the functions in a "manager class"?
How would I make that work, I don't completely understand.
The main reason I wish to make a base object class that has virtual functions:
virtual Create();
virtual Step();
virtual Destroy();
is so that I can add new object classes very simple. How would I get that to work if I made the Create, Step, Destroy methods for every derived objects in an "object manager" class?
-
No, your approach with the base class having virtual functions is just fine. My example above does exactly use your public interface.
At first you have your base class containing the virtual functions (maybe make them pure virtual, think about it).
Then you have all the derived classes from the base class which implement the functionality
And last, but not least, you must have some kind of management class, which manages all your objects, which are derived from your base class. That's what my example is meant for.
-
How would I properly execute this:
void instance_create ( name_of_object ) {
name_of_object newInstance;
instances.push_back( newInstance );
}
int main () {
instance_create( o_ball );
for ( int i=0; i<instances.size(); i++ ) {
instances[i].Step();
}
}
-
You could get the name of object as a string and compare it to all the possibilities.
Example from my game
void spriteManager::registerSprite(std::string type, std::string name)
{
if (type.compare("player") == 0){
genericSprite *player = new Player(name);
Sprites[name] = player;
}else{
std::cout << "Could not find sprite type" << std::endl;
}
}
-
e_barroga:
Please read the posts more carefully. :)
Using an object manager class, your code could look like as follows:
int main() {
ObjectManager mgr;
mgr.addObject( new MyObject ); // MyObject derived from BaseObject
mgr.addObject( new MyOtherObject ); // MyOtherObject derived from BaseObject
while( true ) { // Some kind of main loop.
mgr.processObjects(); // Calls step() for every object.
}
}
BaseObject has the virtual functions create() and step(). create() can be called in several ways: Either by the constructor or by ObjectManager::addObject(). ObjectManager::processObjects() iterates through all added objects and calls step() on each. I hope it's more clear this time.
Edit:
klusark:
You're implementing some kind of factory pattern. I would either prefer to implement functions like: createPlayer(), create...() instead of comparing type strings (we're using OOP ;) ), or directly pass a pointer to the proper object by using the new statement.
For example something like:
objectmanager.register( ObjectFactory::createPlayer(), "an_unique_id" );
-
e_barroga:
Please read the posts more carefully. :)
Using an object manager class, your code could look like as follows:
int main() {
ObjectManager mgr;
mgr.addObject( new MyObject ); // MyObject derived from BaseObject
mgr.addObject( new MyOtherObject ); // MyOtherObject derived from BaseObject
while( true ) { // Some kind of main loop.
mgr.processObjects(); // Calls step() for every object.
}
}
BaseObject has the virtual functions create() and step(). create() can be called in several ways: Either by the constructor or by ObjectManager::addObject(). ObjectManager::processObjects() iterates through all added objects and calls step() on each. I hope it's more clear this time.
Edit:
klusark:
You're implementing some kind of factory pattern. I would either prefer to implement functions like: createPlayer(), create...() instead of comparing type strings (we're using OOP ;) ), or directly pass a pointer to the proper object by using the new statement.
For example something like:
objectmanager.register( ObjectFactory::createPlayer(), "an_unique_id" );
Yes, I already know about writing an object manager, but I am having issues writing the method to create instances through arguments.
void ObjectManager::addObject ( argument0 ) {
new argument0;
}
How do I make it create an instance of whatever is defined in argument0?
-
How do I make it create an instance of whatever is defined in argument0?
You should look into STL containers.
-
e_barroga:
Please read the posts more carefully. :)
Using an object manager class, your code could look like as follows:
int main() {
ObjectManager mgr;
mgr.addObject( new MyObject ); // MyObject derived from BaseObject
mgr.addObject( new MyOtherObject ); // MyOtherObject derived from BaseObject
while( true ) { // Some kind of main loop.
mgr.processObjects(); // Calls step() for every object.
}
}
BaseObject has the virtual functions create() and step(). create() can be called in several ways: Either by the constructor or by ObjectManager::addObject(). ObjectManager::processObjects() iterates through all added objects and calls step() on each. I hope it's more clear this time.
Edit:
klusark:
You're implementing some kind of factory pattern. I would either prefer to implement functions like: createPlayer(), create...() instead of comparing type strings (we're using OOP ;) ), or directly pass a pointer to the proper object by using the new statement.
For example something like:
objectmanager.register( ObjectFactory::createPlayer(), "an_unique_id" );
Yes, I already know about writing an object manager, but I am having issues writing the method to create instances through arguments.
void ObjectManager::addObject ( argument0 ) {
new argument0;
}
How do I make it create an instance of whatever is defined in argument0?
I don't know much about patterns or anything, but I think a Factory class might fit your need. Look it up, it's pretty nifty.
-
How do I make it create an instance of whatever is defined in argument0?
I gave you a concrete example. Hint:
objectmanager.register( ObjectFactory::createPlayer(), "an_unique_id" );
-
e_barroga:
Please read the posts more carefully. :)
Using an object manager class, your code could look like as follows:
int main() {
ObjectManager mgr;
mgr.addObject( new MyObject ); // MyObject derived from BaseObject
mgr.addObject( new MyOtherObject ); // MyOtherObject derived from BaseObject
while( true ) { // Some kind of main loop.
mgr.processObjects(); // Calls step() for every object.
}
}
BaseObject has the virtual functions create() and step(). create() can be called in several ways: Either by the constructor or by ObjectManager::addObject(). ObjectManager::processObjects() iterates through all added objects and calls step() on each. I hope it's more clear this time.
Edit:
klusark:
You're implementing some kind of factory pattern. I would either prefer to implement functions like: createPlayer(), create...() instead of comparing type strings (we're using OOP ;) ), or directly pass a pointer to the proper object by using the new statement.
For example something like:
objectmanager.register( ObjectFactory::createPlayer(), "an_unique_id" );
Yes, I already know about writing an object manager, but I am having issues writing the method to create instances through arguments.
void ObjectManager::addObject ( argument0 ) {
new argument0;
}
How do I make it create an instance of whatever is defined in argument0?
Oh I see, my old post got what you wanted wrong.
You seem to want to create an object instanced to a name, have a look at std::map
-Explination will be here soon-
Basically, the first thing you want after you've got your base class is an object manager, you want your objects to have names i'm assuming so you do something like this
class ObjectManager
{
private:
std::map<std::string, BaseClass*> tObjects; //We are making an associative array here, in other words the string will refer to the object
public:
void addObject(const std::string& name, BaseClass* data);
void step();
}
void ObjectManager::addObject(const std::string& name, BaseClass* data)
{
tObjects[name] = data; //Think of it sorta like a dynamic array that instead of using ints, uses strings, so we set the "Name" position to the "Data"
}
void ObjectManager::step()
{
for(std::map<std::string, BaseClass*>::iterator mIter = tObjects.begin(); mIter != tObjects.end(); mIter++)
{
mIter->second->step();
}
}
Maybe that is what you are looking for
Another example is the first class I made in SFML, I call it the resource manager, it simple handles Image data for me so that there is never more then one of the same image, while it doesn't use a Base class, it shows you how I associate a std::string to another object. Allowing you to name it as I believe you want to.
/// ---------- ResourceManager.hpp -----------
class ResourceManager
{
private:
static Logger* Log;
std::map<std::string, sf::Image*> Images;
public:
~ResourceManager();
static void setLogger(Logger* a) {Log = a;}
//Images
void loadImage(const std::string& name, const std::string& filepath);
const sf::Image& getImage(const std::string& name);
};
///-------------------------------------------------
///------------ ResourceManager.cpp ----------
Logger* ResourceManager::Log = 0;
ResourceManager::~ResourceManager()
{
for(std::map<std::string, sf::Image*>::iterator mIter = Images.begin(); mIter != Images.end(); mIter++)
{
delete mIter->second;
}
}
void ResourceManager::loadImage(const std::string& name, const std::string& filepath)
{
// Test if the key exists
if(Images.find(name) == Images.end())
{
Images[name] = new sf::Image();
if(!Images[name]->LoadFromFile(filepath))
Log->log("Error Loading Image: " + name + " at filepath: " + filepath, LOGTYPE_ERROR);
else
Log->log("Loaded image " + name, LOGTYPE_EVENT);
Images[name]->CreateMaskFromColor(sf::Color(255, 255, 255), 0);
}
else
{
Log->log("Attempt to re-load image " + name + " aborting load", LOGTYPE_ERROR);
}
const sf::Image& ResourceManager::getImage(const std::string& name)
{
// Test if the key exists
if(Images.find(name) == Images.end())
{
Log->log("Image " + name + " does not exist, a blank image will be returned", LOGTYPE_ERROR);
}
return *Images[name];
}
}
///-------------------------------------------------