Jak dodać prefabrykaty kafelków wody do palety

Jak dodać prefabrykaty kafelków wody do palety
tBane
  • Rejestracja:ponad rok
  • Ostatnio:około 10 godzin
  • Lokalizacja:Poznań
  • Postów:312
0

Witam.
Mam obiekty Terrain oraz Water dla każdego z chunków mapy. Chciałbym dodać prefaby wody do palety, tak aby można było je wybrać i rysować tę wodę po terenie. Problem, który napotkałem polega na tym, że wybranie prefabu wody jest również prefabem terenu - set_0_water_sands - set_0_sands_water. Nie wiem jak to pogodzić i jak uporządkować kod. Planuję również w niedalekiej przyszłości rysować shaderem na tym szarym polu. No i właśnie to jest ten problem - jak stworzyć prefabrykaty dla wody ?

Poniżej obrazek przedstawiający kafelki granicze dla terenu
palette.gif

Tak wczytuje prefaby Terenu:

Kopiuj
void createTerrainPrefabs() {
    terrainGameObjects.clear();
    
    // TO-DO
    terrainGameObjects.push_back(new TerrainPrefab("tiles/tile_0_water", 0));
    terrainGameObjects.push_back(new TerrainPrefab("tiles/tile_1_sands", 1));
    terrainGameObjects.push_back(new TerrainPrefab("tiles/tile_2_grass", 2));
    terrainGameObjects.push_back(new TerrainPrefab("tiles/tile_3_gravel",3));
    terrainGameObjects.push_back(new TerrainPrefab("tiles/tile_4_steps", 4));

    countOfBasicTerrain = 5;

    std::vector < string > sets_names;
    sets_names.push_back("tiles/set_0_water_sands");
    sets_names.push_back("tiles/set_0_sands_water");
    sets_names.push_back("tiles/set_1_sands_grass");
    sets_names.push_back("tiles/set_1_grass_sands");

    short id = countOfBasicTerrain;

    for (auto& texture : textures) {
        for (auto& name : sets_names) {
            if (texture->name.find(name) != std::string::npos) {
                
                TerrainPrefab* tpref = new TerrainPrefab(texture->name, id);
                terrainGameObjects.push_back(tpref);
                //cout << tpref->name << "\t" << id << "\n";
                id += 1;
            }
        }
    }


    // create tileset
    sf::RenderTexture rtex;
    rtex.create(id * 64, 64);
    rtex.clear(sf::Color::Transparent);

    short offsetX = 0;

    for (auto& t : terrainGameObjects) {
        sf::Texture tex = *dynamic_cast<TerrainPrefab*>(t)->texture->texture;
        sf::Sprite spr(tex);
        spr.setPosition(offsetX, 0);
        rtex.draw(spr);
        offsetX += 64;
    }
    rtex.display();

    tileset = rtex.getTexture();
    *getTexture("tiles/0_tileset")->texture = tileset;

}

A tak przypisuję te obiekty do palety:

Kopiuj
void setTerrainObjectsToPalette() {

    availableGameObjects.clear();

    availableGameObjects.push_back(terrainGameObjects[0]);
    availableGameObjects.push_back(terrainGameObjects[1]);
    availableGameObjects.push_back(terrainGameObjects[2]);
    availableGameObjects.push_back(terrainGameObjects[3]);
    availableGameObjects.push_back(terrainGameObjects[4]);

    availableGameObjects.push_back(nullptr);
    availableGameObjects.push_back(nullptr);
    availableGameObjects.push_back(nullptr);
    availableGameObjects.push_back(nullptr);
    availableGameObjects.push_back(nullptr);

    // SET - WATER_SANDS
    short id  = 4;

    availableGameObjects.push_back(terrainGameObjects[id + 1]);
    availableGameObjects.push_back(terrainGameObjects[id + 2]);
    availableGameObjects.push_back(terrainGameObjects[id + 2]);
    availableGameObjects.push_back(terrainGameObjects[id + 2]);
    availableGameObjects.push_back(terrainGameObjects[id + 3]);

    availableGameObjects.push_back(terrainGameObjects[id + 4]);
    availableGameObjects.push_back(terrainGameObjects[1]);
    availableGameObjects.push_back(terrainGameObjects[1]);
    availableGameObjects.push_back(terrainGameObjects[1]);
    availableGameObjects.push_back(terrainGameObjects[id + 5]);

    availableGameObjects.push_back(terrainGameObjects[id + 4]);
    availableGameObjects.push_back(terrainGameObjects[1]);
    availableGameObjects.push_back(terrainGameObjects[1]);
    availableGameObjects.push_back(terrainGameObjects[1]);
    availableGameObjects.push_back(terrainGameObjects[id + 5]);

    availableGameObjects.push_back(terrainGameObjects[id + 4]);
    availableGameObjects.push_back(terrainGameObjects[1]);
    availableGameObjects.push_back(terrainGameObjects[1]);
    availableGameObjects.push_back(terrainGameObjects[1]);
    availableGameObjects.push_back(terrainGameObjects[id + 5]);

    availableGameObjects.push_back(terrainGameObjects[id + 6]);
    availableGameObjects.push_back(terrainGameObjects[id + 7]);
    availableGameObjects.push_back(terrainGameObjects[id + 7]);
    availableGameObjects.push_back(terrainGameObjects[id + 7]);
    availableGameObjects.push_back(terrainGameObjects[id + 8]);

    availableGameObjects.push_back(nullptr);
    availableGameObjects.push_back(nullptr);
    availableGameObjects.push_back(nullptr);
    availableGameObjects.push_back(nullptr);
    availableGameObjects.push_back(nullptr);
    
    // SET - SANDS_WATER
    id += 8;

    availableGameObjects.push_back(terrainGameObjects[id + 1]);
    availableGameObjects.push_back(terrainGameObjects[id + 2]);
    availableGameObjects.push_back(terrainGameObjects[id + 2]);
    availableGameObjects.push_back(terrainGameObjects[id + 2]);
    availableGameObjects.push_back(terrainGameObjects[id + 3]);

    availableGameObjects.push_back(terrainGameObjects[id + 4]);
    availableGameObjects.push_back(terrainGameObjects[0]);
    availableGameObjects.push_back(terrainGameObjects[0]);
    availableGameObjects.push_back(terrainGameObjects[0]);
    availableGameObjects.push_back(terrainGameObjects[id + 5]);

    availableGameObjects.push_back(terrainGameObjects[id + 4]);
    availableGameObjects.push_back(terrainGameObjects[0]);
    availableGameObjects.push_back(terrainGameObjects[0]);
    availableGameObjects.push_back(terrainGameObjects[0]);
    availableGameObjects.push_back(terrainGameObjects[id + 5]);

    availableGameObjects.push_back(terrainGameObjects[id + 4]);
    availableGameObjects.push_back(terrainGameObjects[0]);
    availableGameObjects.push_back(terrainGameObjects[0]);
    availableGameObjects.push_back(terrainGameObjects[0]);
    availableGameObjects.push_back(terrainGameObjects[id + 5]);

    availableGameObjects.push_back(terrainGameObjects[id + 6]);
    availableGameObjects.push_back(terrainGameObjects[id + 7]);
    availableGameObjects.push_back(terrainGameObjects[id + 7]);
    availableGameObjects.push_back(terrainGameObjects[id + 7]);
    availableGameObjects.push_back(terrainGameObjects[id + 8]);

    availableGameObjects.push_back(nullptr);
    availableGameObjects.push_back(nullptr);
    availableGameObjects.push_back(nullptr);
    availableGameObjects.push_back(nullptr);
    availableGameObjects.push_back(nullptr);

    // SET - GRASS_SANDS
    id += 8;

    availableGameObjects.push_back(terrainGameObjects[id + 1]);
    availableGameObjects.push_back(terrainGameObjects[id + 2]);
    availableGameObjects.push_back(terrainGameObjects[id + 2]);
    availableGameObjects.push_back(terrainGameObjects[id + 2]);
    availableGameObjects.push_back(terrainGameObjects[id + 3]);

    availableGameObjects.push_back(terrainGameObjects[id + 4]);
    availableGameObjects.push_back(terrainGameObjects[1]);
    availableGameObjects.push_back(terrainGameObjects[1]);
    availableGameObjects.push_back(terrainGameObjects[1]);
    availableGameObjects.push_back(terrainGameObjects[id + 5]);

    availableGameObjects.push_back(terrainGameObjects[id + 4]);
    availableGameObjects.push_back(terrainGameObjects[1]);
    availableGameObjects.push_back(terrainGameObjects[1]);
    availableGameObjects.push_back(terrainGameObjects[1]);
    availableGameObjects.push_back(terrainGameObjects[id + 5]);

    availableGameObjects.push_back(terrainGameObjects[id + 4]);
    availableGameObjects.push_back(terrainGameObjects[1]);
    availableGameObjects.push_back(terrainGameObjects[1]);
    availableGameObjects.push_back(terrainGameObjects[1]);
    availableGameObjects.push_back(terrainGameObjects[id + 5]);

    availableGameObjects.push_back(terrainGameObjects[id + 6]);
    availableGameObjects.push_back(terrainGameObjects[id + 7]);
    availableGameObjects.push_back(terrainGameObjects[id + 7]);
    availableGameObjects.push_back(terrainGameObjects[id + 7]);
    availableGameObjects.push_back(terrainGameObjects[id + 8]);

    availableGameObjects.push_back(nullptr);
    availableGameObjects.push_back(nullptr);
    availableGameObjects.push_back(nullptr);
    availableGameObjects.push_back(nullptr);
    availableGameObjects.push_back(nullptr);

    // SET - GRASS_SANDS
    id += 8;

    availableGameObjects.push_back(terrainGameObjects[id + 1]);
    availableGameObjects.push_back(terrainGameObjects[id + 2]);
    availableGameObjects.push_back(terrainGameObjects[id + 2]);
    availableGameObjects.push_back(terrainGameObjects[id + 2]);
    availableGameObjects.push_back(terrainGameObjects[id + 3]);

    availableGameObjects.push_back(terrainGameObjects[id + 4]);
    availableGameObjects.push_back(terrainGameObjects[1]);
    availableGameObjects.push_back(terrainGameObjects[1]);
    availableGameObjects.push_back(terrainGameObjects[1]);
    availableGameObjects.push_back(terrainGameObjects[id + 5]);

    availableGameObjects.push_back(terrainGameObjects[id + 4]);
    availableGameObjects.push_back(terrainGameObjects[1]);
    availableGameObjects.push_back(terrainGameObjects[1]);
    availableGameObjects.push_back(terrainGameObjects[1]);
    availableGameObjects.push_back(terrainGameObjects[id + 5]);

    availableGameObjects.push_back(terrainGameObjects[id + 4]);
    availableGameObjects.push_back(terrainGameObjects[1]);
    availableGameObjects.push_back(terrainGameObjects[1]);
    availableGameObjects.push_back(terrainGameObjects[1]);
    availableGameObjects.push_back(terrainGameObjects[id + 5]);

    availableGameObjects.push_back(terrainGameObjects[id + 6]);
    availableGameObjects.push_back(terrainGameObjects[id + 7]);
    availableGameObjects.push_back(terrainGameObjects[id + 7]);
    availableGameObjects.push_back(terrainGameObjects[id + 7]);
    availableGameObjects.push_back(terrainGameObjects[id + 8]);

    availableGameObjects.push_back(nullptr);
    availableGameObjects.push_back(nullptr);
    availableGameObjects.push_back(nullptr);
    availableGameObjects.push_back(nullptr);
    availableGameObjects.push_back(nullptr);
}

W wolnych chwilach od codzienności programuję hobbystycznie Edytor gier RPG 2D.
Technologie, z których korzystam to C++ oraz SFML 2.6.
edytowany 1x, ostatnio: tBane
tBane
  • Rejestracja:ponad rok
  • Ostatnio:około 10 godzin
  • Lokalizacja:Poznań
  • Postów:312
0

Już znalazłem rozwiązanie ale jest to taki blok kodu, że nie udostępnie. :-)


W wolnych chwilach od codzienności programuję hobbystycznie Edytor gier RPG 2D.
Technologie, z których korzystam to C++ oraz SFML 2.6.
flowCRANE
Moderator Delphi/Pascal
  • Rejestracja:ponad 13 lat
  • Ostatnio:około 4 godziny
  • Lokalizacja:Tuchów
  • Postów:12171
0

No dobrze, ale dlaczego kafel wody jest czymś innym niż kafel podłoża? To powinny być obiekty tego samego typu, różniące się jedynie teksturą i dodatkowymi parametrami. Tak łopatologicznie pisząc, każdy typ kafli powinien dziedziczyć z bazowego typu kafli.

Wpadłeś w pułapkę — skupiłeś się na efektach (mapa z wodą, woda renderowana shaderami itp.), zamiast na implementacji i elastyczności. Efektem teraz jest to, że masz wodę zrobioną, ale architektura kodu nie pozwala na jej wygodną obsługę w edytorze. I teraz cholernie trudno będzie Ci doradzić jak się z tym uporać, bo każde rozwiązanie będzie drutowaniem kodu, produkowaniem spaghetti. 😕

Jest też druga kwestia — jak w edytorze uzupełniasz mapę w potworki? Masz możliwość wybierania ich z tego zasobnika po prawej i stawiania na mapie, czy zawsze generujesz je dynamicznie? A jeśli dynamicznie, to masz możliwość w tym edytorze wskazania spawn-pointów (albo czegoś podobnego koncepcyjnie), czy miejsca ich spawnowania są hardkodowane?

To są niezwykle istotne sprawy w takim edytorze — możliwość wybierania dowolnych obiektów dowolnego typu i ich układanie na mapie, w formie WYSIWYG. Mam tutaj na myśli absolutnie każdy wspierany typ obiektu, który wejdzie w skład mapy — teren, budynki, potworki, enpece, startowa pozycja gracza, ale też elementy niewidoczne, specjalnego przeznaczenia, takie jak np. triggery cutscenek, ograniczniki ruchu kamery itd. Nie twierdzę, że wszystkie z wymienionych są Ci potrzebne — po prostu chodzi o to, aby edytor pozwalał wygodnie układać mapę, dając możliwość stawiania wszystkich wspieranych obiektów, zawsze w ten sam sposób (zasobnik).


Sugerowałem Ci kiedyś, abyś skorzystał z techniki bottom-top — zrób wstępny projekt, zaplanuj poszczególne elementy zgodnie z tym co ostatecznie chcesz uzyskać, a następnie zacznij implementację od dołu, od najmniejszych elementów do tych bardziej i bardziej abstrakcyjnych (wysokopoziomowych). Czyli najpierw skup się na implementacji bazowych elementów, uniwersalnych klocków, z którym powstaną całe podsystemy, a z nich jeszcze większe podsystemy.

W tym przypadku byłoby to zaprojektowanie bazowych typów, zaczynając od typów kafli, poprzez typ reprezentujący całą mapę, a kończąc na edytorze map (zasobniku z elementami do układania na mapie).


Muszę przyznać, że nie za bardzo ta woda pasuje do Twojej gry. Wg mnie lepiej by było, gdybyś wywalił ten shader, zunifikował kod kafli, natomiast wodę zrobił statyczną (zbudowaną z normalnych kafli). Natomiast jeśli chciałbyś aby była animowana, to zamiast mordować ją shaderem, dodaj obiekty z prostą animacją (np. mała fala przesuwająca się w którąś stronę i znikająca po chwili) i umieść je na kaflach wody. Coś takiego będzie prostsze w implementacji, spójne implementacyjnie z terenem (kafel to kafel — nieważne jaki) oraz da wygląd spójny z resztą mapy i edytorem (pixel art).


PS: chyba zauważyłem bug w tym edytorze. Na gifie widać, że ruszasz kursorem znad mapy, przez dach budynku, aż do zasobnika (przycisk ze strzałką w dół). Dach na tym budynku znika, gdy kursor masz w obszarze zasobnika, co sugeruje, że obszar zasobnika jest przez grę traktowany jako przezroczysty — kursor jest w zasobniku, ale obiekt budynku rejestruje jego obecność i ukrywa dach. ;)


Pracuję nad własną, arcade'ową, docelowo komercyjną grą z gatunku action/adventure w stylu retro (pixel art), programując silnik i powłokę gry od zupełnych podstaw, przy użyciu Free Pascala i SDL3. Więcej informacji znajdziesz na moim mikroblogu.
tBane
  • Rejestracja:ponad rok
  • Ostatnio:około 10 godzin
  • Lokalizacja:Poznań
  • Postów:312
0

Ponieważ Chunki (fragmenty mapy) zawierają terrain oraz water jako osobne obiekty. Zastanawiałem się już nad scaleniem ale nie mógłbym wtedy wygodnie korzystać z shaderów ( musiałbym napisać jeden wielki shader do obsługi animacji wody). Dzięki za znalezienie buga :-) Z dodawaniem / usuwaniem innych obiektów problemów nie mam. Wszystkie elementy są w wygodny sposób pobierane z palety i można je umieszczać na mapie zaś usuwać przez ppm. Potworki też wstawia się z palety i jak się wybierze miejsce, w którym mają być umiejscowione to tam jest im ustawiana "baza" - punkt wokół, którego krążą. A nad tą wodą będę jeszcze pracował - muszę się w końcu zebrać i zacząć uczyć się shaderów - a dokładnie to tego trybu myślenia.


W wolnych chwilach od codzienności programuję hobbystycznie Edytor gier RPG 2D.
Technologie, z których korzystam to C++ oraz SFML 2.6.
edytowany 1x, ostatnio: tBane
LukeJL
  • Rejestracja:około 11 lat
  • Ostatnio:2 minuty
  • Postów:8423
1

Chciałbym dodać prefaby wody do palety, tak aby można było je wybrać i rysować tę wodę po terenie

To problem z UX, ale widzę na tym filmiku, że ręcznie trzeba wybierać odpowiedni wariant wody. Podczas gdybyś zrobił to tak, żeby edytor sam się domyślał wariantu (na podstawie sąsiadów danego kafelka), byłoby przyjaźniejsze dla użytkownika.

Ale i tak nie do końca rozumiem, o który aspekt problemu z mapami kafelkowymi ci chodzi.

Jednak z tego, co zrozumiałem, to tak jakbyś zrobił ALBO-ALBO, a chcesz osiągnąć TO I TO?

Planuję również w niedalekiej przyszłości rysować shaderem na tym szarym polu.

W jaki sposób trzymasz dane na mapie i jak przekazujesz te dane do shadera?
Nie wiem, jakie możliwości ma twój język shaderów, ale pisałem w GLSL w WebGL i robiłem tak, że przekazywałem dane mapy do shadera jako teksturę. I każdy piksel zawierał w rzeczywistości maskę bitową, którą shader odczytywał (np. ten bit włączony, to jest woda, ten bit włączony, to jest ulica). I potem sprawdzałem i mogłem renderować zarówno wodę jak i ulicę (i robiłem to proceduralnie akurat, i nawet sprawdzałem w shaderze sąsiednie kafle i na tej podstawie decydowałem o wariancie).

Tylko nie wiem, czy to dobry pomysł, pisanie takich rzeczy w GLSL to mordęga (choćby z powodu, że piszesz fragment shader pod rendering 1 piksela - odwrotnie niż zwykle myśli mózg programisty) i zbyt dużo ograniczeń. I kod też na kolanie pisałem.


edytowany 3x, ostatnio: LukeJL
tBane
  • Rejestracja:ponad rok
  • Ostatnio:około 10 godzin
  • Lokalizacja:Poznań
  • Postów:312
0

To problem z UX, ale widzę na tym filmiku, że ręcznie trzeba wybierać odpowiedni wariant wody. Podczas gdybyś zrobił to tak, żeby edytor sam się domyślał wariantu (na podstawie sąsiadów danego kafelka), byłoby przyjaźniejsze dla użytkownika.

No właśnie nie za bardzo wiem jak to zrobić i jeszcze nie mam pomysłu jak się za to zabrać. Jak wpadę na coś to to zrobię. Ale na razie niech będzie jak jest. Lepiej tak niż w ogóle. No z tymi shaderami faktycznie jest często problem i trudno coś znaleźć w necie i jeszcze to przepisać. Ale i tak cały czas gierka zmierza ku lepszemu.


W wolnych chwilach od codzienności programuję hobbystycznie Edytor gier RPG 2D.
Technologie, z których korzystam to C++ oraz SFML 2.6.
flowCRANE
Moderator Delphi/Pascal
  • Rejestracja:ponad 13 lat
  • Ostatnio:około 4 godziny
  • Lokalizacja:Tuchów
  • Postów:12171
2
tBane napisał(a):

Ponieważ Chunki (fragmenty mapy) zawierają terrain oraz water jako osobne obiekty.

Nie szkodzi — to możesz albo zrobić w typowy sposób, czyli w klasie dziedziczącej z bazowej klasy kafla, albo za pomocą kompozycji. Końcowa klasa kafli powinna zawierać dane zassane z klasy bazowej (uniwersalne dla kafli dowolnego typu) oraz dane specyficzne dla danego typu kafli.

Zastanawiałem się już nad scaleniem ale nie mógłbym wtedy wygodnie korzystać z shaderów ( musiałbym napisać jeden wielki shader do obsługi animacji wody).

Zawsze możesz mieć mapę zbudowaną z warstw (co bardzo mocno polecam) — w uproszczeniu, jedna dla wody, jedna dla terenu, jedna dla budynków, aktorów, drzew i innych obiektów (tyle ile potrzebujesz). Wyrenderowanie mapy na ekranie to namalowanie w oknie każdej z tych kilku, w odpowiedniej kolejności (najpierw woda, potem teren, na koniec obiekty i budynki).

W ten sposób będziesz mógł swobodnie modyfikować wodę, bez martwienia się o to aby nie tknąć tekstur terenu. Ot taka warstwa zawierałaby tylko wodę i puste przestrzenie, które i tak nie byłyby widoczne, bo one byłyby przykryte później terenem.

Z dodawaniem / usuwaniem innych obiektów problemów nie mam.

No to super. 😉


Pracuję nad własną, arcade'ową, docelowo komercyjną grą z gatunku action/adventure w stylu retro (pixel art), programując silnik i powłokę gry od zupełnych podstaw, przy użyciu Free Pascala i SDL3. Więcej informacji znajdziesz na moim mikroblogu.
edytowany 4x, ostatnio: flowCRANE
Boski
+1 dla warstw

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.