Exispistix wrote: |
---|
Would it be possible that you could show me an example, please (there would have to be some way in which the player's stats and enemy's stats can be compared)? |
Well players and enemies have mostly the same stats, right? Since both players and enemies are derived from GameObject, those stats can go in GameObject, and that class can do all the comparisons:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
class GameObject
{
//...
int attack;
int defense;
int health;
// ... etc
};
// Then you can have a member function to attack another GameObject:
void GameObject::AttackObject(const GameObject& opponent)
{
int damage = attack - opponent.defense;
if(damage < 0)
damage = 0;
opponent.defense -= damage;
// (or something like that -- your damage calculation I'm sure will differ)
}
|
Exispistis wrote: |
---|
When you say about the map being stored in an entirely different class, it is stored in a dynamically allocated 2D array and does not contain the player's coordinates. |
Who "owns" that 2D array (ie, who allocates it, loads it, deletes it)? From the looks of your code, I thought your MapFunctions class was the owner. If that is the case, then deriving players/enemies from MapFunctions is not a great idea.
That said, a map is a complex concept and should be more than just a simple 2D array. It should be encapsulated inside a class. The question now is how do you make that class, and how do your other classes access it?
Very generally speaking, there are 3 major kinds of relationships your classes will have. Determining how to make these classes interact is often as simple as identifying which of the below relationships match the best and writing the implementation to match. Of course, it's not always so cut-and-dry, but these rules are a good "general case" and will work most of the time:
1) ClassA "is a" ClassB Inheritance
Poodle "is a" Dog
Orc "is an" Enemy
Player "is a" GameObject
etc
|
class Orc : public Enemy { /*...*/ };
|
In these cases, the parent class is a general class, and the child class is a more specific kind of that general class. It can get more general the higher up the hierarchy you go:
Poodle is a Dog, Dog is an Animal, Animal is a LivingBeing, etc.
The advantage to using inheritance is that you can write code that works for the general parent class, and it will apply to ALL of the more specific child classes. That is, any code written for 'Dog' will apply to Poodles, Greyhounds, Dalmations, etc. But things specific to only one kind of Dog can be kept in the child classes.
2) ClassA "has a" ClassB or
ClassA "owns a" ClassB Composition
A Player "owns a" Weapon. A Player "has a" health value.
1 2 3 4 5 6
|
class Player
{
//...
Weapon weapon;
int health;
};
|
Here, these things are owned by the Player. In this case you do not derive Player from any other classes (deriving from Weapon would make Player inherit weapon properties, which might be functional, but it implies that a Player "is a" weapon, which is nonsense). Rather, you simply give the player member variables.
The Player class then "owns" those variables and is responsible for keeping them in the correct state. That is, if allocating memory is required, then the Player class should be the only part of your program allocating/freeing memory because it's the owner. It should also be the only class that is modifying or really even accessing these values.
This 3rd one is a little trickier to explain. It's also the most complicated to implement:
3) ClassA "knows of a" ClassB or
ClassA "uses an existing" ClassB accessing by pointer/referece
Here we have a Player and some Enemies. We also have a Map. But how does "Map" fit in with the other classes?
A Player is not a Map, so inheritance doesn't work.
Also, a Player does not own the Map, so using composition also wouldn't fit very well.
The idea here, is that Map is owned elsewhere, and Player/Enemy need access to it. They know about it, and interact with it, but they do not actually own it themselves.
1 2 3 4 5 6 7 8 9 10 11 12
|
class Map
{
/* ... */
};
class Player
{
/*...*/
Map* map;
int posX;
int posY;
};
|
This is tricky because now you have external classes referring to an object they don't own, which makes things more complicated. Particularly, you need to make sure that none of these classes are still using the Map when the Map is destroyed, or else their pointer will go bad.
This is most easily accomplished by having all of these classes owned by a larger class which manages the interactions between them. In games I often use a "GameWorld" or similar class.
1 2 3 4 5 6
|
class GameWorld
{
//...
Map map; // the game world owns the map
std::list<GameObject*> objects; // it also owns a all objects that exist in the world
};
|
This way, the GameWorld class can manage the "big picture" stuff and the smaller Player/Enemy/GameObject/Map classes only have to worry about their respective roles.
Anyway, blah blah blah. Tl;dr. Just spitballing ideas. Take whatever you want from this post.