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;
}

最后则是释放内存。

五.程序演示

 六.不足之处

首先,无法输入中文,然后就是没有游标,只能从后往前依次删除。

本节代码较少,就不上传了。

另外,有关文本输入的可以看这篇,虽然这篇也没有中文相关的处理。。。

猜你喜欢

转载自blog.csdn.net/bull521/article/details/84495417
SDL