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ść
pekfos
» 2024-06-29 19:25:48
W dobrym kierunku zmierzam ?
Wziąłeś poprawny kod i go zepsułeś, więc chyba nie. Porównaj sobie ten fragment z oryginałem
C/C++
tu = int( coords.x * tilesInSideMap + x );
tv = int( coords.y * tilesInSideMap + y );

triangles[ 0 ].position = sf::Vector2f( tu * tileSide, tv * tileSide );
triangles[ 1 ].position = sf::Vector2f(( tu + 1 ) * tileSide, tv * tileSide );
triangles[ 2 ].position = sf::Vector2f( tu * tileSide,( tv + 1 ) * tileSide );
triangles[ 3 ].position = sf::Vector2f( tu * tileSide,( tv + 1 ) * tileSide );
triangles[ 4 ].position = sf::Vector2f(( tu + 1 ) * tileSide, tv * tileSide );
triangles[ 5 ].position = sf::Vector2f(( tu + 1 ) * tileSide,( tv + 1 ) * tileSide );

triangles[ 0 ].texCoords = sf::Vector2f( 0, 0 );
triangles[ 1 ].texCoords = sf::Vector2f( 64, 0 );
triangles[ 2 ].texCoords = sf::Vector2f( 0, 64 );
triangles[ 3 ].texCoords = sf::Vector2f( 0, 64 );
triangles[ 4 ].texCoords = sf::Vector2f( 64, 0 );
triangles[ 5 ].texCoords = sf::Vector2f( 64, 64 );
W tym wypadku tu i tv powinno być zerowe, skoro masz na razie jedną grafikę w teksturze.
P-181285
tBane
Temat założony przez niniejszego użytkownika
» 2024-06-29 19:36:39
Zdążyłem już zmienić kod. Edytowałem posta.
Faktycznie jest to lepsza metoda renderingu gdyż prędkość wzrosła 20x. Poprawiłem tu i tv i teraz działa. Wrzucam kod;

0 ms,   0.000224 s
0 ms,   0.00026 s
0 ms,   0.000228 s
0 ms,   0.000231 s
0 ms,   0.000227 s
0 ms,   0.000227 s
0 ms,   0.000229 s
0 ms,   0.000231 s
0 ms,   0.000226 s
0 ms,   0.000234 s

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 );
       
}
       
       
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
       
       
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
P-181286
tBane
Temat założony przez niniejszego użytkownika
» 2024-06-29 19:49:35
Mam jednak jeszcze jedno pytanie. Czy warto w takim przypadku sprawdzać czy mapa jest w zasięgu widzenia kamery/gracza, czy nie ma to żadnego znaczenia - nie wiem dokładnie jak dziedziczy po drawable, transformable a nie chciałbym żeby zacinało przy renderowaniu większej ilości map.
P-181287
pekfos
» 2024-06-29 20:52:25
Test na zasięg kamery jest tani, więc pewnie warto. Ale czy będzie to zauważalna optymalizacja, to tego nie wiadomo. Możesz to dodać później, jak pomiary wskażą że warto.
P-181288
tBane
Temat założony przez niniejszego użytkownika
» 2024-06-30 14:06:45
Dobra jeszcze jedno pytanie. Jak renderować różne tekstury. Bo próbuję coś zakodzić od dłuższego czasu i mi to nie wychodzi, a w poradniku tego nie ma. Nie mam pojęcia jak to edytować:
states.texture = &*getTexture("assets/tiles/grass")->texture;
i jak podpiąć teksturę zależną od współrzędnych.

C/C++
class Map
    : public sf::Drawable
    , public sf::Transformable
{
public:
   
sf::Vector2i coords;
   
sf::VertexArray vertexes;
   
sf::Texture textures[ 16 ][ 16 ]; // tylko na "TEST" by sprawdzic jak renderować różne tekstury
   
   
Map( int x, int y ) {
       
       
coords.x = x;
       
coords.y = y;
       
       
vertexes.setPrimitiveType( sf::Triangles );
       
vertexes.resize( 16 * 16 * 6 ); // widthMap * heightMap * TwoTrianglesVertices
       
       
int coord_x, coord_y;
       
int tu, tv; // texture coords
       
       
for( int y = 0; y < 16; y++ )
       
for( int x = 0; x < 16; x++ ) {
           
           
// TEST
           
int r = rand() % 4;
           
if( r == 0 )
               
 textures[ x ][ y ] = * getTexture( "assets/tiles/grass" )->texture;
           
           
if( r == 1 )
               
 textures[ x ][ y ] = * getTexture( "assets/tiles/sands" )->texture;
           
           
if( r == 2 )
               
 textures[ x ][ y ] = * getTexture( "assets/tiles/water" )->texture;
           
           
if( r == 3 )
               
 textures[ x ][ y ] = * getTexture( "assets/tiles/floor" )->texture;
           
//
           
           
sf::Vertex * triangles = & vertexes[( y * 16 + x ) * 6 ];
           
           
coord_x =( coords.x * 16 + x );
           
coord_y =( coords.y * 16 + 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 );
       
}
    }
   
   
~Map() { }
   
   
private:
   
   
virtual void draw( sf::RenderTarget & target, sf::RenderStates states ) const
   
{
       
// apply the transform
       
states.transform *= getTransform();
       
       
// jak zmieniać tekstury ?
        // states.texture = getTexture(x,y);
       
states.texture = & * getTexture( "assets/tiles/grass" )->texture;
       
       
// draw the vertex array
       
target.draw( vertexes, states );
       
   
}
   
}
;
P-181289
pekfos
» 2024-06-30 17:34:36
Jak renderować różne tekstury. Bo próbuję coś zakodzić od dłuższego czasu i mi to nie wychodzi, a w poradniku tego nie ma. Nie mam pojęcia jak to edytować:
states.texture = &*getTexture("assets/tiles/grass")->texture;
i jak podpiąć teksturę zależną od współrzędnych.
Tam jest może 5 zdań, ale to akurat jest.
Note that we can apply this strategy only if the whole tile set can fit into a single texture. [..] You can download the tileset used for this tilemap example here.

P-181290
tBane
Temat założony przez niniejszego użytkownika
» 2024-06-30 17:46:50
Dobra, zrobiłem jak doradziłeś. Zastąpiłem zmienną terrainType odpowiednim przypisywaniem tu, tv. Wszystko działa.

tileset:

Kod mapy:
C/C++
#ifndef Maps_hpp
#define Maps_hpp

float tileSide = 16.0f;

class Map
    : public sf::Drawable
    , public sf::Transformable
{
public:
   
sf::Vector2i coords;
   
sf::VertexArray vertexes;
   
sf::Texture tileset;
   
   
Map( int x, int y ) {
       
       
tileset = sf::Texture();
       
tileset.loadFromFile( "assets/tiles/tileset.png" );
       
       
coords.x = x;
       
coords.y = y;
       
       
vertexes.setPrimitiveType( sf::Triangles );
       
vertexes.resize( 16 * 16 * 6 ); // widthMap * heightMap * TwoTrianglesVertices
       
       
int coord_x, coord_y;
       
int tu, tv; // texture coords
       
       
for( int y = 0; y < 16; y++ )
       
for( int x = 0; x < 16; x++ ) {
           
           
sf::Vertex * triangles = & vertexes[( y * 16 + x ) * 6 ];
           
           
coord_x =( coords.x * 16 + x );
           
coord_y =( coords.y * 16 + 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 );
       
}
    }
   
   
void editTile( sf::Vector2f worldMousePosition, int tileNumb = 0 ) {
       
       
int global_x = worldMousePosition.x / 16;
       
int global_y = worldMousePosition.y / 16;
       
       
int onMap_x = global_x - coords.x * 16;
       
int onMap_y = global_y - coords.y * 16;
       
       
if( onMap_x < 0 || onMap_x > 15 || onMap_y < 0 || onMap_y > 15 )
           
 return;
       
       
sf::Vertex * triangles = & vertexes[( onMap_y * 16 + onMap_x ) * 6 ];
       
       
int tu =( int( global_x * tileSide ) % 64 ) +( tileNumb * 64 );
       
int tv =( int( global_y * tileSide ) % 64 );
       
       
cout << "tu: " << tu << ", tv: " << tv << "\n";
       
       
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 );
   
}
   
   
~Map() { }
   
   
private:
   
   
virtual void draw( sf::RenderTarget & target, sf::RenderStates states ) const
   
{
       
// apply the transform
       
states.transform *= getTransform();
       
       
states.texture = & tileset;
       
       
// draw the vertex array
       
target.draw( vertexes, states );
       
   
}
   
}
;

class World {
public:
   
std::vector < Map * > maps;
   
   
World() {
       
maps.clear();
       
       
// 8 x 4
       
int start_x = 0;
       
int end_x = 10;
       
int start_y = 0;
       
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 /( 16 * tileSide );
       
mapCoords.y = worldPosition.y /( 16 * tileSide );
       
       
Map * map = nullptr;
       
       
for( auto & m: maps ) {
           
if( m->coords == mapCoords )
               
 map = m;
           
       
}
       
       
return map;
   
}
   
   
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

Użycie:
C/C++
if( !GUIwasClicked && prefabToPaint != nullptr && prefabToPaint->type == gameObjectType::Terrain && sf::Mouse::isButtonPressed( sf::Mouse::Left ) ) {
   
   
Terrain * terrainPrefab = dynamic_cast < Terrain * >( prefabToPaint );
   
Map * map = world->getMap( worldMousePosition );
   
if( map != nullptr ) {
       
       
if( terrainPrefab->name == "assets/tiles/grass" )
           
 map->editTile( worldMousePosition, 0 );
       
       
if( terrainPrefab->name == "assets/tiles/sands" )
           
 map->editTile( worldMousePosition, 1 );
       
       
if( terrainPrefab->name == "assets/tiles/water" )
           
 map->editTile( worldMousePosition, 2 );
       
       
if( terrainPrefab->name == "assets/tiles/floor" )
           
 map->editTile( worldMousePosition, 3 );
       
   
}
   
}

P-181291
tBane
Temat założony przez niniejszego użytkownika
» 2024-07-14 23:05:41
Chciałbym teraz rozdzielić podłogę(parkiet, płytki etc.) od kafelków(trawa, kamienie, piasek, skały etc.). Podłoga tak samo jak płytki ma mieć rozmiar 16x16 px z użyciem tekstury 64x64 px. Czy dobrym rozwiązaniem będzie stworzenie drugiego vertexarray i dynamicznie przypisywanie do niego elementów ?

C/C++
sf::VertexArray floor; // deklaracja listy
floor.clear();
floor.append( sf::Vertex( position, texCoords ) ); // dodawanie elementów

Czy lepiej stworzyć drugą listę statyczną i pole bez podłogi renderować jako transparent w floorset.png (tileset dla podłogi)

Jeszcze zastanawiam się nad tym
C/C++
sf::Sprite spr;
spr.setTextureRect( sf::IntRect( tu, tv, 16, 16 ) );
P-181313
1 « 2 » 3
Poprzednia strona Strona 2 z 3 Następna strona