71284


はじめまして、コードの初心者です。SDL2でゲームを作り勉強始めました。LazyFoo sdl を参考に使っていました。

ゲーム風に一文字ずつ表したいですが、日本語のフォントでは変な四角形がありました。

[![画像の説明をここに入力](https://i.stack.imgur.com/naUkN.gif)](https://i.stack.imgur.com/naUkN.gif)

でも英語の方は大丈夫でした。

[![画像の説明をここに入力](https://i.stack.imgur.com/zb1rE.gif)](https://i.stack.imgur.com/zb1rE.gif)

他のフォントも試しましたがダメでした。

普通の文は問題がありません。

[![画像の説明をここに入力](https://i.stack.imgur.com/20iI0.png)](https://i.stack.imgur.com/20iI0.png)

いい解決方法がないでしょうか？それとも別のやり方がないでしょうか？  
これ修正出来ないなら別の方法でゲーム風に一文字ずつ表すことができますか？

使っているOSはFedora32です。

* * *

### ソースコード

[code]

    #include <SDL2/SDL.h>
    #include <SDL2/SDL_image.h> 
    #include <SDL2/SDL_ttf.h>
    #include <string>
    #include <stdio.h>
    
    const int win_w = 800;
    const int win_h = 600;
    
    SDL_Window *window;
    SDL_Renderer *renderer;
    
    TTF_Font *font;
    
    class Layer
    {
       public:
            Layer()
            {
                mTexture = NULL;
                mWidth = 0;
                mHeight = 0;
            }
        
            ~Layer()
            {
                free();
            }
        
            void free()
            {
                if (mTexture != NULL)
                {
                    SDL_DestroyTexture(mTexture);
                    mTexture = NULL;
                    mWidth = 0;
                    mHeight = 0; 
                }
            }
        
            SDL_Texture *loadText(std::string Text, SDL_Color textColor)
            {
                free();
            
                SDL_Surface *textSurface = TTF_RenderUTF8_Blended(font, Text.c_str(), textColor);
                mTexture = SDL_CreateTextureFromSurface(renderer, textSurface);
            
                mWidth = textSurface->w;
                mHeight = textSurface->h;
            
                SDL_FreeSurface(textSurface);
            
                return mTexture;            
            }
         
            void render(int x, int y)
            {
                SDL_Rect render_rect = {x, y, mWidth, mHeight};
            
                SDL_RenderCopy(renderer, mTexture, NULL, &render_rect);
            }
    
        private:
            SDL_Texture *mTexture;
            int mWidth;
            int mHeight;
    };
    
    Layer lyrText;
    
    void run_text(std::string text, int x, int y)
    {
        std::string container;
    
        SDL_Color textColor = {0, 0, 0};
    
        for (int i = 0; i < text.length(); i++)
        {
            SDL_Delay(100);
            container += text[i];
            lyrText.loadText(container, textColor);
            lyrText.render(x, y);
        
            SDL_RenderPresent(renderer);
        } 
    }
    
    void init()
    {
        SDL_Init(SDL_INIT_EVERYTHING);
        IMG_Init(IMG_INIT_PNG);
        TTF_Init();
    
        window = SDL_CreateWindow("text", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, win_w, win_h, SDL_WINDOW_SHOWN);
    
        renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
    }
    
    void init_load()
    {
        font = TTF_OpenFont("./Fonts/GenShinM.ttf", 28);
    }
    
    void close()
    {
        lyrText.free();
    
        TTF_CloseFont(font);
    
        SDL_DestroyRenderer(renderer);
        SDL_DestroyWindow(window);
    
        TTF_Quit();
        IMG_Quit();
        SDL_Quit();
    }
    
    int main(int argc, char *args[])
    {
        init();
        init_load();
    
        SDL_Event event;
        int quit = 0;
    
        SDL_SetRenderDrawColor(renderer, 0xff, 0xff, 0xff, 0xff);
        SDL_RenderClear(renderer);
    
        run_text("私の名前は。。。", 200, 300);
        run_text("これは日本語フォント", 400, 200);
    
        while (quit == 0)
        {
            while (SDL_PollEvent(&event))
            {
                if (event.type == SDL_QUIT)
                {
                    quit = 1;
                }
            }
        }
     
        close();
    
        return 0;
    }
    
[/code]


CC BY-SA 4.0
2020-10-17T08:34:50.057
71275
2020-10-18T06:07:33.220
2020-10-18T06:07:33.220
3060
41041
question
c++
SDL2 で日本語フォントを使用すると、文字に重なった四角形が表示されてしまう


デバッガ(gdb)で `run_text()` 関数の内部処理を調べてみます。

[code]

    $ lsb_release -ir
    Distributor ID: Ubuntu
    Release:    20.04
    $ uname -srm
    Linux 5.4.0-51-generic x86_64
    $ g++ --version
    g++ (Ubuntu 10.2.0-5ubuntu1~20.04) 10.2.0
    
    $ g++ -std=c++17 -Wall -Wextra -g show_message.cc -o show_message -lSDL2 -lSDL2_image -lSDL2_ttf
    $ gdb show_message
    (gdb) break run_text
    (gdb) run
    Starting program: ...
    Thread 1 "..." hit Breakpoint 1, run_text (
               :
    (gdb) set charset UTF-8
    (gdb) p text
    $10 = "私の名前は。。。"
    (gdb) p text.length()
    $11 = 24
    (gdb) set charset ASCII
    (gdb) p (char *)text
    $15 = 0x555555e2f020 "\347\247\201\343\201\256\345\220\215\345\211\215\343\201\257\343\200\202\343\200\202\343\200\202"
    
[/code]

UTF-8 encoding の文字列「私の名前は。。。」の「長さ」は `24`(= 3bytes * 8文字)
になっていることが判るかと思います。実際には、「私の名前は。。。」の「文字列長」は `8` であることが期待されているはずです。

そこで、`UTF-8`(内部表現は可変長) <-> `UTF-16`(内部表現は固定長)への変換を行なうことで解決する方法が考えられます。

[code]

    #include <codecvt> 
    #include <locale>
    
    
    void run_text(std::string text, int x, int y)
    {
        std::string container;
        SDL_Color textColor = {.r = 0, .g = 0, .b = 0, .a = 0};
        std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
        for (auto && c : converter.from_bytes(text)) { // UTF-8 -> UTF-16
            SDL_Delay(100);
            // UTF-16 -> UTF-8
            container += converter.to_bytes(c);
            lyrText.loadText(container, textColor);
            lyrText.render(x, y);
            SDL_RenderPresent(renderer);
        } 
    }
    
[/code]

[![show_message](https://i.stack.imgur.com/zxyOz.gif)](https://i.stack.imgur.com/zxyOz.gif)


CC BY-SA 4.0
2020-10-17T14:17:49.733
71284
2020-10-17T14:17:49.733
71275
answer