Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Changing a dungeon generating algorithm to use graphical tiles instead of text.  (Read 2988 times)

0 Members and 1 Guest are viewing this topic.

SimonMate

  • Newbie
  • *
  • Posts: 4
    • View Profile
Hi all,

I was wondering if any of you have experience with adapting the algorithms here:
http://www.roguebasin.com/index.php?title=C%2B%2B_Example_of_Dungeon-Building_Algorithm
from outputting text to drawing tiles (specifically the 2nd and 3rd algorithms)

Thanks in advance for any help.

Simon

JayhawkZombie

  • Jr. Member
  • **
  • Posts: 76
    • View Profile
Have you considered mapping your tiles to a letter and having the algorithms generate the letters? Then render your tiles based on whatever characters it generated.

Turbine

  • Full Member
  • ***
  • Posts: 100
    • View Profile
The algorithms the hard bit, turning them to tiles is relatively trivial. Map a character to a tile, and then once they're placed pass through the tiles so the edges may be touched up with edging tiles.

SimonMate

  • Newbie
  • *
  • Posts: 4
    • View Profile
Hi,

I solved my problem, and I'm writing this post in case anyone else would like to use my solution.
Basically it was to create a tile class, cycle through the vector of characters stored in the Dungeon class to get the characters and their x and y positions, and to place a tile there based on its name via a string.

Tile.h
class Tile
{
public:
        Tile();
        Tile(const std::string tileType);
        Tile(const std::string tileType, int x, int y);
        void update();
        void draw(sf::RenderWindow &window);
private:
        sf::Sprite m_sprite;
        sf::Texture m_texture;
        sf::Vector2f m_pos;
        std::string m_id;
};
 

Tile.cpp
Tile::Tile()
{

}

Tile::Tile(const std::string tileType)
{

}

Tile::Tile(const std::string tileType, int x, int y)
{
        int posX = x * 16;
        int posY = y * 16;
        m_texture.loadFromFile(tileType + ".png");
        m_sprite.setTexture(m_texture);
        m_sprite.setPosition(sf::Vector2f(posX, posY));
        m_id = tileType;
}

void Tile::update()
{

}

void Tile::draw(sf::RenderWindow &window)
{
        window.draw(m_sprite);
}
 

Then back in main() I use this code to populate a tile vector with the correct tiles, and draw them.
        std::vector<Tile*> tileVector;



        //enum Tile
        //{
        //      Unused = ' ',
        //      Floor = '.',
        //      Corridor = ',',
        //      Wall = '#',
        //      ClosedDoor = '+',
        //      OpenDoor = '-',
        //      UpStairs = '<',
        //      DownStairs = '>'
        //};

        for (int x = 0; x <= 79; x++)
        {
                for (int y = 0; y <= 24; y++)
                {
                        if (d.getTile(x, y) == ' ')
                        {
                                tileVector.push_back(new Tile("floor", x, y));
                        }

                        if (d.getTile(x, y) == '#')
                        {
                                tileVector.push_back(new Tile("wall", x, y));
                        }

                        if (d.getTile(x, y) == '.')
                        {
                                tileVector.push_back(new Tile("empty", x, y));
                        }

                        if (d.getTile(x, y) == '+')
                        {
                                tileVector.push_back(new Tile("closedDoor", x, y));
                        }
                }
        }

        while (window->isOpen())
        {
                sf::Event event;

                while (window->pollEvent(event))
                {
                        if (event.type == sf::Event::Closed)
                                window->close();
                }

                window->clear();
                int size = tileVector.size();
                for (int i = 0; i < size; i++)
                {
                        tileVector.at(i)->draw(*window);
                }
                window->display();
        }
 

Thanks for the suggestions everyone.
edit: here's a picture for you to see what it turned out like if you're interested in these algorithms


« Last Edit: March 19, 2017, 07:58:01 pm by SimonMate »

Hapax

  • Hero Member
  • *****
  • Posts: 3379
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
The generated map look pretty good! :)

However, this is certainly not the most efficient way to draw a tile map.

Each tile is a separate sprite. Each sprite "costs" a draw call. You may be interested in reading about vertex arrays:
http://www.sfml-dev.org/tutorials/2.4/graphics-vertex-array.php
There is even an example of a (static) tile map in that very tutorial!:
http://www.sfml-dev.org/tutorials/2.4/graphics-vertex-array.php#example-tile-map

Worse, though, is that each tile stores the texture required for itself. Even if every single tile was different, this is still a waste. However, there are many tiles that use the same texture but the image is loaded for each tile. That is, the "wall" tile is stored as a texture (image in graphics memory) for every wall tile. A better way would be to store the texture(s) outside of the Tile class and pass it to the tile (not by value) to set the sprite.
An even better (probably) way than storing each tile in its image file is to store all of the tiles in a single image, use that texture for all tiles and then just change the texture rectangle for each tile. Note that this way is required if you use vertex arrays (as mentioned above) as a vertex array can only use one texture.

So, then, the aim here is to use a single vertex array that displays all of the tiles that are in view and store all tile images in one texture and then each quad of the vertex array can use a part of that texture by setting texture rectangle (using texture co-ordinates).

Hope that helps!
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*

SimonMate

  • Newbie
  • *
  • Posts: 4
    • View Profile
I made some adjustments:

Firstly, I got a tileset that I actually wanted to use, so every texture is loaded from 1 image now.

Second, each Tile now only stores a pointer to a texture (in this case the tileset texture) which I get by returning it from a GameData class (for json and such).

Finally the actual sprite that is used from the tileset is decided based on the string I pass in the Tile constructor, and then I use setTextureRect to actually get the tile I want.

Cheers,
Simon

Hapax

  • Hero Member
  • *****
  • Posts: 3379
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
It would be wrong of me not to mention that Selba Ward's Tile Map may be of use here.

However, for this particular case, you may be more interested in its Console Screen.
It allows "tiles" or "cells" to be updated from strings. You can "print" the entire string to the screen at once and the tiles will be updated per character. Have a look; it could help.
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*

SimonMate

  • Newbie
  • *
  • Posts: 4
    • View Profile
Hello again,

Is this what you are talking about with using vertex arrays instead of a sprite, Hapax?

        m_vertexArray = sf::VertexArray(sf::Quads, 4);
       
        //Position setting
        m_vertexArray[0].position = sf::Vector2f(posX, posY);
        m_vertexArray[1].position = sf::Vector2f(posX + 10, posY);
        m_vertexArray[2].position = sf::Vector2f(posX + 10, posY + 10);
        m_vertexArray[3].position = sf::Vector2f(posX, posY + 10);
       
       
        if (tileType == "floor")
        {
                m_vertexArray[0].texCoords = sf::Vector2f(56, 85);
                m_vertexArray[1].texCoords = sf::Vector2f(64, 85);
                m_vertexArray[2].texCoords = sf::Vector2f(64, 93);
                m_vertexArray[3].texCoords = sf::Vector2f(56, 93);
        }

        else if (tileType == "wall")
        {
                m_vertexArray[0].texCoords = sf::Vector2f(47, 76);
                m_vertexArray[1].texCoords = sf::Vector2f(55, 76);
                m_vertexArray[2].texCoords = sf::Vector2f(55, 84);
                m_vertexArray[3].texCoords = sf::Vector2f(47, 84);
        }

        else if (tileType == "closedDoor")
        {
                m_vertexArray[0].texCoords = sf::Vector2f(56, 85);
                m_vertexArray[1].texCoords = sf::Vector2f(64, 85);
                m_vertexArray[2].texCoords = sf::Vector2f(64, 93);
                m_vertexArray[3].texCoords = sf::Vector2f(56, 93);
        }

        else if (tileType == "stairsUp")
        {
                m_vertexArray[0].texCoords = sf::Vector2f(137, 76);
                m_vertexArray[1].texCoords = sf::Vector2f(145, 76);
                m_vertexArray[2].texCoords = sf::Vector2f(145, 84);
                m_vertexArray[3].texCoords = sf::Vector2f(137, 84);
        }

        else if (tileType == "stairsDown")
        {
                //m_sprite.setTextureRect(sf::IntRect(146, 76, 8, 8));
                m_vertexArray[0].texCoords = sf::Vector2f(146, 76);
                m_vertexArray[1].texCoords = sf::Vector2f(154, 76);
                m_vertexArray[2].texCoords = sf::Vector2f(154, 84);
                m_vertexArray[3].texCoords = sf::Vector2f(146, 84);
        }

Hapax

  • Hero Member
  • *****
  • Posts: 3379
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
To some extent, yes. However, with a vertex array you can have many quads all in the same array.

It might be easier to store integer values to represent the tile instead of strings; that way, you can calculate the texture co-ordinates directly from the tile value:
https://github.com/Hapaxia/SelbaWard/blob/master/src/SelbaWard/TileMap.cpp#L266-L295
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*