Difference between revisions of "Complete roguelike tutorial using C++ and libtcod - part 2: map and actors"

From RogueBasin
Jump to navigation Jump to search
(→‎Actor class implementation: el for console methods)
Line 58: Line 58:


The render function uses some [http://roguecentral.org/doryen/data/libtcod/doc/1.5.1/html2/console_draw_basic.html?c=false&cpp=true&cs=false&py=false&lua=false#4 console methods] to set the ascii code and foreground color on the root console, leaving the background color unmodified.
The render function uses some [http://roguecentral.org/doryen/data/libtcod/doc/1.5.1/html2/console_draw_basic.html?c=false&cpp=true&cs=false&py=false&lua=false#4 console methods] to set the ascii code and foreground color on the root console, leaving the background color unmodified.
==Map class declaration==
Once again, we create two files, Map.hpp and Map.cpp.
Map.hpp
struct Tile {
    bool canWalk; // can we walk through this tile?
    Tile() : canWalk(true) {}
};
class Map {
public :
    int width,height;
    Map(int width, int height);
    ~Map();
    bool isWall(int x, int y) const;
    void render() const;
protected :
    Tile *tiles;
    void setWall(int x, int y);
};
There are actually two classes in there. In C++, you can declare as many classes as you want in a header file. I consider the Tile class not being big enough to have its own files.
Right now, the Tile only has a single boolean field but it's obvious that we'll add more properties later.
Note that the Tile declaration uses the struct keyword instead of class. They're basically the same except that the field visibility defaults to public in a struct whereas it's private in a class (that's why we add public: in the beginning of our classes). I generally use structs for data-only classes.
The Map class has a Tile pointer member. Since we don't want to hardcode the map size, we will dynamically allocate an array of Tiles. The tiles field will contain the address of the first element of the array.
Since that field will be dynamically allocated, we need to delete it to release the memory when the Map object is destroyed. That's why we declare a destructor.

Revision as of 12:06, 27 September 2015

Complete roguelike tutorial using C++ and libtcod
-originally written by Jice
Text in this tutorial was released under the Creative Commons Attribution-ShareAlike 3.0 Unported and the GNU Free Documentation License (unversioned, with no invariant sections, front-cover texts, or back-cover texts) on 2015-09-21.


In the first part, we created a "walking @" demo. Let's add a NPC and some walls !

libtcod functions used in this article

TCODConsole:setChar

TCODConsole::setCharForeground

TCODConsole::setCharBackground

Actor class declaration

We'll create two files in the src/ directory. One Actor.hpp header and one Actor.cpp module. The header contains the declaration of the class, the module contains the actual code. I'm using the hpp extension for headers but you can use hxx or h as well.

Actor.hpp

class Actor { 
public :
   int x,y; // position on map
   int ch; // ascii code
   TCODColor col; // color

   Actor(int x, int y, int ch, const TCODColor &col);
   void render() const;
};

A few remarks :

we're using an int to store the ascii code of the character representing the Actor instead of a char or unsigned char because it makes it possible to use more than 256 different characters. the color in the constructor is passed as a const reference (syntax const <type> & varname) to keep the compiler from duplicating the object before calling the function. the const keyword after the render declaration means that the function does not modify the content of the Actor object (and thus, the function can be called on constant objects).

Actor class implementation

Actor.cpp

#include "libtcod.hpp"
#include "Actor.hpp"

Actor::Actor(int x, int y, int ch, const TCODColor &col) :
   x(x),y(y),ch(ch),col(col) {
}

void Actor::render() const {
   TCODConsole::root->setChar(x,y,ch);
   TCODConsole::root->setCharForeground(x,y,col);
}

We need to include libtcod.hpp before Actor.hpp because Actor.hpp contains references to TCODColor.

The constructor uses an initialization list to define the value of the class' members :

Actor::Actor(int x, int y, int ch, const TCODColor &col) :
   x(x),y(y),ch(ch),col(col) 

It's often faster (especially for non intrinsic members) so you should as often as possible use that instead of affectations in the constructor. The fields in the initialization list must appear in the order of their declaration in the .hpp file.

The render function uses some console methods to set the ascii code and foreground color on the root console, leaving the background color unmodified.

Map class declaration

Once again, we create two files, Map.hpp and Map.cpp.

Map.hpp

struct Tile {
   bool canWalk; // can we walk through this tile?
   Tile() : canWalk(true) {}
};

class Map {
public :
   int width,height;

   Map(int width, int height);
   ~Map();
   bool isWall(int x, int y) const;
   void render() const;
protected :
   Tile *tiles;

   void setWall(int x, int y);
};

There are actually two classes in there. In C++, you can declare as many classes as you want in a header file. I consider the Tile class not being big enough to have its own files.

Right now, the Tile only has a single boolean field but it's obvious that we'll add more properties later.

Note that the Tile declaration uses the struct keyword instead of class. They're basically the same except that the field visibility defaults to public in a struct whereas it's private in a class (that's why we add public: in the beginning of our classes). I generally use structs for data-only classes.

The Map class has a Tile pointer member. Since we don't want to hardcode the map size, we will dynamically allocate an array of Tiles. The tiles field will contain the address of the first element of the array.

Since that field will be dynamically allocated, we need to delete it to release the memory when the Map object is destroyed. That's why we declare a destructor.