键盘输入在Cocos2Dx分为两个部分。第一部分是一些功能键的处理:后退键和菜单键。第二部分是处理字符输入。功能键相关的代码位于cocos2dx\keypad_dispatcher。字符输入的代码位于\cocos2dx\text_input_node。

我们先看功能键的处理部分。功能键的处理比较简单,只是支持后退键和菜单键。而且功能键的支持只是对WP和Android有效。功能键部分的类关系如下图所示。结构类似于触控处理。只是少了一个Delegate接口。

CCKeypadDelegate定义了两个功能键的处理接口。如果我们对处理功能键感兴趣,我们只需要继承CCKeypadDelegate,然后实现这两个接口即可。CCLayer已经继承了CCKeypadDelegate。我们在定义自己的层的时候,可以重载它即可。

我们还是从WIN32的窗口过程开始,来看功能键处理接口怎么被调用到。CCEGLView::WindowProc:

?
1
2
3
4
5
6
7
8
9
10
11
12
    case WM_KEYDOWN:
        if (wParam == VK_F1 || wParam == VK_F2)
        {
            CCDirector* pDirector = CCDirector::sharedDirector();
            if (GetKeyState(VK_LSHIFT) < 0 || GetKeyState(VK_RSHIFT) < 0 || GetKeyState(VK_SHIFT) < 0)
                pDirector->getKeypadDispatcher()->dispatchKeypadMSG(wParam == VK_F1 ? kTypeBackClicked : kTypeMenuClicked);
        }
        else if (wParam == VK_ESCAPE)
        {
            CCDirector::sharedDirector()->getKeypadDispatcher()->dispatchKeypadMSG(kTypeBackClicked);
        }
        break;

CCDirector内部有一个CCKeypadDispatcher类型的成员。它就是Cocos2Dx里面负责功能键处理的唯一对象。CCDirector有接口setKeypadDispatcher来替换它默认的功能键处理对象。但现在还没有被使用到。系统将收到的功能键消息(退出键、菜单键)交给CCKeypadDispatcher的dispatchKeypadMSG处理。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
bool CCKeypadDispatcher::dispatchKeypadMSG(ccKeypadMSGType nMsgType)
{
    CCKeypadHandler* pHandler = NULL;
    CCKeypadDelegate* pDelegate = NULL;
    m_bLocked = true;
    if (m_pDelegates->count() > 0)
    {
        CCObject* pObj = NULL;
        CCARRAY_FOREACH(m_pDelegates, pObj)
        {
            CC_BREAK_IF(!pObj);
            pHandler = (CCKeypadHandler*)pObj;
            pDelegate = pHandler->getDelegate();
            switch (nMsgType)
            {
            case kTypeBackClicked:
                pDelegate->keyBackClicked();
                break;
            case kTypeMenuClicked:
                pDelegate->keyMenuClicked();
                break;
            default:
                break;
            }
        }
    }
    m_bLocked = false;
    return true;
}

CCKeypadDispatcher::dispatchKeypadMSG内部遍历所有的Handler,然后取出Handler包裹的Delegate,再根据功能键的类型,分别调用Delegate的keyBackClicked或keyMenuClicked。m_bLocked标记是为了分发功能键消息的过程中,又有Handler被添加进来,或者删除掉。CCKeypadDispatcher::removeDelegate和CCKeypadDispatcher::addDelegate在删除和添加时,会检查m_bLocked标记。如果现在正在分发消息,添加和删除的Delegate都先暂存起来,分别放到m_pHandlersToRemove和m_pHandlersToAdd当中。当CCKeypadDispatcher::dispatchKeypadMSG完成消息分发以后,才真正从m_pDelegates中进行删除或添加。相应的代码由于篇幅的关系,没有贴在这。

m_pDelegates中的Delegate是CCKeypadDispatcher::forceAddDelegate添加进来的。但它并不直接暴露给外部。暴露给外部的添加功能键处理Handler的接口是CCKeypadDispatcher::addDelegate。CCLayer在自己的成员函数CCLayer::setKeypadEnabled中,将自己注册到CCKeypadDispatcher:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void CCLayer::setKeypadEnabled(bool enabled)
{
    if (enabled != m_bKeypadEnabled)
    {
        m_bKeypadEnabled = enabled;
        if (m_bRunning)
        {
            CCDirector* pDirector = CCDirector::sharedDirector();
            if (enabled)
            {
                pDirector->getKeypadDispatcher()->addDelegate(this);
            }
            else
            {
                pDirector->getKeypadDispatcher()->removeDelegate(this);
            }
        }
    }
}

跟触控处理一样,CCLayer默认也不是不开启功能键功能的。

现在我们总结一下怎么在游戏中使用功能键。

方式一:使用自己的Layer。然后重写keyBackClicked和keyMenuClicked函数,并且setKeypadEnabled(true)。

方式二:自己定义了一个处理类,让它继承自CCKeypadDelegate,并实现keyBackClicked和keyMenuClicked函数。然后调用CCDirector::sharedDirector()->getKeypadDispatcher()->addDelegate(pYourOwnHandler)。

Android上面,使用的本地方法是Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeKeyDown。有兴趣可以自己研究。

对于字符串的输入是一个比较复杂的话题。Cocos2Dx提供的功能不包括输入法,虽然名字叫做IME。Cocos2Dx提供的处理字符输入的功能包括:从系统接收输入的字符串,然后进行分发;一些常见的输入控件:CCTextFieldTTF和CCEditBox。Cocos2Dx的GUI部分还是比较弱,很多输入控件的支持都需要自己去做。我们先看下用户输入的流程:

第一步:使得输入控件得到焦点。在手机上一般就会弹出虚拟键盘。

第二步:用户输入内容。内容在控件上面得到及时地反应。

第三部:用户输入完毕。可以以回车结束,也可能是通过控件失去焦点。

第一步和第三步是用户操作的主动过程。分别对应到CCIMEDelegate::attachWithIME和CCIMEDelegate::detachWithIME。第二步,每输入一个字或者词,操作系统就会将输入的字符通过系统消息的方式告知应用。对应的接口是CCIMEDelegate::insertText和CCIMEDelegate::deleteBackward。

我们先看attachWithIME和detachWithIME。attachWithIME和detachWithIME在CCIMEDelegate中有默认实现。就是调用CCIMEDispatcher::attachDelegateWithIME和CCIMEDispatcher::detachDelegateWithIME。主要功能是在何时的时机调用CCIMEDelegate的四个函数:canAttachWithIME、didAttachWithIME、canDetachWithIME和didDetachWithIME。带有can前缀的用来测试现在是否可以attach或者detach IME。did前缀的函数用来通知Delegate现在已经做了attach或detach IME。这些回调函数给Delegate一些处理其它的事务的机会。

但什么怎么触发attachWithIME或者detachWithIME呢?答案是我们自己控制。举一个常见例子:用户点击一个控件。控件在Cocos2Dx可以做成一个CCLayer。因为CCLayer带有处理触控事件的能力。我们重载CCLayer::ccTouchEnd的函数,让它收到控件所在区域的触控消息后,就让操作系统弹出键盘允许进行输入,如果触控区域不在控件所在区域,隐藏键盘。

?
1
2
3
4
5
6
7
8
9
10
11
12
void TextFieldTTFDefaultTest::onClickTrackNode(bool bClicked)
{
    CCTextFieldTTF * pTextField = (CCTextFieldTTF*)m_pTrackNode;
    if (bClicked)
    {
        pTextField->attachWithIME();
    }
    else
    {
        pTextField->detachWithIME();
    }
}

这里用到了CCTextFieldTTF。他是Cocos2Dx提供的一个完整输入控件。虽然还有CCEditBox,但它是以扩展插件的身份存在的,并且实现方式也不一样。CCTextFieldTTF本身我们后面还会提到。

CCTextFieldTTF::attachWithIME和CCTextFieldTTF::detachWithIME内部会先调用CCIMEDelegate::attachWithIME和CCIMEDelegate::detachWithIME。因为CCIMEDelegate::attachWithIME和CCIMEDelegate::detachWithIME会告知控件当前是否允许做IME的attach和detach操作。如果CCIMEDelegate的两个函数返回fasle,那么当前控件的IME attach和detach都会被拒绝。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
bool CCTextFieldTTF::attachWithIME()
{
    bool bRet = CCIMEDelegate::attachWithIME();
    if (bRet)
    {
        CCEGLView * pGlView = CCDirector::sharedDirector()->getOpenGLView();
        if (pGlView)
        {
            pGlView->setIMEKeyboardState(true);
        }
    }
    return bRet;
}
bool CCTextFieldTTF::detachWithIME()
{
    bool bRet = CCIMEDelegate::detachWithIME();
    if (bRet)
    {
        CCEGLView * pGlView = CCDirector::sharedDirector()->getOpenGLView();
        if (pGlView)
        {
            pGlView->setIMEKeyboardState(false);
        }
    }
    return bRet;
}

CCTextFieldTTF::attachWithIME和CCTextFieldTTF::detachWithIME进一步让操作系统准备输入法。但这步依赖于Cocos2Dx所处的平台,不同的平台有不同的实现,Win32上面上面都不需要做,但Android上面就需要自己去通过InputMethodManager获取输入法服务。平台的差异都隐藏在CCEGLView::setIMEKeyboardState后面。有兴趣可以自己进一步深入跟进。

现在再来看CCIMEDelegate::insertText和CCIMEDelegate::deleteBackward。insertText代表我们现在正在进行输入;deleteBackward代表我们现在需要删除一个已经输入的字符。CCIMEDelegate自身并没有对这两个函数提供默认的实现。可用的实现是CCTextFieldTTF。可用在CCTextFieldTTF进行自己的扩展。

看到上面的图,可能会问CCIMEDelegate怎么向CCIMEDispatcher进行注册的呢?注释说明了一下:CCIMEDelegate的构造函数会调用CCIMEDispatcher的单例对象的addDelegate函数注册自己。addDelegate是保护成员,为了能够访问它,声明了CCIMEDelegate为CCIMEDispatcher的友元。析构函数会做类似的取消注册操作。CCTextFieldTTF继承CCIMEDelegate,它构造或析构的时候,会分别调用CCIMEDelegate的构造和析构函数,CCTextFieldTTF也就自动向CCIMEDispatcher注册和取消注册。

CCTextFieldTTF的输入字符显示实际上是靠CCLabelTTFT完成的。CCLabelTTFT还有一个CCTextFieldDelegate成员,它将一些控制功能封装在一个Delegate当中。CCTextFieldDelegate所有成员都返回bool值,决定相应的操作是否允许被执行。

现在走一些按键消息的分发流程。CCEGLView::WindowProc在收到键盘消息后,需要进行一下判断:

  • 删除键(VK_BACK),调用CCIMEDispatcher::dispatchDeleteBackward,进一步调用CCIMEDelegate::deleteBackward(子类实现)。

  • 回车键(VK_RETURN),调用CCIMEDispatcher::dispatchInsertText,进一步调用CCIMEDelegate::insertText(子类实现)。但并没有直接送键码,二是送了'\n'。

  • ASCII字符,同上。

  • 其他可输入字符。在发送前需要做转码,宽字串转UTF8。

CCIMEDelegate的子类CCTextFieldTTF收到按键输入后,将其存放在一个字符串中。具体的实现,比较易懂,我们就不再累述。

另外,Android上面,字符输入相关的代码位于cocos2dx\platform\android\java\src\org\cocos2dx\lib\Cocos2dxTextInputWraper.java。

Cocos2Dx之处理键盘输入-欧阳左至相关推荐

  1. Cocos2Dx之处理键盘输入

    键盘输入在Cocos2Dx分为两个部分.第一部分是一些功能键的处理:后退键和菜单键.第二部分是处理字符输入.功能键相关的代码位于cocos2dx\keypad_dispatcher.字符输入的代码位于 ...

  2. Cocos2Dx之触控处理-欧阳左至

    在上个章节中,我们已经看到了Win 32的消息泵驱动CCDirector在每个帧间隔时间到期后,调用mainLoop.但是对于触控事件,它同样是操作系统上报给应用的事件,我们没有看到它的踪迹.事实上, ...

  3. Cocos2Dx之游戏启动过程-欧阳左至

    Cocos2Dx应用由导演(CCDirector).场景(CCSprite).层(CCLayer)和精灵(CCSprite)构成.导演负责管理整个游戏.映射到操作系统,每个操作系统需要的应用入口都不一 ...

  4. 公民身份号码是一种由18位数字组成的特征组合码,其排列顺序从左至右依次为:6位数字地址码、8位数字出生日期码,3位数字顺序码和1位数字校验码(校验码若为10则用字符X来表示)。编写程序从键盘输入一个

    #include <stdio.h> int main() {int add,year,month,day;/*定义地址码,年月日*/int shunxuma;/*定义顺序码*/char ...

  5. 模拟键盘输入的keycode

    今天有个弹出框的element一直找不到,换了种方法关掉,就是用模拟键盘输入,找到以下对应的keycode,先记录下来,说不定以后用得上/*--------------------edit by 20 ...

  6. 汇编语言随笔(15)- 对键盘输入进行处理(int 9 和 int 16h 中断例程)

    键盘输入 1,前面讲过, int 9 中断例程负责对键盘输入进行处理,具体为:从 60h 端口读出扫描码,并将其转化为相应的ASCII码或状态信息,一起存储到内存的指定空间(键盘缓冲区或状态字节)中. ...

  7. 汇编语言笔记17-使用BIOS进行键盘输入和磁盘读写

    转载必须注明出处,违者必究.http://www.cnblogs.com/dennisOne ☞使用BIOS进行键盘输入和读取键盘缓冲区 复习键盘缓冲区和状态字节 (1). BIOS键盘缓冲区是系统启 ...

  8. python获取键盘输入能不能不按回车键_Github获8300星!用Python开发的一个命令行的网易云音乐...

    最近在逛Github发现了一个非常有趣的库musicbox,是用纯Python打造的,收获了8300颗星.Python语言简单易学,好玩有趣,身边越来越多的小伙伴都开始学习Python.她的魅力非常大 ...

  9. python如何模拟键盘输入_python 模拟键盘输入

    python 模拟键盘输入 一.PyUserInput安装 python3.5的PyMouse和PyKeyboard模块都集成到了PyUserInput模块中.在python3.5中,直接安装PyUs ...

最新文章

  1. PyTorch 深度剖析:如何保存和加载PyTorch模型?
  2. 中达变频器参数_台达变频器:满足未来驱动需求
  3. 最简单的分形图像生成算法
  4. VTK:Shaders之ColorByNormal
  5. Oracle具有开放性吗,Oracle数据库的特点与工作原理
  6. python保存和加载数组
  7. 数学要考多少分?才能考上985、211,很多高中生看完后沉默了......
  8. va_start、va_end、va_arg 实现可变长参数
  9. shell中的大括号和小括号
  10. Java中关于参数的讲解_解读Java参数传递
  11. 赛锐信息:SAP进化论
  12. php用高德地图api坐标返回市_php 使用高德地图(一) 画多边形及编辑 获取坐标 (简单总结)...
  13. wps 安装字体手册
  14. win10 +cude9.2+相匹配的cudnn+相匹配的tensorflow+ssd学习之路(问题百出1)
  15. 降低网站跳出率9种方法
  16. Excel饼图中既显示百分比
  17. 视频+公众号是未来微信生态的一种新的运营方式!
  18. 【爬虫】一种基于网页相似度去重的爬虫系统
  19. 渗透杂记2013-07-31
  20. 安信可ESP8266模块实现对接天猫精灵IOT平台控制单路插座的教程

热门文章

  1. PCA9554芯片测试
  2. 逆天了!3D网页展示居然可以这么屌!
  3. 服务器回环地址修改,服务器回环地址修改
  4. mysql 数据库大小与内存关系_【MYSQL】CPU资源和可用内存大小对数据库性能的影响...
  5. 【记录】VUE3 + VITE + TS 配置跨域
  6. 2023-2029年中国变频空调行业市场行情监测及发展趋势分析报告
  7. html_css_四分之一圆
  8. Win10迅雷下载速度为0的解决方法
  9. 万能刷机王HD2刷WP7详细教程
  10. pageadmin去掉xxx