Sign in to follow this  

[C++] Mapping ints to classes. Ints decide which class to instantiate. How?

This topic is 4132 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hey! I'm pretty new to C++, and I'm thinking a bit ahead. I'm planning to make a game and one particular problem would be that a user would click on an icon to cast a spell. Spells are represented by classes and casting a new spell would instantiate the appropriate class. The clicking of the icon (or pressing of the key) generates an int that is sent from the client to server where the instantiation takes place. The question is, as you may have guessed: How can I slove this problem without the use of a huge switch statement?

Share this post


Link to post
Share on other sites
Let's assume all your classes derive from a base class Spell. You can then create factory functions with the signature Spell * (void). Ex:

Spell * create_fireball(void) {
return new Fireball();
}

Then you can create a std::map<int, Spell * (*)(void)>, a map of ints to function pointers, which you can then fill with statements like:

spell_map[FIREBALL_NUMBER] = &create_fireball;


Variations: return smart pointers instead of raw pointers; use boost::function<Spell *(void)> instead of function pointersl; use a hash_map/unorder_map in place of a std::map.

Share this post


Link to post
Share on other sites
Or you can use the prototype pattern. in a very similar fashion.

In the prototype pattern your spell class implements a copy function that returns a pointer to a new copy of it self.

For example
class Spell
{
public:
virtual Spell* copy() =0;
};

class FireBall :public Spell
{
public:
Spell* copy()
{
return new FireBall;
}
};
class LightningBolt :public Spell
{
public:
Spell* copy()
{
return new LightningBolt;
}
};


Then Instead of a map with function pointers you have a map with Spell pointers, each initialized to a different subclass of spell. In fact it can be a simple vector which will access in constant time.

//create the vector
std::vector<Spell*> SpellVector;

//initialze the prototypes;
SpellVector[FIREBALL] = new FireBall;
SpellVector[LIGHTNING] = new LightningBolt;
SpellVector ...... ect.


//create a new instance.
Spell* MynewSpell = SpellVector[FIREBALL]->copy(); // Just replace the FIREBALL enum with a user inputted number.



Taking it a step further, You could wrap that vector in a SpellFactory

class SpellFactory
{
std::vector<Spell*> SpellVector;
public:
SpellFactory()
{
//initialze the prototypes;
SpellVector[FIREBALL] = new FireBall;
SpellVector[LIGHTNING] = new LightningBolt;
SpellVector ...... ect.
}

Spell* CreateSpell(int spell_ID)
{
return SpellVector[spell_ID]->copy();
}

};



Then all you need to do in your main game program.

SpellFactory TheSpellFactory;

Spell* MynewSpell = TheSpellFactory.CreateSpell(FIREBALL);


[Edited by - Grain on August 20, 2006 5:48:17 PM]

Share this post


Link to post
Share on other sites
As Sicrane said, I reccomend using Boost::function to do this. Also, try to parameterize your spell classes as much as possible. For example, don't make a StrongFireSpell and a WeakFireSpell class when you can have a single FireSpell class with a 'power' parameter. To enhance your program in this case, use boost::bind in conjuction with boost::function, that allows you to "store" the parameters of the function that will be called later. A short example:


class Spell
{
public:
virtual void Cast()=0;
};

class FireSpell:public Spell
{
private:
float m_power;
public:
FireSpell(float power):m_power(power){}
virtual void Cast()
{
std::cout<<"Cast firespell with power "<<m_power<<std::endl;
}
};

class IceSpell:public Spell
{
private:
float m_power;
float m_duration;
public:
IceSpell(float power,float duration):m_power(power),m_duration(duration){}
virtual void Cast()
{
std::cout<<"Cast IceSpell with power "<<m_power<<" and duration "<<m_duration<<std::endl;
}
};

Spell* CreateFireSpell(float power)
{
return new FireSpell(power);
}

Spell* CreateIceSpell(float power,float duration)
{
return new IceSpell(power,duration);
}

class Icon
{
public:
boost::function< Spell*(void) > OnClick;
};


int main()
{
std::map<std::string,Icon> icons;

icons["WeakFireSpell"].OnClick=boost::bind(CreateFireSpell,10.0);
icons["StrongFireSpell"].OnClick=boost::bind(CreateFireSpell,100.0);
icons["WeakAndShortIceSpell"].OnClick=boost::bind(CreateIceSpell,10.0,3.0);
icons["WeakAndLongIceSpell"].OnClick=boost::bind(CreateIceSpell,10.0,30.0);
icons["StrongAndShortIceSpell"].OnClick=boost::bind(CreateIceSpell,100.0,3.0);
icons["StrongAndLongIceSpell"].OnClick=boost::bind(CreateIceSpell,100.0,30.0);

std::string icon_name;
std::cout<<"Enter icon name to click: ";
std::cin>>icon_name;
if (icons.find(icon_name)!=icons.end())
{
Spell* spell=icons[icon_name].OnClick();
spell->Cast();
}
else std::cout<<"No such icon exists."<<std::endl;


}


Share this post


Link to post
Share on other sites

This topic is 4132 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this