2d Tiles and Maps Tutorial
2d Tiles and Maps Tutorial
I've been browsing these forums for a few weeks now, and feel that it's time I gave something back to the community. I hope some of you might find this useful. Also I wasn't sure whether to post this in snippets or tutorials section, so if someone thinks this should be moved by all means do so .
You are welcome to use any of my code in any of your applications commercial or non commercial. Just be nice and leave my name in the file, thats all. Furthermore I am not liable for anything that should happen to you or your computer, directly or indirectly caused by the execution of my code. Including but not limited to spilling soda all over your keyboard.
Ok... on to the goods stuff. For a little while now I have been developing a 2d tile and map rendering engine. I thought I'd share my code with those looking to implement these techniques into their game using irrlicht. I am also open to comments and suggestions about my code (just polite suggestions though please). If you know a way to improve my code, I'd love to hear it.
Theres A lot of code here so I'm going to reserve the first few posts to split my code into sections. Hope you enjoy
Oh and a WARNING, for purposes of clarity of the tutorial my code is devoid of a lot of necessary error checking. You are responsible for adding this in yourself! (It should be pretty obvious where).
You are welcome to use any of my code in any of your applications commercial or non commercial. Just be nice and leave my name in the file, thats all. Furthermore I am not liable for anything that should happen to you or your computer, directly or indirectly caused by the execution of my code. Including but not limited to spilling soda all over your keyboard.
Ok... on to the goods stuff. For a little while now I have been developing a 2d tile and map rendering engine. I thought I'd share my code with those looking to implement these techniques into their game using irrlicht. I am also open to comments and suggestions about my code (just polite suggestions though please). If you know a way to improve my code, I'd love to hear it.
Theres A lot of code here so I'm going to reserve the first few posts to split my code into sections. Hope you enjoy
Oh and a WARNING, for purposes of clarity of the tutorial my code is devoid of a lot of necessary error checking. You are responsible for adding this in yourself! (It should be pretty obvious where).
Last edited by dmbush on Tue Aug 19, 2008 1:13 am, edited 3 times in total.
Part 1:
What is a tile?
Those of you who already know can feel free to skip this paragraph. Many games will create worlds by piecing together smaller images referred to as "tiles." Generally these tiles are squares (though they can also be diamonds, hexagons etc...). If you have a skilled artist, when you put the tiles together, you won't even be able to see them. This technique is most commonly used in 2d RTS and RPG style games.
I know what a tile is, how do I use them in my games?
Well, thankfully irrlicht easily handles the loading and drawing of 2d images. So it is up to us simply to create a data structure to store tiles, the information about them and how to piece them together (maps).
Ok enough blah blah blah, get to the code please!
Fine! The first task is to create a data structure to load and store the tile images to be drawn. I called mine TileManager.
here is TileManager.h
and of course its friend TileManager.cpp
Ok how to use this? Lets say we have a 320 x 320 texture of 32x32 tiles called "tiles.png" and we want the color pink to be transparently drawn.
Stay tuned, in the next section we will start talkin about maps!
What is a tile?
Those of you who already know can feel free to skip this paragraph. Many games will create worlds by piecing together smaller images referred to as "tiles." Generally these tiles are squares (though they can also be diamonds, hexagons etc...). If you have a skilled artist, when you put the tiles together, you won't even be able to see them. This technique is most commonly used in 2d RTS and RPG style games.
I know what a tile is, how do I use them in my games?
Well, thankfully irrlicht easily handles the loading and drawing of 2d images. So it is up to us simply to create a data structure to store tiles, the information about them and how to piece them together (maps).
Ok enough blah blah blah, get to the code please!
Fine! The first task is to create a data structure to load and store the tile images to be drawn. I called mine TileManager.
here is TileManager.h
Code: Select all
/*
7/31/08
TileManager.h
Dan Bush
*/
#ifndef TILE_MANAGER_H
#define TILE_MANAGER_H
#include <irrlicht.h>
#include <string>
#include <vector>
using namespace irr;
using namespace video;
using namespace std;
// structure that stores the images and tile information
//for each texture we load
struct sTileSet
{
ITexture * texture;
unsigned short width;
unsigned short height;
unsigned short collumns;
};
class TileManager
{
private:
vector<sTileSet> tSet;
public:
TileManager();
~TileManager();
bool load(IVideoDriver * p, char * s, const unsigned short w, const unsigned short h, const unsigned short c, SColor key);
bool free();
bool draw(IVideoDriver * pDriver, const unsigned short texNum, const unsigned short tileNum, const short x, const short y, irr::core::rect<s32> * r = 0);
short getNumTileSets();
sTileSet getTileSetAt(const unsigned short i);
};
#endif
Code: Select all
/*
7/31/08
TileManager.cpp
Dan Bush
*/
#include "TileManager.h"
#include <string>
#include <iostream>
#include <vector>
using namespace irr;
using namespace video;
using namespace std;
using namespace core;
//TileManager()
TileManager::TileManager()
{
free();
}
//~TileManager()
TileManager::~TileManager()
{
free();
}
//load()
//loads a tileset given a texture, the width and hieght of the tiles, the number of collumns
//and the transparent color key
bool TileManager::load(IVideoDriver * p, char * s, const unsigned short w, const unsigned short h, const unsigned short c, SColor key)
{
sTileSet set;
set.texture = p->getTexture(s);
set.width = w;
set.height = h;
set.collumns = c;
tSet.push_back(set);
p->makeColorKeyTexture(tSet[tSet.size() - 1].texture, key);
return true;
}
//free()
bool TileManager::free()
{
//dealloc the vector
tSet.clear();
return true;
}
//draw()
//draws a tile at x, y
//you probably wont call this directly very often (unless maybe you're writing an editor)
bool TileManager::draw(IVideoDriver * pDriver, const unsigned short texNum, const unsigned short tileNum, const short x, const short y, irr::core::rect<s32> * r)
{
// get the source coordinates
int sx = (tileNum % tSet.at(texNum).collumns) * tSet.at(texNum).width;
int sy = (tileNum / tSet.at(texNum).collumns) * tSet.at(texNum).height;
pDriver->draw2DImage(tSet.at(texNum).texture,position2d<s32>(x,y), rect<s32>(sx, sy, sx + tSet.at(texNum).width, sy + tSet.at(texNum).height),
r, SColor(255,255,255,255), true);
return true;
}
//getNumTileSets()
short TileManager::getNumTileSets()
{
return tSet.size();
}
//getTileSetAt()
sTileSet TileManager::getTileSetAt(const unsigned short i)
{
if(i >= tSet.size())
return tSet[tSet.size()-1];
else
return tSet[i];
}
Ok how to use this? Lets say we have a 320 x 320 texture of 32x32 tiles called "tiles.png" and we want the color pink to be transparently drawn.
Code: Select all
TileManager t;
//load texture with 32x32 tiles and 10 collumns
//pDriver is a pointer to IVideoDriver
t.load(pDriver, "tiles.png", 32, 32, 10, SColor(SColor(255,255,0,255));
//this part would go between your beginscene and endscene
t.draw(pDriver, 0, 3, 0, 0); //draws tile #3 coordinates (64,0) from texture at (0,0) on the screen.
t.draw(pDriver, 0, 11, 64, 64); //draws tile #11 coordinates (0,32) from texture at (64,64) on the screen.
Last edited by dmbush on Tue Aug 19, 2008 12:06 am, edited 2 times in total.
Ok I know how to draw cache and draw tiles now! Lets draw maps
Well actually, I lied we're not going to learn about maps in this section. We're going to learn about the individual sections of maps that I like to call nodes. A map node is essentially a tile, (or group of tiles drawn on top of eachother) at a certain location on the map. So to represent your map you would have two dimensional array, or vector, or whatever, of nodes that represent the word.
Each node contains the tile information as well as other custon information you can store for your own purposes. I have provided a character block that you could use to store bitflag information about each map node. Things like... you can't walk here, there is a trigger here, etc.
Just shut up and show us the code please
!!!!! *sigh* heres cTileMapNode.h
So you call one file cTileMapNode but you don't call the other cTileManager, thats not very consistent
dont ask, heres cTileMapNode.cpp
example please!
I'm glad we're being polite again.
Thats it! Of coure nodes by themselves arn't very useful unless they are put together into maps. Which is why in the next section we are going to learn about maps. IM NOT LYING THIS TIME I PROMISE.
Well actually, I lied we're not going to learn about maps in this section. We're going to learn about the individual sections of maps that I like to call nodes. A map node is essentially a tile, (or group of tiles drawn on top of eachother) at a certain location on the map. So to represent your map you would have two dimensional array, or vector, or whatever, of nodes that represent the word.
Each node contains the tile information as well as other custon information you can store for your own purposes. I have provided a character block that you could use to store bitflag information about each map node. Things like... you can't walk here, there is a trigger here, etc.
Just shut up and show us the code please
!!!!! *sigh* heres cTileMapNode.h
Code: Select all
/*
8/11/08
cTileMapNode.h
Dan Bush
*/
#ifndef TILE_MAP_NODE_H
#define TILE_MAP_NODE_H
#include <vector>
#include "TileManager.h"
#include <irrlicht.h>
using namespace irr;
using namespace std;
struct sTile
{
unsigned char tileset;
unsigned char id;
sTile(unsigned char t, unsigned char i){tileset = t; id = i;}
};
class cTileMapNode
{
private:
vector<sTile> tiles;
unsigned char bitflags; //use for whatever you want :) e.g. maybe setting bit 1 means you can't walk there
public:
cTileMapNode();
~cTileMapNode();
bool addTile(sTile t);
bool removeTile();
bool free();
bool draw(IVideoDriver * driver, TileManager * pImages, int x, int y, irr::core::rect<s32> * r = 0);
unsigned char getFlags();
void setFlags(const unsigned char c);
short getNumTiles();
sTile getTileAt(const unsigned short num);
};
#endif
dont ask, heres cTileMapNode.cpp
Code: Select all
/*
8/11/08
cTileMapNode.cpp
Dan Bush
*/
#include "cTileMapNode.h"
#include <irrlicht.h>
using namespace irr;
using namespace video;
//cTileMapNode
cTileMapNode::cTileMapNode()
{
free();
bitflags = 0;
}
//~cTileMapNode()
cTileMapNode::~cTileMapNode()
{
free();
}
//free()
bool cTileMapNode::free()
{
tiles.clear();
return true;
}
//addTile()
bool cTileMapNode::addTile(sTile t)
{
tiles.push_back(t);
return true;
}
//removeTile()
bool cTileMapNode::removeTile()
{
if(!tiles.empty())
tiles.erase(tiles.end() - 1);
return true;
}
//draw()
bool cTileMapNode::draw(IVideoDriver * driver, TileManager * pImages, int x, int y, irr::core::rect<s32> * r)
{
std::vector<sTile>::iterator itr;
for ( itr = tiles.begin(); itr != tiles.end(); ++itr )
{
pImages->draw(driver, (*itr).tileset, (*itr).id, x, y, r);
}
return true;
}
unsigned char cTileMapNode::getFlags()
{
return bitflags;
}
//getNumTiles()
short cTileMapNode::getNumTiles()
{
return tiles.size();
}
sTile cTileMapNode::getTileAt(const unsigned short num)
{
if (num >= tiles.size())
return tiles[tiles.size()-1];
return tiles[num];
}
I'm glad we're being polite again.
Code: Select all
// tManager is a loaded TileManager
// pDriver is a pointer to the Video driver
cTileMapNode n;
sTile t(0,0);
sTile t2(0,1);
n.addTile(t);
n.addTile(t2);
//and later in your drawing routine
n.draw(pDriver, &tManager, 0,0);
Last edited by dmbush on Tue Aug 19, 2008 12:31 am, edited 1 time in total.
Ok, so remember how we said in the last part how maps are basically just two dimensional arrays of map nodes? Well I wasn't lying about that either. I actually use vectors instead of arrays because I love vectors. Not going to fool around this time. Heres the code.
cTileMap.h
and cTileMap.cpp
and a quick example of how to use this:
ideally you don't want to create a map from scratch in your code. You would write an editor program to make the maps and save them. If the map is already saved you only have to do this.
pretty slick eh?
Hope you enjoyed, comments?
cTileMap.h
Code: Select all
/*
8/11/08
cTileMap.h
Dan Bush
unsigned short nWidth;
unsigned short nHeight;
};
class cTileMap
{
private:
ITexture * map;
TileManager manager;
vector<vector<cTileMapNode>> nodes;
unsigned short maxNodeX;
unsigned short maxNodeY;
unsigned short nodeWidth;
unsigned short nodeHeight;
unsigned short cameraX;
unsigned short cameraY;
unsigned short viewPortWidth;
unsigned short viewPortHeight;
public:
cTileMap();
~cTileMap();
bool freeNodes();
bool draw(IVideoDriver * driver, const short drawX, const short drawY);
bool load(IVideoDriver * driver, const std::string s);
bool save(const std::string s);
bool setNodeDimensions(const unsigned short w,const unsigned short h);
bool createNodes(const unsigned short x, const unsigned y);
bool setViewPortDimensions(const unsigned short w, const unsigned short h);
bool setCamera(const short x, const short y);
unsigned short getCameraX();
unsigned short getCameraY();
TileManager * getTileManager();
vector<vector<cTileMapNode>> * getNodes();
bool addTile(sTile t, unsigned int nodeX, unsigned int nodeY);
};
#endif
and cTileMap.cpp
Code: Select all
/*
8/11/08
cTileMap.h
Dan Bush
*/
#include "cTileMap.h"
#include <fstream>
using namespace irr;
using namespace video;
using namespace std;
using namespace core;
//cTileMap()
//initializes everything to arbitrary values
//you probably want to set your own values using setNodeDimensions()... etc..
cTileMap::cTileMap()
{
map = NULL;
freeNodes();
setNodeDimensions(32,32);
setViewPortDimensions(320,320);
maxNodeX = 0;
maxNodeY = 0;
cameraX = 0;
cameraY = 0;
}
//~cTileMap()
//calls freeNodes()
cTileMap::~cTileMap()
{
freeNodes();
}
//freeNodes
//not much to say about this
bool cTileMap::freeNodes()
{
nodes.clear();
return true;
}
//draw()
//draws the map to the screen at given positions using the camera viewport
bool cTileMap::draw(IVideoDriver * driver, const short drawX, const short drawY)
{
//translate from fine coordinates to tile coordinates
unsigned int startY = cameraY / nodeHeight;
unsigned int endY = (cameraY + viewPortWidth) / nodeHeight;
unsigned int startX = cameraX / nodeWidth;
unsigned int endX = (cameraX + viewPortHeight) / nodeWidth;
//shift the tiles over because we want to have per pixel scrolling
unsigned int shiftY = cameraY % nodeHeight;
unsigned int shiftX = cameraX % nodeWidth;
//finally draw the tiles using the above calculations
for(unsigned int y = 0; y <= (endY - startY); y++)
{
for(unsigned int x = 0; x <= (endX - startX); x++)
{
if((startX + x) > nodes.size()- 1) //this shouldnt happen ever due to our camera, but just in case
break;
if((startY + y) > nodes[startX + x].size() - 1)//this shouldnt happen ever due to our camera, but just in case
break;
nodes[startX + x][startY + y].draw(driver, &manager, drawX + ((x * nodeWidth ) - shiftX), drawY + ((y * nodeHeight) - shiftY), &rect<s32>(drawX,drawY, drawX + viewPortWidth, drawY + viewPortHeight));
}
}
return true;
}
//load()
//not yet implemented
bool cTileMap::load(IVideoDriver * driver, const std::string s)
{
freeNodes();
ifstream f(s.c_str(), ios::binary);
//read the header information
sMapHeader h;
f.read((char *) &h, sizeof(h));
createNodes(h.width, h.height);
setNodeDimensions(h.nWidth, h.nHeight);
// READ THE TILEMANAGER INFORMATION
short sets;
f.read((char *) &sets, sizeof(short));
for(int q = 0; q < sets; q++)
{
char name[30];
f.read(name, sizeof(char) * 30);
unsigned short w;
f.read((char *)&w, sizeof(unsigned short));
unsigned short h;
f.read((char *)&h, sizeof(unsigned short));
unsigned short col;
f.read((char *)&col, sizeof(unsigned short));
manager.load(driver, name, w, h, col, SColor((255,255,0,255)));
}
for(int y = 0; y < h.height; y++)
{
for(int x = 0; x < h.width; x++)
{
unsigned char c;
unsigned short num;
sTile t(0,0);
f.read((char *) &c, sizeof(unsigned char));
f.read((char *) &num, sizeof(unsigned short));
for(int i = 0; i < num; i++)
{
f.read((char *) &t, sizeof(sTile));
addTile(t, x, y);
}
}
}
f.close();
return true;
}
//save()
//not yet implemented
bool cTileMap::save(const std::string s)
{
ofstream f(s.c_str(), ios::binary | ios::trunc);
// SAVE THE MAP HEADER
sMapHeader h;
h.width = maxNodeX;
h.height = maxNodeY;
h.nWidth = nodeWidth;
h.nHeight = nodeHeight;
f.write((char *) &h, sizeof(h));
// SAVE THE TILEMANAGER INFORMATION
short sets = manager.getNumTileSets();
f.write((char *) &sets, sizeof(short));
for(int q = 0; q < sets; q++)
{
sTileSet t = manager.getTileSetAt(q);
int s = sizeof(t.texture->getName().c_str());
char name[30];
strcpy_s(name, t.texture->getName().c_str());
f.write(name, sizeof(char)*30);
f.write((char *)&t.width, sizeof(unsigned short));
f.write((char *)&t.height, sizeof(unsigned short));
f.write((char *)&t.collumns, sizeof(unsigned short));
}
// SAVE THE NODES
for(int y = 0; y < h.height; y++)
{
for(int x = 0; x < h.width; x++)
{
unsigned char c = nodes[x][y].getFlags();
unsigned short num = nodes[x][y].getNumTiles();
f.write((char *) &c, sizeof(unsigned char));
f.write((char *) &num, sizeof(unsigned short));
for(int i = 0; i < num; i++)
f.write((char *) &nodes[x][y].getTileAt(i), sizeof(sTile));
}
}
f.close();
return true;
}
//setNodeDimensions()
//self explanatory
bool cTileMap::setNodeDimensions(const unsigned short w, const unsigned short h)
{
if(w ==0 || h == 0)
return false;
nodeWidth = w;
nodeHeight = h;
return true;
}
//createNodes()
//self explanatory
bool cTileMap::createNodes(const unsigned short x, const unsigned y)
{
freeNodes();
vector<cTileMapNode> v(y);
for(int i = 0; i < x; i++)
nodes.push_back(v);
maxNodeX = x;
maxNodeY = y;
return true;
}
//setViewPortDimensions()
//self explanatory
bool cTileMap::setViewPortDimensions(const unsigned short w, const unsigned short h)
{
if(w <= 0 || h <= 0)
return false;
viewPortWidth = w;
viewPortHeight = h;
return true;
}
//getNodes()
//self explanatory
vector<vector<cTileMapNode>> * cTileMap::getNodes()
{
return &nodes;
}
//getNodes()
//self explanatory
TileManager * cTileMap::getTileManager()
{
return &manager;
}
//addTile()
//adds a tile to node[x][y]
bool cTileMap::addTile(sTile t, unsigned int nodeX, unsigned int nodeY)
{
if(nodeX > nodes.size())
return false;
if(nodeY > nodes[nodeX].size())
return false;
nodes[nodeX][nodeY].addTile(t);
return true;
}
//setCamera()
//positions the camera upper left coordinate at x,y, this location can be
//outside the map, the camera will automatically shift over to the map boundary
bool cTileMap::setCamera(const short x, const short y)
{
if (x < 0)
cameraX = 0;
else if(x > ((maxNodeX * nodeWidth) - viewPortWidth))
cameraX = ((maxNodeX * nodeWidth) - viewPortWidth);
else
cameraX = x;
if (y < 0)
cameraY = 0;
else if(y > ((maxNodeY * nodeHeight) - viewPortHeight))
cameraY = ((maxNodeY * nodeHeight) - viewPortHeight);
else
cameraY = y;
return true;
}
//getCameraX()
//self explanatory
unsigned short cTileMap::getCameraX()
{
return cameraX;
}
//getCameraY()
//self explanatory
unsigned short cTileMap::getCameraY()
{
return cameraY;
}
and a quick example of how to use this:
Code: Select all
//this will create a map from scratch
cTileMap map;
map.createNodes(16,16);
map.setNodeDimensions(64,64);
map.setViewPortDimensions(200,200);
map.getTileManager()->load(pDriver, "tiles.png", 64, 64, 16, SColor(255,255,0,255));
int i = 0;
for(int y = 0; y < 16; y++)
{
for(int x = 0; x < 16; x++)
{
map.addTile(sTile(0,i), x, y);
i++;
}
}
//save this for later
map.save("map.txt");
//later in your drawing routine
map.draw(pDriver, 0, 0);
Code: Select all
cTileMap map;
map.load(pDriver, "map.txt");
map.setViewPortDimensions(200,200);
//later in your drawing routine
map.draw(pDriver, 0, 0);
Hope you enjoyed, comments?
-
- Posts: 86
- Joined: Wed Aug 29, 2007 10:45 pm
-
- Posts: 6
- Joined: Tue Apr 29, 2008 6:45 pm
I recently implemented this tile map class in a game I'm working on, but I had a snag when trying to access nodes from the map.
When calling the "get nodes" class in cTileMap
how do you receive this pointer in another function? I tried multiple things, such as
The above code returns a pointer to a vector array with 3 dimensions (eg, I have to call nodes[x][y][z]->blah();)
and
plus all sorts of others combinations, but I get a compile error with everything I try (other than the first option). I'll post the exact code and error when I get home this afternoon. Thanks a ton for any help.
When calling the "get nodes" class in cTileMap
Code: Select all
vector<vector<cTileMapNode>> * getNodes();
Code: Select all
vector<vector <cTileMapNode> >* nodes = map.getNodes();
and
Code: Select all
and vector<cTileMapNode>* nodes = map.getNodes();;
triedvector<vector<cTileMapNode>> * getNodes();
Code: Select all
vector<vector<cTileMapNode> > * getNodes();
Also, do yourself a favor and pass references around
-
- Posts: 6
- Joined: Tue Apr 29, 2008 6:45 pm
Ok, so here's what I've got right now:
This code doesn't compile, visual C++ gives this error:
Changing said line to:
and it compiles just fine, but it's not a 3D array, it's only passing a 2D array. Am I using the vector container wrong? If I try to operate on mapnodes[0][0], the compiler gives a selector box that has functions relevant to a vector, not a TileMapNode.
Halp!
Code: Select all
TileManager tileManager;
TileMapNode node;
TileMap tMap;
.....
void beginGame(IrrlichtDevice* device){
IVideoDriver* driver = device->getVideoDriver();
device->setWindowCaption(L"2D Graphics");
tileManager.load(driver, "Road.bmp", 32, 32, 10, SColor(255,255,0,255));
loadMap(driver);
while(device->run() && driver)
{
driver->beginScene(true, true, SColor(0,120,102,136));
vector<vector<TileMapNode> > *mapnodes;
mapnodes = tMap.getNodes();
TileMapNode currentNode = mapnodes[0][0];
}
}
void loadMap(IVideoDriver *driver){
tMap.createNodes(300,300);
tMap.setNodeDimensions(32,32);
tMap.setViewPortDimensions(600,600);
tMap.getTileManager()->load(driver, "road.bmp", 32, 32, 16, SColor(255,255,0,255));
int i = 0;
for(int y = 0; y < 300; y++)
{
for(int x = 0; x < 300; x++)
{
tMap.addTile(Tile(0,i), x, y);
i++;
if (i > 18){
i=0;
}
}
}
}
which is pointing to this line:1>armoredfightingvehicles.cpp(137) : error C2440: 'initializing' : cannot convert from 'std::vector<_Ty>' to 'TileMapNode'
1> with
1> [
1> _Ty=TileMapNode
1> ]
1> No constructor could take the source type, or constructor overload resolution was ambiguous
Code: Select all
TileMapNode currentNode = mapnodes[0][0];
Code: Select all
TileMapNode currentNode = mapnodes[0][0][0];
Halp!
nice peace of code. i guess the top of cTileMap.h should look like this and u messed up copy&paste :
/*
8/11/08
cTileMap.h
Dan Bush
*/
#ifndef TILE_MAP_H
#define TILE_MAP_H
using namespace std;
struct sMapHeader {
unsigned short width;
unsigned short height;
unsigned short nWidth;
unsigned short nHeight;
};
class cTileMap
{
private:
ITexture * map;
TileManager manager;
...
/*
8/11/08
cTileMap.h
Dan Bush
*/
#ifndef TILE_MAP_H
#define TILE_MAP_H
using namespace std;
struct sMapHeader {
unsigned short width;
unsigned short height;
unsigned short nWidth;
unsigned short nHeight;
};
class cTileMap
{
private:
ITexture * map;
TileManager manager;
...
And Then Nothing Turned Itself Inside-Out
This is an old post, but I'll post anyway.
I'm about to write my own tile manager type class for scrolling 2D backgrounds.
Just looking quickly over the code shown here...
It seems a bit messed up or backwards.
As an example, why would the tile node need to know about the tile manager class but not the other way around.
I haven't looked at it carefully yet, but it seems backwards.
That alone would make me restructure my class.
Actually, when I look at the last posts that dmbush made, the classes are changed, and the manager class does use the node class.
If someone wants to post this much code, they should upload a working version in a zip file. I don't want to (and I won't) waste my time making someones "idea" work.
I'm about to write my own tile manager type class for scrolling 2D backgrounds.
Just looking quickly over the code shown here...
It seems a bit messed up or backwards.
As an example, why would the tile node need to know about the tile manager class but not the other way around.
I haven't looked at it carefully yet, but it seems backwards.
That alone would make me restructure my class.
Actually, when I look at the last posts that dmbush made, the classes are changed, and the manager class does use the node class.
If someone wants to post this much code, they should upload a working version in a zip file. I don't want to (and I won't) waste my time making someones "idea" work.
I can hear birds chirping
I live in the Eye of Insanity.
I live in the Eye of Insanity.
-
- Posts: 27
- Joined: Mon Aug 14, 2006 12:36 pm
Yes, that sort of awkward class relationships can become real pain in the ass when the game becomes more complex.Ulf wrote:It seems a bit messed up or backwards.
As an example, why would the tile node need to know about the tile manager class but not the other way around.
I would recommend drawing a few class diagrams before writing any line of code.
Class diagrams with relations between them like this:
http://www.cc.gatech.edu/classes/AY2004 ... agram3.jpg
In my game I also have "manager" classes, and most of the time these classes use "data" classes (IMHO CTilenode is a data class), that do not know about their manager, and mostly just contain data and have a few set/get functions. You can not ask a Tilenode to draw itself.
A manager needs to get the relevant data for drawing out of the tilenode, and call another function to draw this node using this data.
I also like to keep the irrlicht graphics stuff like ITexture* pointers and IVideoDriver* functions in AS FEW classes as possible, not having them running through all your code.
So I would create a new class eg. CTileRenderer.
A quick idea:
Code: Select all
CMap
/ \___________________
/ \
... CTileManager
____ /\________
/ \
tileNode(0..x)* CTilenode tileRenderer* CTileRenderer
|
tile(0..x)* CTile
CTileRenderer
- holds the pointers to the textures (tilesheets)
- holds the tilesheet metadata (for each tile_ID which tilesheet it is on, and on what position)
- has a draw procedure with parameters a tile_ID and a destination point (where to draw it on the screen), this calls the draw function of irrlicht
CTileManager
- holds the pointers to the TileRenderer and TileNodes
- has a draw procedure (called by CMap::draw) with as input parameter the maptoview offset. The procedure loops trough the tilenodes, gets the IDs of the tiles and their position on the map, calculates where it should be on the screen and calls the TileRenderer to draw it.
CTilenode
- holds the pointers to it's tiles
- holds it's position on the map
- holds other game-related information about this node (for example whether it is visible to the player or not)
CTile
- holds it's tile_ID