Game Engine design problem

Hello again. I have question concerning polymorphism. I´m reasonably aware of inheritance, virtual functions etc, but when I was creating my game engine project I ended up into a design problem. First, take a look at this:

virtual bool Face(Characters::__Character*);

This virtual function is protected. It is supposed to be used by the character (via inheritance). It takes character as it´s parameter and handles it appropriately for the character using it. Now, __Character is a base class that is not supposed to be used by the user of my game engine. I though I could´ve created many virtual functions like this:

1
2
virtual bool Face(Characters::Player);
virtual bool Face(Characters::Enemy);


...and so on.

But then I realized that there could be handier way to program this kind of program, because all characters (Enemy & Player) are inherited from base class called __Character. But then I was wondering "How?". I though pointers would help, but I don´t know what would be solution clever enough to this problem.

Free feel to ask if something feels vague.
Last edited on
I don't think I understand your problem.
If you pass __Character*, you'll be able to use all virtual functions it has.
If you want to hide __Character from the user of your engine, don't. There is no reason to.
If you need to know whether the __Character* passed actually points to Player or Enemy object, look into dynamic_cast keyword.
Alright, let me explain my problem bit better. So, when I create my virtual function, I´d like to use this virtual function to be compatible with all character classes I´m going to inherit from __Character. dynamic_cast -keyword is good idea, but how should I implement my solution?
What is compatibility? Virtual functions will work. If you have some non virtual ones, use dynamic_cast to find the type at run time. Though you should try to stick with virtual ones.. Should I show what dynamic_cast does?
Also, what is the purpose of that function?
The purpose of this "Face" -function is to check what game character (enemy/player) is facing. Here´s an example:

1
2
3
if(OurHero->Faces(Enemy)){
    // do something
}


But because __Character isn´t Enemy, I can´t use __Character as parameter, because __Character is a base class and doesn´t have all the functionality as Enemy does. However, I´m aware there is some kind of way to use derived classes from their base class(es), but how I´m supposed to do it?
Last edited on
Do you really need to know the type of the argument to know whether you are facing it? It seems to be a problem only concerning coordinates..
We may be failing to communicate properly..
Anyway, on dynamic_cast,
1
2
3
4
5
6
7
bool Face( __Character* c ){
   Enemy* e = dynamic_cast<Enemy*>( c );//dynamic cast returns 0 if conversion is not valid.
   if( e != 0 ){
      //here you know that 'c' pointed to an Enemy.
   }
   //...
}
We may be failing to communicate properly..
I may be failing to explaining what I mean.

Perhaps the level of abstraction is too high, so I think I just have to ask: "How could I implement a function checking distance from other character while the type of the character (enemy/player, whatever) does not matter/is unknown?
Last edited on
Since, I assume, all characters are capable of having a position, this should only concern __Character class. Add x, y members to it. If you want to keep everything neatly encapsulated, add functions to get and set the position. If you want __Character to have no member variables (though there is no reason why you would), make the get and set functions virtual and implement them for each child.
Also, the distance function doesn't need to be virtual, if it is going to be the same for all children.
It could look something like
1
2
3
float __Character::distance( __Character* c ) {
   return sqrt( (x - c->x)*(x - c->x) + (y - c->y)*(y - c->y) );
}
closed account (D80DSL3A)
I've been tinkering with an example which may help to show how it can work.
Here the base class (character) defines position (x,y) which all characters have. The player and enemy classes inherit these members.

The base class defines a distance method which works to find the distance between any 2 characters, regardless of type. This doesn't need to be virtual. Two versions are defined. One takes a character reference as argument and the other takes a character pointer.

The base class also introduces a virtual method showMe() which allows a character type to be shown. This method is also defined in the derived (enemy and player) classes.

The base class has a showUs() method which calls the virtual showMe() functions so that both the calling object and the passed object types can be shown.

Note that all of these methods take a character reference or pointer as arguments, showing that any derived type may be passed as well.

Hope this helps to clarify how to do this kind of stuff. The program is complete so you can run it and see how it works.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include <cmath>// for sqrt()
#include <iostream>// for I/O
using namespace std;

// base class
class character
{
public:
	float x;
	float y;
	float dist( const character& c ){ return sqrt( (x-c.x)*(x-c.x) + (y-c.y)*(y-c.y) );	}
	float dist( const character* pc ){ return sqrt( (x-pc->x)*(x-pc->x) + (y-pc->y)*(y-pc->y) ); }

	virtual void showMe(void){ cout << "Character"; }// derived objects define what this means in their context
	void showUs( character* pc )
	{
		cout << "Caller is :"; this->showMe(); cout << endl;
		cout << "Passed is :"; pc->showMe(); cout << endl << endl;
	}
	// constructor
	character(float X, float Y): x(X), y(Y){}
};

class enemy: public character
{
	int E1;// some property
public:
	enemy(int e1, float X, float Y): E1(e1), character(X, Y){}
	void showMe(void){ cout << "Enemy"; }
};

class player: public character
{
	int P1;// some property
	int P2;// another property
public:
	player(int p1, int p2, float X, float Y): P1(p1), P2(p2), character(X, Y){}
	void showMe(void){ cout << "Player"; }
};

int main(void)
{
	character cA( 5.0f, 5.0f );
	enemy eA( 1, 0.0f, 0.0f ), eB( 2, 3.0f, 4.0f );
	player pA( 3, 4, 1.0f, 1.0f ), pB( 5, 6, 2.0f, 3.0f );

	// mix derived types in base function call
	cout << "distance between enemies = " << eA.dist( eB ) << endl;
	cout << "distance between pA and eB = " << pA.dist( eB ) << endl;

	// again using base pointers
	character* pChars[] = { &cA, &eA, &eB, &pA, &pB };
	cout << "distance between characters 1 and 3 = " << pChars[1]->dist( pChars[3] ) << endl << endl;

	// virtual function calls using base pointers
	pChars[0]->showUs( pChars[4] );// character and player
	pChars[1]->showUs( pChars[2] );// 2 enemies
	pChars[3]->showUs( pChars[2] );// player and enemy

	cout << endl;
	return 0;	
}

Wow, I´d better familiarize myself with these two posts. They really look promising. It seems you finally understood what I was trying to explain. Thanks, hamsterman & fun2code. It´s fun to code!
HenriK wrote:
The purpose of this "Face" -function is to check what game character (enemy/player) is facing.
But because __Character isn´t Enemy, I can´t use __Character as parameter, because __Character is a base class and doesn´t have all the functionality as Enemy does.
The question you need to ask yourself is, "do I need that extra functionality to determine what my character is facing?" If the answer is yes, then you should define separate face methods in each class; if no, then you should define the method in the character (parent) class, possibly taking a character as a parameter. Example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <iostream>
#include <string>

using namespace std;


struct Person {
   const string name;

   Person(string n = "Person") : name(n) {}

   void say(string &msg) {
      cout << name << ": " << msg << endl;
   }

   void sayTo(const Person &p, string msg) {
      cout << "(@" << p.name << ") ";
      say(msg);
   }
};


class Student : public Person {
   char grade;

 public:
   Student(string n = "Student", char g = 'A') : Person(n), grade(g) {}

   char getGrade() { return grade; }
   
   void setGrade(char g) {
      if( 'A' <= g && g <= 'F' )
         grade = g;
      else
         throw g;
   }
};


int main() {
   Student s("Mathhead200"),
           t("HenriK", 'B');
   s.sayTo(t, "Do you understand?");
   return 0;
}
(@HenriK) Mathhead200: Do you understand?
Last edited on
Yes, Mathhead200, I understand. But if I'd have files called "__Character.hpp", "Player.hpp" & "Enemy.hpp", how can I create member function into __Character -base class detecting any character inherited from this __Character -base class while I use this member function called "Face"?

Let me tell how my game should work.

In game, we have a character. It can be player or enemy. Naturally when we play, we play as player. Player can face enemy. Enemy can face player. Now, however, if Player & Enemy -classes are defined in different header files, how can we have some kind of interaction between them? Both Player & Enemy -class must have face function. It wouldn´t make any sense if player is the only character that can check whether it is facing something.
Last edited on
It doesn't matter what files you have. Face method will be inherited.
HenriK wrote:
Player can face enemy. Enemy can face player.
Mathhead200 wrote:
"do I need that extra functionality to determine what my character is facing?" ... if no, then you should define the method in the character (parent) class


Look at my example again. On line 43 I'm invoking the sayTo method on a Student. The method was defined in the parent class Person. This is an example of inheritance. I'm passing (as the first argument) a Student to the method. The method declaration asked for a Person&, but this is fine because a Student is a Person. This is an example of polymorphism.

If any character can face any other character, regardless of whether they are a player, enemy, or some other character derivative you have yet to write, then the method should be define once inside of the character class.
Last edited on
Topic archived. No new replies allowed.