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

Started by
3 comments, last by mikeman 17 years, 8 months ago
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?
Advertisement
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.
Ah, yes, function pointers. Thanks!
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 vectorstd::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]
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;}

This topic is closed to new replies.

Advertisement