SDL 簡單文本的輸入處理

這幾天忙着局域網聯機相關內容,俗話說,用到什麼學什麼(這什麼俗話。。。),我大致分析了一下我的需求,大致如下:

  • 遊戲使用局域網聯機,每個用戶既可以當客戶端,又可以當服務器,但服務器僅有一個,即房主。
  • 服務器端顯示IP地址。
  • 客戶端能輸入IP地址和個人的名稱,來嘗試鏈接入服務器。

前兩個在前面寫的SDL_net文章中大致都實現了,接下來就是實現IP地址和用戶名的輸入(英文、數字)。

一.相關API

1.void SDL_StartTextInput(void)

調用該函數使得SDL開始接收文本輸入事件。

2.void SDL_StopTextInput(void)

調用該函數使得SDL停止接收文本輸入事件。

3.SDL_bool SDL_IsTextInputActive(void)

判斷SDL是否正在接收文本輸入事件。

4.void SDL_SetTextInputRect(SDL_Rect* rect)

該函數如果依照這篇帖子的說法的話,它的作用就是“聯想”,即比如輸入sk,可能顯示sky,不過我測試之後,nothing。

二.本節目標

根據上述所言,其實用到的主要是第一個和第二個函數以及在事件輪訓中進行相應處理。

那麼本節目標如下:給定一個矩形(輸入框),如果點擊了矩形,則可以輸入文本,如果點擊了矩形外或者點擊了回車鍵,則停止輸入(這個在移動端尤其重要,因爲移動端調用SDL_StartTextInput()會出現虛擬鍵盤),然後點擊backspace會刪去最後一個字符。

三.環境需求

  1. SDL2
  2. SDL2_ttf
  3. cmake
  4. FontCache:https://github.com/grimfang4/SDL_FontCache

FontCache是對SDL_ttf的優化,本節也只是簡單使用了一下,並沒有測試其效率如何。

不過我個人是不太喜歡使用SDL_ttf的,因爲SDL_ttf在移動端效率極低,尤其對於那些文本豐富的遊戲,如RPG遊戲;

另外一個選擇是圖字,原理類似於活字印刷書,需要哪個字就選出哪個字,然後繪製到對應的位置即可,具體軟件如BMFont hiero,我個人認爲BMFont好用,不過此軟件不跨平臺,所以我目前用的是hiero,這種在遊戲引擎中使用的比較多,如cocos2dx。

四.編碼實現

#include <SDL.h>
#include "SDL_FontCache.h"

char text[1024] = {}; 
char* composition = nullptr;
Sint32 cursor = 0;
Sint32 selection_len = 0;

SDL_Window* gWindow = nullptr;
SDL_Renderer* gRen = nullptr;

bool init();

爲方便,以上變量爲全局變量。

bool init()
{
        //初始化SDL
        if(SDL_Init(SDL_INIT_TIMER | SDL_INIT_AUDIO | SDL_INIT_VIDEO | SDL_INIT_EVENTS)!=0)
        {
                printf("%s\n",SDL_GetError());
                return false;
        }
        gWindow = SDL_CreateWindow("test", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 640, SDL_WINDOW_SHOWN);
        gRen = SDL_CreateRenderer(gWindow,-1,SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE
                | SDL_RENDERER_PRESENTVSYNC);
        return true;
}
   

init函數是對SDL初始化,並創建了窗口和對應的渲染器。

接着就是主函數的編寫。

int main(int argc, char* argv[])
{
        bool running = true;
        SDL_Event event;
        init();
        FC_Font* font = FC_CreateFont();
        FC_LoadFont(font, gRen, "fonts/FreeSans.ttf", 20, FC_MakeColor(255, 255, 255, 255), TTF_STYLE_NORMAL);

        SDL_Rect rect = {200, 200, 100, 100};
        //SDL_SetTextInputRect(&rect);

先初始化,之後創建了FontCache的一個實例,並加載一個ttf文件。另外,我選定rect這個矩形爲輸入框的範圍。之後則是一個大循環:

        while (running)
        {
                //繪製
                SDL_SetRenderDrawColor(gRen, 0, 255, 255, 255);
                SDL_RenderClear(gRen);
                SDL_SetRenderDrawColor(gRen, 0, 0, 255, 255);
                SDL_RenderFillRect(gRen, &rect);
                FC_Draw(font, gRen, rect.x, rect.y, "%s", text);
                SDL_RenderPresent(gRen);

上述代碼的主要功能就是繪製,先依照rect填充一個矩形,然後繪製text內包含的文本,一開始text數組爲空。接下來是事件輪訓。

               while (SDL_PollEvent(&event))
                {
                        switch (event.type)
                        {
                                case SDL_QUIT:
                                        running = false;
                                        break;
                                case SDL_TEXTINPUT:
                                        strcat(text, event.text.text);
                                        //printf("%s\n", text);
                                        break;
                                case SDL_TEXTEDITING:
                                        composition = event.edit.text;
                                        cursor = event.edit.start;
                                        selection_len = event.edit.length;
                                        printf("%s %d %d\n", composition, cursor, selection_len);
                                        break;

先檢測是否點擊了退出按鈕,這個是程序的退出條件;在調用了SDL_StartTextInput()函數之後,SDL纔會產生SDL_TEXTINPUT和SDL_TEXTEDITING事件,然後在SDL_TEXTINPUT事件中負責添加字符(注:我從未觸發SDL_TEXTEDITING事件,不知道什麼原因)。

SDL_TEXTEDITING和SDL_SetTextInputRect()函數有關,主要負責文本“聯想功能”。詳情

                                case SDL_MOUSEBUTTONDOWN:
                                        {
                                                SDL_Point pos = {event.motion.x, event.motion.y};
                                                //判斷是否在矩形內
                                                if (SDL_PointInRect(&pos, &rect))
                                                {
                                                        SDL_StartTextInput();
                                                }
                                                else
                                                {
                                                        SDL_StopTextInput();
                                                }
                                        }
                                        break;

SDL_MOUSEBUTTONDOWN表示鼠標點擊事件.獲取鼠標點擊的位置,然後和矩形判斷,如果點擊 了矩形,則開始文本輸入;否則停止文本輸入。

(注:如果需要移植到Android,則可以把SDL_MOUSEBUTTONDOWN改爲SDL_FINGERDOWN。不過,默認情況下,android也可以響應鼠標事件,具體可看SDL wiki,搜索SDL_HINT_ANDROID_SEPARATE_MOUSE_AND_TOUCH 或SDL_SetHint)

                              case SDL_KEYDOWN:
                                        {
                                                auto key = event.key.keysym.sym;
                                                if (key == SDLK_BACKSPACE && strlen(text))
                                                {
                                                        text[strlen(text) - 1] = '\0';
                                                }
                                                else if (key == SDLK_RETURN)
                                                {
                                                        SDL_StopTextInput();
                                                }
                                        }
                                        break;
                        }

然後就是判斷是否點擊了按鍵。如果點擊了backspace,則刪除最後一個字符;如果點擊了回車鍵,則停止輸入。

        }
        FC_FreeFont(font);
        SDL_DestroyRenderer(gRen);
        SDL_DestroyWindow(gWindow);

        return 0;
}

最後則是釋放內存。

五.程序演示

 六.不足之處

首先,無法輸入中文,然後就是沒有遊標,只能從後往前依次刪除。

本節代碼較少,就不上傳了。

另外,有關文本輸入的可以看這篇,雖然這篇也沒有中文相關的處理。。。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章