Skip to main content
gamedev.net
🔒 Locked

C++ templates and skill bar

Started by CirdanValen Apr 19, 2011 at 2:40 AM 8 replies 3.3k views
Original Post
CirdanValen
CirdanValen
I'm designing a skills and skill bar system for my game, similar to how most MMORPGs work. I have run into a problem of how to organize the code.

Here is what my system looks like so far. I have a Skill class which has a template. I pass the missile class as the template class for the skill. When I call a method to active the skill, it creates and returns the correct missile type. I went on to implement a skill bar, with a method that allows an outside class to assign a slot a skill. The method looks like this: assignSkill(Skill* s, int slot). Missile is the base class for the projectile or object that the skill creates. So if I want to create a skill for a shield as such Skill, I cannot pass it to assignSkill because the signatures don't match, despite the fact that Shield is a child of Missile.

The only other way I could see to fix this is remove the template, make each skill inherit from the Skill class and make each skill spawn a Missile. However, the ONLY different between the skills is the missile they create. It seems too messy to me to have to create a bunch of subclasses just to change the type of missile it returns. Any tips or ideas? Thanks
SiCrane
SiCrane
If I understand what you're asking, just derive your Skill<> template from a non-template base class.
SimonForsman
SimonForsman
You could just skip the template entierly and assign the missile in the skill constructor.

Thus you'd have:

class Skill {
private:
Missile *missile;
std::string name;
Icon *icon;
.......
public:
Skill(std::string skillname, Missile *missile, Icon* icon .... (other relevant values));
......
}

Since your missiles allready inherit from the Missile class there is no need to complicate the Skill class. (I'd argue that you don't really want to inherit Missile either, personally i'd go with a missile class containing a set of scripts and properties. (If you don't have a scripting language you can replace scripts with function pointers, you'd probably only need scripts/functions for initializing the missile(s), updating it, and responding to various outside events (collisions primarily i guess))

The advantage of this is that you can load your Missiles from datafiles that look something like:

Fireball
icons/fireball.png
500
60
40
GenericBoltUpdate.lua
FireballCollision.lua
etc.

Creating new missiles by mixing existing behaviours becomes trivial and tweaking stats won't require you to recompile your game.(Without a scripting system you could just store a index number for the function it should use instead)
[size="1"]I don't suffer from insanity, I'm enjoying every minute of it.
The voices in my head may not be real, but they have some good ideas!
CirdanValen
CirdanValen

If I understand what you're asking, just derive your Skill<> template from a non-template base class.


*smacks forehead* Thanks, seems so obvious now!
CirdanValen
CirdanValen
So tonight I continued working on my game, but ran into a problem with the solution posted above. I can't pass the missile to the skill, because the skill needs to be able to create new missile objects when it's activated. I created the base for the Skill class as was suggested by SiCrane:

class SkillBase {
public:

virtual void clicked() { }
virtual void unclicked() { }
virtual void setPosition(int x, int y) { }
virtual Missile *castSkill() { return NULL };
virtual void tick(Graphics *g, int delta) { }

};


However, when I call castSkill...it calls this method instead of the one in it's child class Skill. I can't cast as Skill because then I would have to provide the template type...which I can't specify. It works if I cast it to Skill, however, when it creates the missile, it calls the Missile's constructor...not the overloaded one. From this I've learned that templates are not very friendly when it comes to inheritance and polymorphism.

Templates are causing more problems than they solve it seems. Looks like I'm going to have to take the route of sub classing Skill for each skill.
SiCrane
SiCrane
It sounds like it should work. What does your Skill template class look like?
CirdanValen
CirdanValen
It looked something like (I don't have the exact code atm), so here is some psuedo code


template<class T>
class Skill : public SkillBase {
public:
Skill( ... ) {
// I had a BOOST function call to assert that T was a subclass of Missile at compile time
}

Missile* activateSkill(...) {
return new T(...);
}
};



So what I would do with this is


mCannonSkill = new Skill<Cannon>(...);

if(keyPressed) {
mCannonSkill->activateSkill(...)
}


This worked fine. The problem came when I wanted to put this within a SkillBar class to manage the skills. I created a std::map so I could assign skills to a specific slot. To activate a skill, I would call mSkillBar->activeSkill(0) to activate the skill in the first slot, which looks like this:

Missile *SkillBar::activateSkill(int slot) {
return ((Skill<Missile>*)mSkillSlots[slot])->castSkill(0, 0, NULL);
}


I want the slots to be dynamic so eventually the player can choose their skill set. With the above method, when castSkill is called and creates the new missile, it calls the constructor of the Missile class instead of the overloaded constructor of the Cannon class. If I don't have the cast in front of the slot map, this method will call SkillBase's castSkill method and return a NULL object. I don't think there is a way to specify the type at run time like this with templates, so this method won't work for my situation.

Going the route of subclassing Skill should work fine...just need to figure out how to name the classes so that there aren't any name collisions between the actual Missile and the Skill.
Malal
Malal
What about changing

virtual Missile *castSkill() { return NULL };

to

virtual Missile *castSkill() = 0;
rip-off
rip-off
You seem to have two functions, castSkill() and activateSkill(). One appears to take arguments, the other does not. Is your real code like this? The cast in SkillBar::activateSkill() should be unnecessary. Never use C style casts on pointers unless you are 100% sure of what you are doing.

Here is a minimal program demonstrating the basic ideas:
[source lang="c++"]
#include
#include

class Missile {
public:
virtual ~Missile()
{
}

virtual void show() = 0;
};

class Shell : public Missile {
public:
virtual void show()
{
std::cout << "Shell\n";
}
};

class FireBall : public Missile {
public:
virtual void show()
{
std::cout << "Fireball\n";
}
};

class SkillBase
{
public:
virtual Missile *activateSkill() = 0;
};

template
class Skill : public SkillBase {
public:
Skill()
{
// I had a BOOST function call to assert that T was a subclass of Missile at compile time
}

Missile* activateSkill()
{
return new T();
}
};

class SkillBar {
public:
Missile *activateSkill(int slot)
{
return skills.at(slot)->activateSkill();
}

void addSkill(SkillBase *skill)
{
skills.push_back(skill);
}

private:
std::vector skills;
};

int main()
{
Skill cannon;
Skill magic;

SkillBar bar;
bar.addSkill(&magic);
bar.addSkill(&cannon);

for(int i = 0 ; i < 2 ; ++i)
{
Missile *missile = bar.activateSkill(i);
missile->show();
delete missile;
}
}
[/source]
If you're still having trouble, can you produce a similar version of your own code, something small that we can compile and run that demonstrates the problem.
CirdanValen
CirdanValen
So I took your code and modified it slightly to match my situation a bit closer and, to my surprise, it worked as expected! (http://pastebin.com/dTwH9WUx) When I tried to add the skill to the skill bar through assignSkillSlot(), VC was telling me that Skill<> was not compatible with SkillBase *. I think I cast Skill<> to SkillBase, which would cause the base class' constructors to be called and the activateSkill method to return NULL. Instead of casting, I included "SkillBase.h" in the Skill header, which made it recognize that they were compatible and now works as I want it. VC is telling me it can't find "SkillBase.h" even though it's clearly there, in the same directory and the project compiles fine. That would explain why I cast instead of including SkillBase.h in the first place.

Thanks for your help guys. I guess I still have a lot to learn, and that's what I hope to accomplish with this project.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.