Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Flexible Object Manager  (Read 8794 times)

0 Members and 1 Guest are viewing this topic.

e_barroga

  • Jr. Member
  • **
  • Posts: 76
    • View Profile
Flexible Object Manager
« 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:

Code: [Select]

Create()

Step()

Destroy()


This way, I can easily write new object classes without messing with the engine in its entirety.


Code: [Select]

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]

klusark

  • Newbie
  • *
  • Posts: 45
    • View Profile
Flexible Object Manager
« Reply #1 on: January 10, 2009, 08:17:56 am »
For looping though you could use std::vector

Tank

  • SFML Team
  • Hero Member
  • *****
  • Posts: 1486
    • View Profile
    • Blog
    • Email
Flexible Object Manager
« Reply #2 on: January 10, 2009, 12:20:18 pm »
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).

e_barroga

  • Jr. Member
  • **
  • Posts: 76
    • View Profile
Flexible Object Manager
« Reply #3 on: January 11, 2009, 04:32:33 am »
One thing that stumps is how to properly call the specific object when I want to create it:

Code: [Select]

instance_create( <name of object> ) {
  <name of object> newObj;
  instances.push_back( newObj );
}


I want to use it in the main loop like so:
Code: [Select]

instance_create( test );



Also.... when initializing the instance list, it doesn't seem to recognize the child objects. I did the following:

Code: [Select]

std::vector <base> instances;


I've created a child object:
Code: [Select]

class test: public base {
  public:
    void step() {
      std::cout << "Testing\n";
    }
};


Then I have the following in my main loop:
Code: [Select]

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:
Code: [Select]

std::vector <test> instances;


And it outputs the text repeatedly, as I wanted.

Tank

  • SFML Team
  • Hero Member
  • *****
  • Posts: 1486
    • View Profile
    • Blog
    • Email
Flexible Object Manager
« Reply #4 on: January 11, 2009, 12:15:28 pm »
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.

Code: [Select]

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.

e_barroga

  • Jr. Member
  • **
  • Posts: 76
    • View Profile
Flexible Object Manager
« Reply #5 on: January 11, 2009, 11:42:47 pm »
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:

Code: [Select]

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?

Tank

  • SFML Team
  • Hero Member
  • *****
  • Posts: 1486
    • View Profile
    • Blog
    • Email
Flexible Object Manager
« Reply #6 on: January 12, 2009, 12:41:48 am »
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.

e_barroga

  • Jr. Member
  • **
  • Posts: 76
    • View Profile
Flexible Object Manager
« Reply #7 on: January 13, 2009, 03:35:14 am »
How would I properly execute this:
Code: [Select]


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();
  }
}

klusark

  • Newbie
  • *
  • Posts: 45
    • View Profile
Flexible Object Manager
« Reply #8 on: January 13, 2009, 05:38:16 am »
You could get the name of object as a string and compare it to all the possibilities.
Example from my game
Code: [Select]
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;
}
}

Tank

  • SFML Team
  • Hero Member
  • *****
  • Posts: 1486
    • View Profile
    • Blog
    • Email
Flexible Object Manager
« Reply #9 on: January 13, 2009, 03:18:22 pm »
e_barroga:
Please read the posts more carefully. :)

Using an object manager class, your code could look like as follows:
Code: [Select]

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:
Code: [Select]

objectmanager.register( ObjectFactory::createPlayer(), "an_unique_id" );

e_barroga

  • Jr. Member
  • **
  • Posts: 76
    • View Profile
Flexible Object Manager
« Reply #10 on: January 14, 2009, 02:45:10 am »
Quote from: "Tank"
e_barroga:
Please read the posts more carefully. :)

Using an object manager class, your code could look like as follows:
Code: [Select]

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:
Code: [Select]

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.

Code: [Select]

void ObjectManager::addObject ( argument0 ) {
  new argument0;
}


How do I make it create an instance of whatever is defined in argument0?

Wizzard

  • Full Member
  • ***
  • Posts: 213
    • View Profile
Flexible Object Manager
« Reply #11 on: January 14, 2009, 03:02:41 am »
Quote from: "e_barroga"
How do I make it create an instance of whatever is defined in argument0?

You should look into STL containers.

dorkfish

  • Newbie
  • *
  • Posts: 38
    • View Profile
Flexible Object Manager
« Reply #12 on: January 31, 2009, 09:26:42 pm »
Quote from: "e_barroga"
Quote from: "Tank"
e_barroga:
Please read the posts more carefully. :)

Using an object manager class, your code could look like as follows:
Code: [Select]

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:
Code: [Select]

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.

Code: [Select]

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.

Tank

  • SFML Team
  • Hero Member
  • *****
  • Posts: 1486
    • View Profile
    • Blog
    • Email
Flexible Object Manager
« Reply #13 on: February 01, 2009, 01:05:17 pm »
Quote from: "e_barroga"
How do I make it create an instance of whatever is defined in argument0?

I gave you a concrete example. Hint:

Code: [Select]
objectmanager.register( ObjectFactory::createPlayer(), "an_unique_id" );

t0rento

  • Newbie
  • *
  • Posts: 11
    • View Profile
Flexible Object Manager
« Reply #14 on: February 05, 2009, 05:29:41 am »
Quote from: "e_barroga"
Quote from: "Tank"
e_barroga:
Please read the posts more carefully. :)

Using an object manager class, your code could look like as follows:
Code: [Select]

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:
Code: [Select]

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.

Code: [Select]

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

Code: [Select]

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.
Code: [Select]

/// ---------- 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];
}
}
///-------------------------------------------------