Allegro.cc - Online Community

Allegro.cc Forums » Game Design & Concepts » A better way

This thread is locked; no one can reply to it. rss feed Print
 1   2 
A better way
SonShadowCat
Member #1,548
September 2001
avatar

So I could create a vector for say EnergyBolt and do this:

push_back( *pTheBolt);

right?

I could do this for every spell, but would it be the best way?

Korval
Member #1,538
September 2001
avatar

Quote:

So I could create a vector for say EnergyBolt and do this:

push_back( *pTheBolt);

right?

As I pointed out in my comments, classes derived from Entity store themselves in a list and take care of their own lifetimes. An EnergyBolt derives from the same class as a Player or a Creature or whatever else is out there. So you don't have to be concerned about having a vector of EnergyBolts.

Of course, that's how I would organize my Entity classes. You don't have to do that. But, if you don't, then you have to deal with managing objects allocated by spells. I would suggest using self-managing objects as much as possible.

nonnus29
Member #2,606
August 2002
avatar

One thing about Korvals example that may be confusing to ssc is Korval is separating the spell effect from the spell; so he is talking about two different things; the spell and the energy bolt the spell creates. So you only ever need one instance of a spell and this one spell instance can create multiple energy bolts.

Something that is confusing to me is the use of inner classes, maybe someone could explain that? I knew you can do it in java but I've never seen it done in c++ before. Why wouldn't energy bolt be a separate class?

gillius
Member #119
April 2000

There's not much technical reason to do inner classes in C++. It's a matter of phycological design. For example in Java Swing you can use an anonymous class, a nested private class, or a public class. Most people use the first or second method as external objects don't care about the mouse listener object that works only for one type of object.

Gillius
Gillius's Programming -- https://gillius.org/

SonShadowCat
Member #1,548
September 2001
avatar

Shouldnt SpellBolt and BoltSpell derive from spell since they all have the InvokeSpell function? And How else would you get the name of the spell then?

This part is confusing me to hell and back:

  class EnergyBolt : public Entity
  {
   //Do whatever you need to to create an entity
   //that tracks a target.
   //Note that all Entity classes add themselves to
   //the main entity list. They kill themselves when needed.
   //There is no need to keep a pointer of these around.
  }

public:
  virtual void InvokeSpell()
  {
    EnergyBolt *pTheBolt = new EnergyBolt();

Isnt this implying that SpellBolt can only make 1 instance of EnergyBolt? In InvokeSpell, im going to have create a switch arent I? So I can check which type of spell to invoke?

new EnergyBolt();

Why are there ()? Its a class so why does it look like a function.

vector<Spell *> m_SpellList;

Now, for this vector, At the beginning of the program, I add all the different types of spell categories( as I assume SpellBolt is a category and not an actual spell)?

I just dont get the SpellBolt class and EnergyBolt class. If SpellBolt is an actual spell,
then why do we need EnergyBolt?

And is BoltSpell cast when the spell hits the target?

Im sorry im bothering you so much, I dont understand why its troubling me so much.

Krzysztof Kluczek
Member #4,191
January 2004
avatar

Quote:

Why are there ()? Its a class so why does it look like a function.

Because it`s a constructor call, with no parameters in this case. Constructor is a class method that is named exactly like the class, eg.:
EnergyBolt::EnergyBolt();
If a class have no constructors written, no parameters constructor is assumed. The only thing it does is calling parent classes constructors (in this case none).

You can have many instances of an object this way. Example:
</code>void foo()
{
EnergyBolt *bolt; // 0

bolt = new EnergyBolt(); // 1
use_somewhere(bolt); // 2
bolt = new EnergyBolt(); // 1
use_somewhere(bolt); // 2
bolt = new EnergyBolt(); // 1
use_somewhere(bolt); // 2
}</code>
0 - create a pointer. No EnergyBolt objects yet.
1 - create EnergyBolt object, we have pointer to it stored in bolt
2 - use pointer somewhere, this procedure is responsible for placing the pointer in some safe location since we are going to lose it`s address (overwrite variable bolt).

If use_somewhere() won`t store the pointer then we have a memory leak since objects have memory allocated, but we don`t know where the memory (we`ve just lost all pointers to it and don`t know it`s location). Also there should be some routine that deletes these objects when they are no longer used.

As for the rest of code, I can`t understand it too. Maybe I`ll try to read it once more. :)

Korval
Member #1,538
September 2001
avatar

Quote:

Something that is confusing to me is the use of inner classes, maybe someone could explain that? I knew you can do it in java but I've never seen it done in c++ before. Why wouldn't energy bolt be a separate class?

Mostly for protection. Anyone #include-ing the header for SpellBolt can't start allocating EnergyBolt objects. It just prevents someone from hacking around the Spell-system.

C++ inner classes, btw, don't have a pointer to the class that they are defined in. Inner classes in C++ are purely for either logical grouping or for class protection, as I've used it for here.

Quote:

Isnt this implying that SpellBolt can only make 1 instance of EnergyBolt?

As I said, the comments are important:

The Comments said:

//Note that all Entity classes add themselves to
//the main entity list. They kill themselves when needed.
//There is no need to keep a pointer of these around.

'pTheBolt' is a local variable; it goes off the stack after creating it. So, if the above were not true, we would have just leaked memory.

Quote:

Now, for this vector, At the beginning of the program, I add all the different types of spell categories( as I assume SpellBolt is a category and not an actual spell)?

Bad assumption.

Look at my code. The Player::CastSpell takes a spell name. If that name isn't in the vector, then no spell is cast. As such all Spell instances that the Player can cast are stored in that object.

A category is a conceptual idea. Each spell could have its own individual class, or you could make it so that some spells have individual classes and others are all derived from a few classes that are mostly data driven.

Quote:

I just dont get the SpellBolt class and EnergyBolt class. If SpellBolt is an actual spell,
then why do we need EnergyBolt?

Because that's how I decided to do it. Classes derived from Spell only invoke the spell. Think of a Magic Missle spell. The spell itself only creates a homing missile; it is the missile that does damage.

Or, even better, think of a summoning spell. All the spell does is summon the creature. Once the creature is here, it's here; the spell is over the moment the creature arrives. It is a separation of the spell's invokation from it's effects (a create, a magical bolt, a visual effect, etc).

By doing this, you don't have to have a switch statement to pick a spell from a list; you just make spell classes and add them to the player's list. The CastSpell function, and the Spell's functions, do the rest.

Quote:

And is BoltSpell cast when the spell hits the target?

No. BoltSpell is just a different kind of spell. The SpellBolt fires an Entity that tracks the target. BoltSpell is an instantaneous spell. It displays a Sprite (a visual effect) and directly deals damage. BoltSpell relys on the Entity that it creates to do damage and any special effects when it hits.

SonShadowCat
Member #1,548
September 2001
avatar

I think im beginning to understand now. For SpellBolt and EnergyBolt, its the same as doing this:

class SpellLightning
{
  class LightningBolt : public Enitity

SpellLightning just creates the physical representation of the spell which is LightningBolt...ok, I understand now. And LightningBolt inherits say X_Pos, Y_Pos, Status, etc from its base Entity class right? Only functions in LightningBolt are the ones that are specific for that class?

I did ask this, SpellLightning is inherited from Spell right?

Now, in the InvokeSpell function, after I create a new spell instance, I should add it to the grand list/vector of spells that are currently active right? And then delete them as they hit targets, etc...ok.

For the BoltSpell class, SimpleSprite a class right?

james_lohr
Member #1,947
February 2002

gonna bookmark this thread - will be useful when I finally start using classes.

...I have to admit though, even after reading this entire thread, I'm still not convinced that classes are useful.

SonShadowCat
Member #1,548
September 2001
avatar

They are very useful, you'll find out when you start using them.

Korval
Member #1,538
September 2001
avatar

Quote:

I'm still not convinced that classes are useful.

Really?

Take the Entity class I was mentioning. Such a class does not need to be added to an Entity list manually, because every instance allocated will automatically put itself in that list. This is because the Entity constructor adds the Entity to the list. That way, you are guarenteed that all Entity objects are managed properly, because the constructor is always guarenteed to be called.

SonShadowCat
Member #1,548
September 2001
avatar

1 more question( aside form theones I ask in the last post), why do new EnergyBolt() and not new EnergyBolt?

Dont they both do the same thing?

ImLeftFooted
Member #3,935
October 2003
avatar

James Lohr said:

I'm still not convinced that classes are useful.

if for no other reason, classes are at least useful for defaulting values:

typdef struct cord{int x,y;}cord; //I'm really unfamilar with C, is this right?

//usage
cord player;
player.x = 0;
player.y = 0;

class cord{
public:int x,y;
cord() {x=y=0;}
};

//usage
cord player;

I like to think of classes as smart variables, until they get complicated anyway. then they're a bunch more.

edit:
say for example you wanted a variable that cant go below 5 or above 15

1class fi_int{
2private:
3 int var;
4public:
5 m_int() {var=5;}
6
7 void setVar(int v) {if(v<=15 && v>=5)var=v;}
8 int getVar() {return var;}
9};
10 
11//usage
12fi_int special_num;
13special_num.setVar(10);
14special_num.setVar(2);
15 
16getVar() == 10

you could go even farther and operator overload it

1class fi_int{
2private:
3 int var;
4public:
5 m_int() {var=5;}
6
7 void setVar(int v) {if(v<=15 && v>=5)var=v;}
8 int getVar()const {return var;}
9};
10 
11fi_int &operator=(fi_int& f, int v){
12 f.setVar(v);
13 return f;
14}
15 
16bool operator==(const fi_int& f, int v){
17 if(getVar() == v) return true;
18 return false;
19}
20 
21//usage
22fi_int special_num;
23special_num = 10;
24special_num = 2;
25 
26if(special_num == 2)//it doesnt
27if(special_num == 10)//it does!

ok, im probably getting way too complicated way too fast.. but, i think it shows some uses of classes, does it not?

edit2: whoops forgot to return the fi_int in operator=

Korval
Member #1,538
September 2001
avatar

Quote:

why do new EnergyBolt() and not new EnergyBolt?

Just my syntax. I usually put the parenthesis on objects I allocate.

james_lohr
Member #1,947
February 2002

Quote:

I'm still not convinced that classes are useful.

Sorry about the troll. I was just trying to prompt a few replies that would make the thread even more useful ;). Worked quite nicely indeed ;D.

Krzysztof Kluczek
Member #4,191
January 2004
avatar

I think, everything mentioned above can be done nicely in pure C too.

Creating Entity:

1Entity *new_Entity()
2{
3 Entity *ent = malloc(sizeof(Entity));
4 
5 // if most of the variables are 0 you can do this:
6 memset(ent,0,sizeof(*ent));
7 
8 // and other variables
9 ent->pos.x = 1;
10 ent->pos.y = 1;
11 
12 // put it into game logic
13 add_entity_to_list(ent);
14 
15 return ent;
16}
17 
18// code creating entity has the same complexity now
19Entity *e = new_Entity();

And smart variables:

void set_5_15(int *var,int val)
{
  if(val<5) *var=5;
  else if(val>15) *var=15;
  else *var=val;
}

// and usage:
set_5_15(&variable,value);

// or simplier using one of Allegro defines
variable = MID(5,value,15);

In fact, variable setting code looks really nice in C++, when operator = is used. The only problem is that operators are quite nice source of bugs. I made some time ago a class that implemented very large number`s operations with a bunch of operators including int conversion operators. As a result, when I was adding these numbers, they were converting to ints, adding up, and then converting to large numbers back! :P

I think, C++ operators and templates are quite useful, but when I don`t need these I write in C (but still using references, new and delete from C++).

 1   2 


Go to: