键盘输入在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. c语言从键盘输入一个百分制成绩score,C语言 基础练习40题

    一.题目 1.输入2个整数,求两数的平方和并输出. 2. 输入一个圆半径(r)当r>=0时,计算并输出圆的面积和周长,否则,输出提示信息. 3.函数y=f(x)可表示为: 4.编写一个程序,从4 ...

  3. ACMNO.44 C语言-平均分 有N个学生,每个学生的数据包括学号、姓名、3门课的成绩,从键盘输入N个学生的数据,要求打印出3门课的总平均成绩,以及最高分的学生的数据(包括学号、姓名、3门课成绩)

    题目描述 有N个学生,每个学生的数据包括学号.姓名.3门课的成绩, 从键盘输入N个学生的数据, 要求打印出3门课的总平均成绩,以及最高分的学生的数据 (包括学号.姓名.3门课成绩) 输入 学生数量N占 ...

  4. ACMNO.22 C语言-公约公倍2 写两个函数,分别求两个整数的最大公约数和最小公倍数,用主函数调用这两个函数,并输出结果两个整数由键盘输入。 输入 两个数 输出 最大公约数 最小公倍数

    题目描述 写两个函数,分别求两个整数的最大公约数和最小公倍数, 用主函数调用这两个函数,并输出结果两个整数由键盘输入. 输入 两个数 输出 最大公约数 最小公倍数 样例输入 6 15 样例输出 3 3 ...

  5. ACMNO.9求Sn=a+aa+aaa+…+aa…aaa(有n个a)之值,其中a是一个数字。 例如:2+22+222+2222+22222(n=5),n由键盘输入。 输入 n 输出 a=2 时

    题目描述 求Sn=a+aa+aaa+-+aa-aaa(有n个a)之值,其中a是一个数字. 例如:2+22+222+2222+22222(n=5),n由键盘输入. 输入 n 输出 a=2 时的Sn 样例 ...

  6. ACMNO.3 有三个整数a b c,由键盘输入,输出其中的最大的数。 输入 一行数组,分别为a b c 输出 a b c其中最大的数 样例输入 10 20 30 样例输出 30

    基于平台Dev-C++ 5.11 题目描述 有三个整数a b c,由键盘输入,输出其中的最大的数. 输入 一行数组,分别为a b c 输出 a b c其中最大的数 样例输入 10 20 30 样例输出 ...

  7. Keypress - 捕获键盘输入的JavaScript库

    Keypress 是一个强大的 JavaScript 库,用于捕获键盘输入.这是一个有非常特殊的功能的输入捕获库,它是很容易掌握和使用,并且不依赖第三方库.在网站开发中,经常会碰到需要处理键盘输入的场 ...

  8. 有6个候选人,100个选民,每个选民选择一个侯选人投票;从键盘输入每个选民选择的候选人名,统计并输出6个候选人的票数。java,c++实现

    题目: 有6个候选人,100个选民,每个选民选择一个侯选人投票;从键盘输入每个选民选择的候选人名,统计并输出这6个候选人的票数.(10分) 代码部分: #include<iostream> ...

  9. 从键盘输入10个互不相同的整数,找出其中最小的元素将其与数组中的第一个元素进行交换。

    题目: /* 从键盘输入10个互不相同的整数,找出其中最小的元素将其与数组中的第一个元素进行交换. */ 代码: c++做的. #include<iostream> using names ...

最新文章

  1. 【nacos】本机电脑、服务器上如何安装配置nacos
  2. 使用xshell5 从CentOS主机download资料
  3. Java 线程池的介绍以及工作原理
  4. java mvc 获取session_Spring MVC----获取session/request
  5. Python 03 学习内容整理
  6. 数学图形(1.33) 棕子曲线
  7. STM32示波器 信号发生器
  8. python函数语法学习
  9. (转)泊松分布和指数分布:10分钟教程
  10. Mybatis-Spring扫描路径有重叠导致Invalid bound statement(not found)问题
  11. 常见网络协议端口号整理
  12. 两小时学会ajax,全程无废话,直接上代码,追求速度的来
  13. python三元一次方程代码_求三元一次方程计算器代码
  14. python暴力破解WiFi
  15. [原创]MASM32新手指南
  16. linux中如何查看本机ip,Linux中如何查看本机IP地址呢?
  17. 喜欢Photoshop的来看看啊
  18. iOS开发之企业开发者账号打包发布 App 到服务器上的流程
  19. 用阳光心态去实现快乐工作
  20. 【工具分享】如何识别手机里偷数据的那些软件(适用于安卓平台)

热门文章

  1. STM32F103自定义的printf函数的实现
  2. 图像算法九:【图像特征提取】特征降维、PCA人脸特征抽取、局部二进制
  3. pytorch出现RuntimeError: CUDA out of memory.
  4. fstream与 C 风格(例如fread 和 fwrite )两种读写文件方法的效率比较
  5. for 2018.12.3
  6. NB-IoT 的“前世今生”
  7. CSS文件的三种引入方式
  8. 后台取值页面显示——Vue.js
  9. 使用python脚本将XML 电子表格转变为Excel
  10. 2013 Multi-University Training Contest 4