Dlaczego tekst zawijany jest w nieprawidłowy sposób?

0

Witam! Mam metodę, która wyświetla tekst i chciałem dodać do niej możliwość przesunięcia tekstu do następnej linijki w momencie kiedy wystąpi '\n'

typedef unsigned int uint;

inline void RemoveCharFromString(std::string& string, char remove)                         
    {
        if (string.find(remove) != std::string::npos) {
            string.erase(std::remove(string.begin(), string.end(), remove), string.end());
        }
    }

void DrawString(const std::string& text, const Vector2& position, uint color, float scale, const Font& font)
    {
        using namespace ftgl;
        texture_font_t* ftFont = font.getFTFont();

        Vector2 finalPosition = position;
        std::string finalText = text;

        float lineHeight = font.getFontSize() * scale;

        for (uint i = 0; i < finalText.length(); ++i) {
            texture_glyph_t* glyph = texture_font_get_glyph(ftFont, finalText.c_str() + i);
            if (glyph) {
                float kerning = 0.0f;
                if (i > 0) {
                    kerning = texture_glyph_get_kerning(glyph, finalText.c_str() + i - 1);
                }   
                finalPosition.x += kerning * scale;

                                //Usuwam '\n', bo na ekranie pokazywał się kwadrat, a tego nie chce
                RemoveCharFromString(finalText, '\n');
                                //sprawdzam czy w tym nie usuniętym tekście jest gdzieś '\n', jeśli jest to tekst przesuwamy w dół i pozycję X ustawiamy na początek tekstu
                if(text[i] == '\n') {
                    finalPosition.y -= lineHeight;
                    finalPosition.x = position.x;
                }

                float x0 = finalPosition.x + static_cast<float>(glyph->offset_x) * scale,
                      y0 = finalPosition.y + static_cast<float>(glyph->offset_y) * scale,
                      x1 = x0 + static_cast<float>(glyph->width) * scale,
                      y1 = y0 - static_cast<float>(glyph->height) * scale,

                      u0 = glyph->s0,
                      v0 = glyph->t0,
                      u1 = glyph->s1,
                      v1 = glyph->t1;

                               //Dalej są x0, x1 itd.. przekazywane do buffera, nie ma znaczenia dla sprawy

                finalPosition.x += glyph->advance_x * scale;
            }
        }
    }

Problem pojawia się gdy w tekście jest więcej niż jeden '\n'

np.

Font jakisFont(...);

DrawString("String\nString", {300.0f, 300.0f}, 0xffffffff, 1.0f, jakisFont);

wyświetla

String
String

ale już

DrawString("String\nString\nString", {300.0f, 300.0f}, 0xffffffff, 1.0f, jakisFont);

wyświetla

String
Strings
tring

a powinien

String
String
String

Dlaczego tak się dzieje? W jaki sposób to naprawić?

0

IMO problemem jest samo podejście do tego zadania.

Niepotrzebnie usuwasz znaki z ciągu, przez co trudniej później dobrać się do danych konkretnych linii. Chyba najłatwiejszy sposób – i przy okazji czytelny – to podzielenie ciągu znaków na linie w pierwszym kroku, a w drugim ustalenie pozycji każdej linii. Ogólnie mowa o rozbiciu problemu renderowania tekstu na kilka kroków, gdzie każdy z nich to osobna metoda wykonująca jedną, konkretną czynność.

Nie jestem specjalistą od gamedevu (ani od C++), ale w swoim platformerku implementowałem znacznie bardziej złożony mechanizm renderowania tekstu z bitmap znaków (wieloliniowość, dowolny align i proste formatowanie), więc jeśli chcesz, to co nieco mogę na ten temat napisać.

0

Niepotrzebnie usuwasz znaki z ciągu, przez co trudniej później dobrać się do danych konkretnych linii.

Jak nie usunę to \n jest renderowane właśnie tak (na jakość tekstu proszę nie zwracać uwagi, korzystam z algorytmu signed distance field i mam ustawione wartości dla małego tekstu, a dla zwykłego sprawdzania nie chce mi się zmieniać):
screenshot-20181110210557.png
a problemu z odczytaniem danych kolejnej linii nie powinno być problemu, bo zawsze mamy const std::string& text, który jest niezmienny.

Chyba najłatwiejszy sposób – i przy okazji czytelny – to podzielenie ciągu znaków na linie w pierwszym kroku, a w drugim ustalenie pozycji każdej linii.

tutaj nie za bardzo rozumiem o co ci chodzi.

0

Mam na myśli to, że dla ułatwienia roboty możesz sobie najpierw podzielić wejściowy ciąg na podciągi, łącznie z pustymi liniami. Wrzuć sobie je do jakiegoś kontenera, np. do wektora stringów. Wydziel sobie kod odpowiedzialny za renderowanie do osobnej metody, do której owy wektor przekaż w parametrze. W tej metodzie po prostu iteruj po ciągach i renderuj tekst linia po linii, inkrementując offset Y w każdej iteracji.

Po prostu rozbij sobie jeden problem na dwa mniejsze – jeden to podział tekstu na linie, a drugi to renderowanie. Możesz sobie też samo renderowanie rozbić na dwie metody, bo malowanie jednej linii też wymaga kilku instrukcji i obliczeń (offsety, skalowanie).

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0