Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I've been recently trying to get into Object Oriented Programming by porting a game I've made in C to C++. The problem is, even though my implementation works, I don't know if is the best approach.

The game is a tower defense, which contain 3 main elements: Tiles, Enemies and Towers. Each of the three elements have a "engine" class, which manipulates and allocates the individual objects. For example:

Tiles:

Class Tile
{
  private:
  int  posX, posY;
  bool visible;

  public:
  void DrawItself();

};

Class TileEngine
{
  private:
  Tile tileArray[m][n];

  public:
  int GetTileXPosition(int m, int n);
  int GetTileYPosition(int m, int n);
  void DrawArray();
};

Enemies:

Class Enemy
{
  private:
  int  posX, posY;
  bool health;

  public:
  void DrawItself();
  void MoveItself();

};

Class EnemyEngine
{
  private:
  Enemy enemyArray[m][n]


  public:
  void DrawArray();
  void MoveArray();
};

TileEngine and EnemyEngine are then instantiated on a Game class, and their public methods are then called on a loop:

Loop()
{
  TileEngine TileEngine;
  EnemyEngine EnemyEngine;
  /*FPS controlling routine omitted*/
  while(1)
  {
    EnemyEngine.Movement();

    TileEngine.DrawArray();
    EnemyEngine.DrawArray()
  }

} 

The problem is that the EnemyEngine.Movement() method requires information from the TileArray, of which it doesn't have access. My temporary solution has been giving the EnemyEngine class a pointer to the TileEngine class:

void Loop()
{
  TileEngine TileEngine;
  EnemyEngine EnemyEngine;

  EnemyEngine.pTileEngine = &TileEngine;

  while(1)
  {
    /*...*/
  }

} 

But doesn't that defeat the purpose of object oriented programming? Is there a better approach to get information between the "engine" classes? Or is the Element -> ElementEngine -> Gameloop implementation flawed altogether?

share|improve this question

closed as too broad by Philipp, bummzack, Byte56 Nov 22 '13 at 20:50

There are either too many possible answers, or good answers would be too long for this format. Please add details to narrow the answer set or to isolate an issue that can be answered in a few paragraphs.If this question can be reworded to fit the rules in the help center, please edit the question.

1  
I answered a similar question a while ago, because I don't want to repeat the same answer here is a link stackoverflow.com/questions/19935302/…. –  concept3d Nov 17 '13 at 17:51

2 Answers 2

up vote 2 down vote accepted

Long text incoming!

What makes OOP powerful is the Inheritance and Polymorphism not knowing exactly how much you know about this I will be very basic. A very common way of structuring a game with OOP is to have a base class that all the other "game objects"(tiles, enemies, players, towers, what have you) inherits from. That is, one class that has alot of children.

So for instance, you create a base class, lets call it GameObject. This class contains all that every object in the game needs, such as posision, rotation and so on.

Class GameObject
{
public: 
    GameObject();
    ~GameObject();
    virtual void update() {}; //virtual will let you rewrite the function
                              //in a child class.

protected:        //Protected lets the children be able to modify the variables
    int posX, posY;
};

Now your Tile and Enemy class can inherit from this class making them subclasses or "childs". That contains all that the "parent does so you don't have to write it again, in this case it's the "int posX, posY"

#include "GameObject.h"
Class Tile : public GameObject //Inheritance
{
public:
    void update(); //Add logic

private:
    bool visible;
    //automaticly has int posX, posY; since we inherit.
};

What this lets you do is to "trick"(not quite but it's easier to understand) the computer into thinking that a pointer to an Enemy or Tile object is a GameObject pointer. And thereby we can put all the object-pointers to objects that inherites from the GameObject class into one list. This will make the Game Loop look something like this:

void Loop()
{
    //creates the list
    std::vector<GameObject*> gameObjects;  //Create a vector or a regular array 
                                           //that can hold all your gameobjects
    //Fills the list
    gameOjbects.push_back(new Enemy());
    gameObjects.push_back(new Tile());
    gameOjbects.push_back(new Enemy());
    gameObjects.push_back(new Tile());
    gameOjbects.push_back(new Enemy());
    gameObjects.push_back(new Tile());

    //UpdateLoop
    while(true)
    {
        for(int i = 0; i < gameObjects.size(); i++)
            gameObject[i]->update(); 
        //This is where the virtual keyword comes in.
        //Here the compiler will regognize this as an Enemy- or Tile-pointer, not GameObject.
        //Having the update function in GameObject as "virtual"
        //will make the compiler take the Enemies or Tiles version of the function
        //if there is one. This is "Polymorphism".
    }
}

Now your Enemy.update() function still needs an array of tiles, and spontaneously I would do it sort of like you solved it.

#include "Tile.h"
#include <vector>
Class Enemy : public GameObject //Inheritance
{
public:
    void update(); //Add movement logic in .cpp file

    //This will set the pointer to the tiles array or vector.
    static void setTilesPointer(std::vector<Tile*>* tiles) { m_tiles = tiles; };

private:
    bool health;
    static std::vector<Tile*>* m_tiles; //The pointer to the tiles array or vector.
};

Then in the top of Enemy.cpp we add, so we don't get an unresolved external symbol error:

std::vector<Tile*>* Enemy::m_tiles = new std::vector<Tile*>();

And now you have to modify your loop function a bit:

void Loop()
{
    //Creates the lists
    std::vector<GameObject*> gameObjects;  //Create a vector or a regular array 
                                           //that can hold all your gameobjects
    std::vector<Tiles*> tiles;

    //Sets the pointer to the tiles
    Enemy::setTilesPointer(&tiles);


    //Fills the lists
    gameOjbects.push_back(new Enemy());
    gameOjbects.push_back(new Enemy());
    gameOjbects.push_back(new Enemy());
    tiles.push_back(new Tile());
    tiles.push_back(new Tile());
    tiles.push_back(new Tile());

    for(int i = 0; i < tiles.size(); i++)
        gameObject.push_back(tiles[i]);

    //UpdateLoop
    while(true)
    {
        for(int i = 0; i < gameObjects.size(); i++)
            gameObject[i]->update();
    }
}

Since the "m_tiles" variable in Enemy is static we only have to set it once and it will be the same for all Enemy objects.

Hope you got some deeper insight to the world of OOP and that I didn't waste your time :)

share|improve this answer
    
Thanks! I've actually been using polymorphism by deriving specified entities (such as FlyingEnemy from Enemy, which has its own pathing method instead of the default one). I think the biggest question with the OOP seems to be how those objects' containers are manipulated. Since the entities' vectors' logic are very different from each other (i.e. enemies would have a "spawn" method while tiles would have a map generation one), is it better to have classes made specifically to deal with those vectors (as in my original post) or have them all on a more generic class, as in your example? –  ememorais Nov 18 '13 at 22:29
    
Situational and taste. For the map generation I would recommend making it a static function in the Tile class that returns a vector of tile*. In the gameloop we have above: std::vector<Tiles*> tiles = Tile::generateMap();. As for the spawning of enemies it's sort of up to you if you want to keep a vector of the enemies in the gameloop, or a separate static "Spawning" class. –  kamden Nov 19 '13 at 13:12
    
Wops, pressed enter before I was done >.< If there is alot of info to keep track of then have a seperate class for it. However if you only need a 'vector' to know when to spawn a new batch of enemies I would not create a class for it. You could use something like: if(enmVector.Size() == 0){ currentWave++; enmVector = Enemy::spawnWave(currentWave); } or just change the if statment if it's time based spawning. I personally would use the gameObject 'vector' primarily for updating of objects and to send them to a Rendering class for drawing. –  kamden Nov 19 '13 at 13:28

I would recommend you to decouple the graphic engine from the game logic. The classes for the game logic should not have any code which is related to drawing their visual representation. The drawing should be done by a separate GraphicEngine class which receives a reference to all the game data it needs to visualize and then decides internally how to draw them.

When you decouple the implementation of game logic and the graphic engine, you can easily make changes to either without affecting the other. You could even switch from a 2d engine to a 3d engine later without having to touch any code in your game logic. On the other hand, when the graphic engine is unaware of how the game logic works, you can also easily reuse the graphic engine for an entirely different game without having to change it either.

Being able to easily replace and combine components of an application is one of the most useful applications of object-oriented programming.

Besides: Decoupling game code and drawing code makes it easier to prevent the typical beginner mistake of tying game mechanics to framerate, which leads to the game running faster or slower depending on how fast the users computer is.

share|improve this answer

Not the answer you're looking for? Browse other questions tagged or ask your own question.