Roguelike - grid abstraction

Published October 05, 2014
Advertisement
Bugs and frustration.... Looks like I need to do a little more cleaning before releasing. Been so long since I worked on the creature system, I had forgotten how I designed the current template loading. There's a bit of work to be done to bring it inline with how I refactored the item system. Not to come empty handed I figured I'd write about a couple templated classes I designed and implemented over the summer delay.

Ive been writing 2D games since the get go. I can't count the amount of times I've coded a tile map. Every time it's almost the same exact map class over and over. I decided to write to kinds of tile map style classes that would be reusable in future projects and bring cohesion to how I store and position the various objects in the game.
Grid and UnorderedGrid

Each stores cells that are a bounding box in the world and the 2D coord within the grid that it resides along with a templated data parameter. The grid makes sure that all objects stay within a certain bound. Has access to position data and generic querying of objects. The basics of Everyone of my tilemaps and probably all yours aswell ;)
template class GridCell { public: GridCell(); GridCell(cii::GridCell& gridCell); GridCell(sf::Vector2i newGridCoord, sf::Vector2f newWorldCoord, sf::Vector2f newWorldSize); cii::GridCell operator = (const cii::GridCell& gridCell); void init(sf::Vector2i newGridCoord, sf::Vector2f newWorldCoord, sf::Vector2f newWorldSize); sf::Vector2i getGridCoord() const; sf::Vector2f getWorldCoord() const; sf::Vector2f getWorldSize() const; void setData(T& newData); T* getData(); private: sf::Vector2i gridCoord; sf::Vector2f worldCoord; sf::Vector2f worldSize; T data; };

The Grid class is the basic tilemap class. You initialize it to a specific size, giving it a cell size in world coords as well. It'll then generate a 2D grid of cells giving them all a grid coord and world coord based on the cell size and that the starting cell is always at 0x0y.

It doesn't initialize the data though. I though about having it except a factory method or something that would allow setting a default state to all the data. After looking into how it would be used, all the data gets set right after being initialized and almost all of it is unique. It seemed counter intuitive to do so.

This also allows grid to be used purely as a coordinate system. It doesn't have to store information other than position of each cell if all you need is to be able to get position data.
template class Grid { public: Grid(); void init(sf::Vector2i newGridSize, sf::Vector2f newCellSize); void clear(); cii::GridCell* getCell(sf::Vector2i gridCoord); // returns the cell at the given gridcoord. / returns NULL if the coord is invalid or cell doesnt exist. cii::GridCell* getCell(sf::RenderWindow& window); // returns the cell at the current mouse position using the given window and its current view. / returns NULL if the coord doesnt exist or is invalid. std::vector*> getCellsInArea(sf::Vector2i startCoord, sf::Vector2i areaSize); bool isGridCoordValid(sf::Vector2i gridCoord); sf::Vector2i getGridSize() const; sf::Vector2f getCellSize() const; private: sf::Vector2i gridSize; sf::Vector2f cellSize; std::vector> cells; };

The big difference between the two classes; Grid initializes every cell in the grid and they will always be available, whether data is present or not, UnorderedGrid doesnt actually initialize any cells and allows adding them one by one. Like grid though, UnorderedGrid follows the same rules that only one cell can exist per coordinate on the grid. Think of Grid being used for terrain, and UnorderedGrid is for things like items on the ground. Items are scattered about but my stay within the grid and follow the rule of only one per cell.
template class UnorderedGrid { public: UnorderedGrid(); void init(sf::Vector2i newGridSize, sf::Vector2f newCellSize); void clear(); // getCell() / getCellsInArea() returns null if the gridcoord is invalid or the cell doesnt exist cii::GridCell* getCell(sf::Vector2i gridCoord); std::vector*> getCellsInArea(sf::Vector2i startCoord, sf::Vector2i areaSize); bool isGridCoordValid(sf::Vector2i gridCoord); sf::Vector2i getGridSize() const; sf::Vector2f getCellSize() const; // push tries to adds a new cell with the given data. return false if the coord is occupied or invalid bool push(sf::Vector2i gridCoord, T& data); // removes the cell at the given coord from the grid. return false if the coord deosnt exist or is invalid. bool pop(sf::Vector2i gridCoord); private: sf::Vector2i gridSize; sf::Vector2f cellSize; std::vector> cells; };

Since I've implemented this into the game, it's helped with quite a bit of clutter removing alot of copy pasted code that existed in every system. And for something that only took a day to put together and test it's been satisfying and cant wait to put it to use in other projects. It should help speed up development which is always I nice feature.

Next time I'll go into the TemplateFactory I wrote for the item system and am having a headache refactoring the creature system to use. Till then keep coding and stay classy.
1 likes 1 comments

Comments

arka80

I cant' count the times I coded a tile map class too.

And I can't count the times I decided to code a reusable one ;) Fact is that, at every new project, old classes seems... well, old

October 06, 2014 07:33 AM
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Profile
Author
Advertisement
Advertisement