Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?

[SFML] Mapa kafelkowa - czyli jak to zrobić dobrze

Ostatnio zmodyfikowano 2024-07-16 18:18
Autor Wiadomość
tBane
Temat założony przez niniejszego użytkownika
[SFML] Mapa kafelkowa - czyli jak to zrobić dobrze
» 2024-06-24 12:52:28
Witam.
Piszę system mapy kafelkowej i chciałbym to dobrze zrobić. Jakieś rady?
Kod zamieszczam poniżej.


C/C++
#ifndef Maps_hpp
#define Maps_hpp

float tileSide = 32.0f;
int tilesInSideMap = 16;
float mapSide = tilesInSideMap * tileSide;

enum class terrainType { grass, sands };

class Tile {
public:
   
sf::Vector2i coords;
   
sf::Vector2f position;
   
Texture * texture;
   
sf::Sprite sprite;
   
terrainType ttype;
   
   
Tile( int x, int y ) {
       
coords.x = x;
       
coords.y = y;
       
ttype = terrainType::grass;
       
texture = getTexture( "assets/tiles/grass" );
   
}
   
   
~Tile() { }
   
   
void setTerrainType( terrainType ttype ) {
       
       
this->ttype = ttype;
       
       
if( ttype == terrainType::grass )
           
 texture = getTexture( "assets/tiles/grass" ); // na razie niech tak będzie
       
       
if( ttype == terrainType::sands )
           
 texture = getTexture( "assets/tiles/sands" );
       
       
sprite.setTexture( * texture->texture );
   
}
}
;

class Map {
public:
   
sf::Vector2i coords;
   
std::vector < Tile * > tiles;
   
   
Map( int x, int y ) {
       
       
coords.x = x;
       
coords.y = y;
       
       
tiles.clear();;
       
for( int y = 0; y < tilesInSideMap; y++ )
       
for( int x = 0; x < tilesInSideMap; x++ ) {
           
           
Tile * tile = new Tile( x, y );
           
           
tile->position.x = float( coords.x ) * mapSide + float( x ) * tileSide;
           
tile->position.y = float( coords.y ) * mapSide + float( y ) * tileSide;
           
           
tile->sprite = sf::Sprite();
           
tile->sprite.setTexture( * tile->texture->texture );
           
tile->sprite.setPosition( tile->position );
           
           
tiles.push_back( tile );
       
}
    }
   
   
~Map() { }
   
   
void render() {
       
for( auto & t: tiles )
           
 window->draw( t->sprite );
       
   
}
}
;

class World {
public:
   
std::vector < Map * > maps;
   
   
World() {
       
maps.clear();
       
       
maps.push_back( new Map( 0, 0 ) );
       
maps.push_back( new Map( 1, 0 ) );
   
}
   
   
Tile * getTile( sf::Vector2f worldPosition ) {
       
       
sf::Vector2i mapCoords;
       
mapCoords.x = worldPosition.x / mapSide;
       
mapCoords.y = worldPosition.y / mapSide;
       
       
Map * map = nullptr;
       
       
for( auto & m: maps ) {
           
if( m->coords == mapCoords )
               
 map = m;
           
       
}
       
       
if( map == nullptr )
           
 return nullptr;
       
       
sf::Vector2i tileCoords;
       
tileCoords.x = int( worldPosition.x ) % int( mapSide ) / tileSide;
       
tileCoords.y = int( worldPosition.y ) % int( mapSide ) / tileSide;
       
       
return map->tiles[ tileCoords.y * tilesInSideMap + tileCoords.x ];
       
   
}
   
   
void render() {
       
for( auto & m: maps ) {
           
m->render();
       
}
    }
}
;

World * world;

#endif
P-181273
tBane
Temat założony przez niniejszego użytkownika
» 2024-06-26 13:42:13
Kafelki mapy mają wymiary 16x16 px, a tekstury 64x64 px.
Aby renderować mapę kafelków 16x16 px, używając tekstur o wymiarach 64x64 px, tak aby fragmenty tekstury były wyświetlane w siatce 16x16 px należy:

C/C++
tx = int( tile->position.x ) % tile->texture->texture->getSize().x;
ty = int( tile->position.y ) % tile->texture->texture->getSize().y;
tile->sprite.setTextureRect( sf::IntRect( tx, ty, tileSide, tileSide ) );

P-181275
DejaVu
» 2024-06-26 13:43:48
Offtopic: Twój projekt zaczyna nieźle wyglądać.
P-181276
pekfos
» 2024-06-26 20:18:08
Ile trwa renderowanie? Robienie tego sprajtami jest zwykle wolne.
P-181279
tBane
Temat założony przez niniejszego użytkownika
» 2024-06-26 21:06:01
Nawet nie mierzyłem, ponieważ renderowało płynnie. Teraz dokonałem pomiaru i wyszło całkiem nieźle. Wyniki dla 24 map (8x4) 16x16 pól:
4 ms,   0.004343 s
4 ms,   0.004179 s
4 ms,   0.004295 s
4 ms,   0.004318 s
4 ms,   0.004167 s
4 ms,   0.004141 s
4 ms,   0.004191 s
4 ms,   0.004175 s
4 ms,   0.004155 s
4 ms,   0.004133 s
4 ms,   0.004223 s
4 ms,   0.004329 s
(W sumie to mam całkiem dobrą kartę graficzną i może dlatego wyniki są dobre...?)

A jest lepsza metoda renderingu map?

P-181280
tBane
Temat założony przez niniejszego użytkownika
» 2024-06-27 12:51:23
Proporcje budynków

szerokość (x) x 16px
wysokość h x 16px
długość (x/2 + h) x 16px


pozostaje jedynie zdecydować czy wszystkie budynki mają mieć tekstury o stałym rozmiarze 256x256 px czy też różne rozmiary wielokrotności 16px.

A tak się to prezentuje w edytorze

P-181281
pekfos
» 2024-06-27 17:41:18
A jest lepsza metoda renderingu map?
Gdy wyświetlasz wiele obiektów (wiele w znaczeniu >100 bardziej niż >1) z tej samej tekstury, lepiej użyć sf::VertexArray. Strona SFML podaje jako przykład użycia właśnie mapę kafelkową: https://www.sfml-dev.org/tutorials/2.6/graphics-vertex-array.php.
P-181282
tBane
Temat założony przez niniejszego użytkownika
» 2024-06-27 19:50:14
Dobra opanowane częściowo:

C/C++
#ifndef Maps_hpp
#define Maps_hpp

float tileSide = 16.0f;
int tilesInSideMap = 16;
float mapSide = tilesInSideMap * tileSide;

class Tile {
public:
   
sf::Vector2i coords;
   
sf::Vector2f position;
   
Texture * texture;
   
sf::Sprite sprite;
   
terrainType ttype;
   
   
Tile( int x, int y ) {
       
coords.x = x;
       
coords.y = y;
       
ttype = terrainType::grass;
       
texture = getTexture( "assets/tiles/grass" );
   
}
   
   
~Tile() { }
   
   
void setTerrainType( terrainType ttype ) {
       
       
this->ttype = ttype;
       
       
if( ttype == terrainType::grass )
           
 texture = getTexture( "assets/tiles/grass" );
       
       
if( ttype == terrainType::sands )
           
 texture = getTexture( "assets/tiles/sands" );
       
       
if( ttype == terrainType::water )
           
 texture = getTexture( "assets/tiles/water" );
       
       
sprite.setTexture( * texture->texture );
   
}
}
;

class Map
    : public sf::Drawable
    , public sf::Transformable
{
public:
   
sf::Vector2i coords;
   
std::vector < Tile * > tiles;
   
sf::VertexArray vertexes;
   
   
   
Map( int x, int y ) {
       
       
coords.x = x;
       
coords.y = y;
       
       
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // NEW CODE
       
       
vertexes.setPrimitiveType( sf::Triangles );
       
vertexes.resize( tilesInSideMap * tilesInSideMap * 6 ); // widthMap * heightMap * TwoTrianglesVertices
       
       
int coord_x, coord_y;
       
int tu, tv; // texture coords
       
       
for( int y = 0; y < tilesInSideMap; y++ )
       
for( int x = 0; x < tilesInSideMap; x++ ) {
           
           
sf::Vertex * triangles = & vertexes[( x + y * tilesInSideMap ) * 6 ];
           
           
coord_x =( coords.x * tilesInSideMap + x );
           
coord_y =( coords.y * tilesInSideMap + y );
           
           
triangles[ 0 ].position = sf::Vector2f( coord_x * tileSide, coord_y * tileSide );
           
triangles[ 1 ].position = sf::Vector2f(( coord_x + 1 ) * tileSide, coord_y * tileSide );
           
triangles[ 2 ].position = sf::Vector2f( coord_x * tileSide,( coord_y + 1 ) * tileSide );
           
triangles[ 3 ].position = sf::Vector2f( coord_x * tileSide,( coord_y + 1 ) * tileSide );
           
triangles[ 4 ].position = sf::Vector2f(( coord_x + 1 ) * tileSide, coord_y * tileSide );
           
triangles[ 5 ].position = sf::Vector2f(( coord_x + 1 ) * tileSide,( coord_y + 1 ) * tileSide );
           
           
tu = int( coord_x * tileSide ) % 64;
           
tv = int( coord_y * tileSide ) % 64;
           
           
triangles[ 0 ].texCoords = sf::Vector2f( tu, tv );
           
triangles[ 1 ].texCoords = sf::Vector2f( tu + tileSide, tv );
           
triangles[ 2 ].texCoords = sf::Vector2f( tu, tv + tileSide );
           
triangles[ 3 ].texCoords = sf::Vector2f( tu, tv + tileSide );
           
triangles[ 4 ].texCoords = sf::Vector2f( tu + tileSide, tv );
           
triangles[ 5 ].texCoords = sf::Vector2f( tu + tileSide, tv + tileSide );
           
           
triangles[ 0 ].color = sf::Color::Red;
       
}
       
       
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
       
       
tiles.clear();;
       
for( int y = 0; y < tilesInSideMap; y++ )
       
for( int x = 0; x < tilesInSideMap; x++ ) {
           
           
Tile * tile = new Tile( x, y );
           
           
tile->position.x = float( coords.x ) * mapSide + float( x ) * tileSide;
           
tile->position.y = float( coords.y ) * mapSide + float( y ) * tileSide;
           
           
tile->sprite = sf::Sprite();
           
tile->sprite.setTexture( * tile->texture->texture );
           
           
tu = int( tile->position.x ) % tile->texture->texture->getSize().x;
           
tv = int( tile->position.y ) % tile->texture->texture->getSize().y;
           
           
tile->sprite.setTextureRect( sf::IntRect( tu, tv, tileSide, tileSide ) );
           
           
tile->sprite.setPosition( tile->position );
           
           
tiles.push_back( tile );
       
}
    }
   
   
~Map() { }
   
   
void render() {
       
       
// window->draw(vertexes); ???
       
        /*
  for (auto& t : tiles)
   window->draw(t->sprite);
  */
       
        //window->draw(vertexes,*getTexture("assets/tiles/grass")->texture);
   
}
   
private:
   
   
virtual void draw( sf::RenderTarget & target, sf::RenderStates states ) const
   
{
       
// apply the transform
       
states.transform *= getTransform();
       
       
// apply the tileset texture
       
states.texture = & * getTexture( "assets/tiles/grass" )->texture;
       
       
// draw the vertex array
       
target.draw( vertexes, states );
   
}
   
}
;

class World {
public:
   
std::vector < Map * > maps;
   
   
World() {
       
maps.clear();
       
       
// 8 x 4
       
int start_x = 4; // 8
       
int end_x = 11;
       
int start_y = 7; // 4
       
int end_y = 10;
       
       
for( int y = start_y; y <= end_y; y++ )
       
for( int x = start_x; x <= end_x; x++ )
           
 maps.push_back( new Map( x, y ) );
       
       
   
}
   
   
Map * getMap( sf::Vector2f worldPosition ) {
       
sf::Vector2i mapCoords;
       
mapCoords.x = worldPosition.x / mapSide;
       
mapCoords.y = worldPosition.y / mapSide;
       
       
Map * map = nullptr;
       
       
for( auto & m: maps ) {
           
if( m->coords == mapCoords )
               
 map = m;
           
       
}
       
       
return map;
   
}
   
   
Tile * getTile( sf::Vector2f worldPosition ) {
       
       
Map * map = getMap( worldPosition );
       
       
if( map == nullptr )
           
 return nullptr;
       
       
sf::Vector2i tileCoords;
       
tileCoords.x = int( worldPosition.x ) % int( mapSide ) / tileSide;
       
tileCoords.y = int( worldPosition.y ) % int( mapSide ) / tileSide;
       
       
return map->tiles[ tileCoords.y * tilesInSideMap + tileCoords.x ];
       
   
}
   
   
void render() {
       
sf::Clock c;
       
sf::Time start = c.getElapsedTime();
       
       
for( auto & m: maps ) {
           
window->draw( * m );
       
}
       
       
       
sf::Time end = c.getElapsedTime();
       
       
cout <<( end - start ).asMilliseconds() << " ms,\t";
       
cout <<( end - start ).asSeconds() << " s \n";
   
}
}
;

World * world;

#endif
#endif
P-181283
« 1 » 2 3
  Strona 1 z 3 Następna strona