博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
UINode扩展
阅读量:6295 次
发布时间:2019-06-22

本文共 9186 字,大约阅读时间需要 30 分钟。

1、现有的cocos2d-x的ui和点击事件分派机制是无法适应mmo的ui逻辑的。 mmo的ui元素很多,一两百个界面是很正常的。 规则既单一又复杂,这些界面肯定是由简单的对话框组成的,不会有什么amazing的东西,但是这些对话框之间的遮挡、层级关系又是复杂的。如果把这些东西丢给写ui或者写逻辑的程序员去操心,那就等着无穷无尽的bug吧。而且即便维护好现有的代码,这些代码结构也是脆弱的,每添加一个新功能都有可能对以前的逻辑造成影响。

2、一套完善的ui可以不是cocos2d-x的核心代码,但是一定要属于cocos2d-x引擎的一部分。 不要想着cocos2d-x只是一个渲染引擎,其他的东西都可以外部扩展。  这种思路就像是cocos2d和ogre的思路一样,并不利于引擎的发展。 什么东西都是灵活可选的,反而不如提供一套默认好用的。  就像是java和c#提供的标准库对比c++的标准库。 一个提供好你需要的基础套件,一个只提供最核心的容器语法,最后哪个容易学?哪个发展的好?   很多时候灵活可选对终端用户而言并不一定是一件好事。

3、最早cocos2d提供的控件只有CCMenu,其他控件需要的话就用UIKit。 这个勉强可以接受。因为那个时候ios平台独立开发者做的都是愤怒的小鸟,水果忍者什么的,这些ui元素几乎为0,一个CCMenu就可以走天下了。 如果需要其他控件的时候,UIKIt也可以和cocos2d引擎完美的结合起来,用起来还算方便。

      但是转移到cocos2d-x就是另外一个天地了,因为用cocos2d的思路结合本地控件和游戏引擎在android平台上几乎是不可能实现的,那绝对是一个噩梦。 于是一套基于游戏引擎渲染的ui控件库便孕育而生。

4、但是这套新的ui控件库依然只是一个控件库,不是一个ui库。它依然无法承载一个mmo的需求。 mmo的开发者依然需要对其进行再封装和再开发。这里要表达两个观点,一是如果cocos2d-x提供一套方便好用的ui库,这些再开发是可以避免的,例如CEGUI。  二是,这样的一个好用方便的ui库并不是说一定限定于mmo使用,我们做水果忍者或者斗地主依然可以使用这套ui,依然会享受到其提供的便利。

5、现有的点击事件分发处理天然就不适应于复杂的ui框架。所有的控件平铺给CCTouchDispatcher处理,按照优先级处理点击事件。这个后期维护起代码来无比蛋疼,并且很有可能出现这些恶心的情况,窗体已经隐藏了,但是CCControlButton依然可以响应点击事件(注意Button本身依然是Visible的,只不过他的父窗体隐藏了); 看到的是一个界面,但是点击到的是另外一个界面。

6、原本另写一套ui库是更加漂亮的方法,但是一方面现有游戏已经发布,代码中大量CCNode作为节点容器存在的情况,不可能对其大动手术;另一方面再写一个ui库消耗的时间很多,短时间内不可能实现。 所以选择直接改写CCNode的代码,重写事件分发机制。  但是我没有对其进行更加完整的思考,所以这种修改适应于我们的游戏,但是不一定适应于所有的游戏。  当然,适应于大多数游戏并不是一件困难的事情,只不过我懒得做了而已。

核心代码:

[cpp]
  1. enum
  2.     kEventModeNone = 1,                  
  3.     kEventModeNormal,               // 正常电击处理,大多数控件都是这种 
  4.     kEventModeIgnoreEvent,          // 不响应任何点击事件 
  5.     kEventModeBackground,           // 大多数node和layer是这种,本身不响应点击,但是会传递事件给子控件,如果没有子控件响应则穿透 
  6.     kEventModePassDrag,             // 一些子node会有这个属性,将move事件传递给父node 
  7.     kEventModeModel,                // 模态对话框 
  8. }; 
enum {	kEventModeNone = 1,						kEventModeNormal,				// 正常电击处理,大多数控件都是这种	kEventModeIgnoreEvent,			// 不响应任何点击事件	kEventModeBackground,			// 大多数node和layer是这种,本身不响应点击,但是会传递事件给子控件,如果没有子控件响应则穿透	kEventModePassDrag,				// 一些子node会有这个属性,将move事件传递给父node	kEventModeModel,				// 模态对话框};

 

[cpp]
  1. CCNode* CCNode::getTouchedChildTarget(CCTouch* pTouch, uint eventType, bool includeIgnore) 
  2.     if (!pTouch) { 
  3.         return NULL; 
  4.     } 
  5.  
  6.     bool ret = false
  7.     if (!m_pChildren || m_pChildren->count() == 0) { 
  8.         return NULL; 
  9.     } 
  10.  
  11.     CCPoint touchLocation = pTouch->getLocation(); 
  12.     for (int i = m_pChildren->count() - 1; i >= 0; --i) { 
  13.         CCNode* pNode = dynamic_cast<CCNode*>(m_pChildren->objectAtIndex(i)); 
  14.         if (!pNode || !pNode->isVisible()) { 
  15.             continue
  16.         } 
  17.  
  18.         if (pNode->testEventMode(kEventModeNone)) { 
  19.             continue
  20.         } 
  21.  
  22.         if (!includeIgnore && pNode->testEventMode(kEventModeIgnoreEvent)) { 
  23.             continue
  24.         } 
  25.  
  26.         CCNode* pChildNode = pNode->getTouchedChildTarget(pTouch, eventType); 
  27.         if (pChildNode) { 
  28.             return pChildNode; 
  29.         } else
  30.             CCSize size = pNode->getContentSize(); 
  31.             if (size.width <= 0 || size.height <= 0) { 
  32.                 continue
  33.             } 
  34.  
  35. //          if (typeid(*this) == typeid(CCNode) || typeid(*this) == (CCSprite)) {
     
  36. //          } 
  37.  
  38.             CCPoint pt = pNode->convertToWorldSpace(CCPoint(0, 0)); 
  39.             CCRect bBox(pt.x, pt.y, size.width, size.height); 
  40.  
  41.             if (pNode->testEventMode(kEventModeModel) || bBox.containsPoint(touchLocation)) { 
  42.                 if (!pNode->testEventMode(kEventModeBackground)) { 
  43.                     return pNode; 
  44.                 } 
  45.             } 
  46.         } 
  47.     } 
  48.  
  49.     return NULL; 
  50.  
  51. bool CCNode::ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent) 
  52.     returntrue
  53.  
  54. void CCNode::ccTouchMoved(CCTouch *pTouch, CCEvent *pEvent) 
  55.  
  56. void CCNode::ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent) 
  57.  
  58. void CCNode::ccTouchCancelled(CCTouch *pTouch, CCEvent *pEvent) 
  59.  
  60. void CCNode::setEventMode(int mode) 
  61.     m_eventMode = (1 << mode); 
  62.  
  63. int CCNode::testEventMode(int mode) const 
  64.     return(m_eventMode & (1 << mode)) != 0; 
CCNode* CCNode::getTouchedChildTarget(CCTouch* pTouch, uint eventType, bool includeIgnore){	if (!pTouch) {		return NULL;	}	bool ret = false;	if (!m_pChildren || m_pChildren->count() == 0) {		return NULL;	}	CCPoint touchLocation = pTouch->getLocation();	for (int i = m_pChildren->count() - 1; i >= 0; --i) {		CCNode* pNode = dynamic_cast
(m_pChildren->objectAtIndex(i)); if (!pNode || !pNode->isVisible()) { continue; } if (pNode->testEventMode(kEventModeNone)) { continue; } if (!includeIgnore && pNode->testEventMode(kEventModeIgnoreEvent)) { continue; } CCNode* pChildNode = pNode->getTouchedChildTarget(pTouch, eventType); if (pChildNode) { return pChildNode; } else { CCSize size = pNode->getContentSize(); if (size.width <= 0 || size.height <= 0) { continue; }// if (typeid(*this) == typeid(CCNode) || typeid(*this) == (CCSprite)) {// } CCPoint pt = pNode->convertToWorldSpace(CCPoint(0, 0)); CCRect bBox(pt.x, pt.y, size.width, size.height); if (pNode->testEventMode(kEventModeModel) || bBox.containsPoint(touchLocation)) { if (!pNode->testEventMode(kEventModeBackground)) { return pNode; } } } } return NULL;}bool CCNode::ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent){ return true;}void CCNode::ccTouchMoved(CCTouch *pTouch, CCEvent *pEvent){}void CCNode::ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent){}void CCNode::ccTouchCancelled(CCTouch *pTouch, CCEvent *pEvent){}void CCNode::setEventMode(int mode){ m_eventMode = (1 << mode);}int CCNode::testEventMode(int mode) const{ return(m_eventMode & (1 << mode)) != 0;}

 

 

[cpp]
  1. void CCTouchDispatcher::touchesEnded(CCSet *touches, CCEvent *pEvent) 
  2.     if (!m_bDispatchEvents) { 
  3.         return
  4.     } 
  5.  
  6.     CCTouch *pTouch; 
  7.     CCSetIterator setIter; 
  8.     for (setIter = touches->begin(); setIter != touches->end(); ++setIter) 
  9.     { 
  10.         pTouch = (CCTouch *)(*setIter); 
  11.         if (!pTouch) { 
  12.             continue
  13.         } 
  14.  
  15.         if (m_pFocusNode) { 
  16.             m_pFocusNode->ccTouchEnded(pTouch, pEvent); 
  17.         } 
  18.     } 
  19.  
  20.     setFocusNode(NULL); 
  21.  
  22. void CCTouchDispatcher::touchesCancelled(CCSet *touches, CCEvent *pEvent) 
  23.     if (m_bDispatchEvents) 
  24.     { 
  25.         CCTouch *pTouch; 
  26.         CCSetIterator setIter; 
  27.         for (setIter = touches->begin(); setIter != touches->end(); ++setIter) 
  28.         { 
  29.             pTouch = (CCTouch *)(*setIter); 
  30.             if (!pTouch) { 
  31.                 continue
  32.             } 
  33.             if (m_pFocusNode) { 
  34.                 m_pFocusNode->ccTouchCancelled(pTouch, pEvent); 
  35.             } elseif (m_pSceneRootNode) { 
  36.                 m_pSceneRootNode->ccTouchCancelled(pTouch, pEvent); 
  37.             } 
  38.         } 
  39.     } 
  40.  
  41.     setFocusNode(NULL); 
  42.  
  43.  
  44.  
  45.  
  46. void CCTouchDispatcher::setFocusNode(CCNode* node) 
  47.     m_pFocusNode = node; 
  48.  
  49. CCNode* CCTouchDispatcher::getFocusNode() const 
  50.     return m_pFocusNode; 
  51.  
  52.  
  53. void CCTouchDispatcher::setUIRootNode(CCNode* node) 
  54.     if (!node) { 
  55.         return
  56.     } 
  57.     m_pUIRootNode = node; 
  58.  
  59. void CCTouchDispatcher::setSceneNode(CCNode* node) 
  60.     if (!node) { 
  61.         return
  62.     } 
  63.  
  64.     m_pSceneRootNode = node; 
void CCTouchDispatcher::touchesEnded(CCSet *touches, CCEvent *pEvent){    if (!m_bDispatchEvents) {		return;	}	CCTouch *pTouch;	CCSetIterator setIter;	for (setIter = touches->begin(); setIter != touches->end(); ++setIter)	{		pTouch = (CCTouch *)(*setIter);		if (!pTouch) {			continue;		}		if (m_pFocusNode) {			m_pFocusNode->ccTouchEnded(pTouch, pEvent);		}	}	setFocusNode(NULL);}void CCTouchDispatcher::touchesCancelled(CCSet *touches, CCEvent *pEvent){    if (m_bDispatchEvents)    {		CCTouch *pTouch;		CCSetIterator setIter;		for (setIter = touches->begin(); setIter != touches->end(); ++setIter)		{			pTouch = (CCTouch *)(*setIter);			if (!pTouch) {				continue;			}			if (m_pFocusNode) {				m_pFocusNode->ccTouchCancelled(pTouch, pEvent);			} else if (m_pSceneRootNode) {				m_pSceneRootNode->ccTouchCancelled(pTouch, pEvent);			}		}    }	setFocusNode(NULL);}void CCTouchDispatcher::setFocusNode(CCNode* node){	m_pFocusNode = node;}CCNode* CCTouchDispatcher::getFocusNode() const{	return m_pFocusNode;}void CCTouchDispatcher::setUIRootNode(CCNode* node){	if (!node) {		return;	}	m_pUIRootNode = node;}void CCTouchDispatcher::setSceneNode(CCNode* node){	if (!node) {		return;	}	m_pSceneRootNode = node;}

 

[cpp]
  1. void CCControl::sendActionsForControlEvents(CCControlEvent controlEvents) 
  2.     retain(); 
  3.     // For each control events 
  4.     for (int i = 0; i < kControlEventTotalNumber; i++) 
  5.     { 
  6.         // If the given controlEvents bitmask contains the curent event 
  7.         if ((controlEvents & (1 << i))) 
  8.         { 
  9.             // Call invocations 
  10.             // <CCInvocation*> 
  11.             CCArray* invocationList = this->dispatchListforControlEvent(1<<i); 
  12.             CCObject* pObj = NULL; 
  13.             CCARRAY_FOREACH(invocationList, pObj) 
  14.             { 
  15.                 CCInvocation* invocation = (CCInvocation*)pObj; 
  16.                 invocation->invoke(this); 
  17.             } 
  18.         } 
  19.     } 
  20.     release(); 
void CCControl::sendActionsForControlEvents(CCControlEvent controlEvents){	retain();    // For each control events    for (int i = 0; i < kControlEventTotalNumber; i++)    {        // If the given controlEvents bitmask contains the curent event        if ((controlEvents & (1 << i)))        {            // Call invocations            // 
CCArray* invocationList = this->dispatchListforControlEvent(1<
invoke(this); } } } release();}

 

思路和简单的说明:

1、在每个场景设置一个UIRoot,来进行统一的事件分发。 事件分发是由父节点传递给子节点,这样的层次结构。如果父节点是隐藏的或者不可点击的,那么子节点也不会接收到点击事件。 

2、通过一个EventMode的属性来标志当前节点是否可以点击。比如大多数CCNode和CCLayer就是Background属性,这个属性的节点本身是不可点击的,但是它会把点击事件分派给他的子节点。如果子节点都没有响应,那么就传递给下一个界面节点。   而大多数控件都是Normal属性,它可以正常响应点击事件

3、查找到一个合适目标的节点,然后给这个目标节点发送点击事件,而不是遍历当前节点,响应点击事件,根据其返回值决定是否继续分发。   这样做的目的是防止响应事件的时候销毁当前窗体,造成一些恶心的内存问题(在遍历过程中修改容器的元素代码怎么写都不会非常的安全和稳定,还是采用现在的做法保险点)。

4、原本事件处理有个陷阱,那就是CCTouchDispatcher的Handler集合会给控件引用计数+1,所以事件分发的时候可以安心的remove掉,因为控件本身不会被delete。但是取消掉这个机制后,就要小心和多测试下。比如CCControl的事件分发代码就需要先retain然后release来防止响应事件时销毁控件造成崩溃。

5、事件处理是万里长征第一步,这一步完成后,再添加  1、xml配置自动创建界面的功能。(这个完成后就像模像样了)  2、添加一个模式可以在游戏中拖动ui控件的位置大小并且保存(游戏就是ui编辑器了)   3、一套lua和代码结合的方式,把大多数ui逻辑移动到lua(一切都是为了提高开发效率,所有代码都用lua写并不是一个好主意,但是部分体力活的代码挪到脚本里面确实会清爽不少)

转载地址:http://ckmta.baihongyu.com/

你可能感兴趣的文章
继承自ActionBarActivity的activity的activity theme问题
查看>>
设计模式01:简单工厂模式
查看>>
项目经理笔记一
查看>>
Hibernate一对一外键双向关联
查看>>
mac pro 入手,php环境配置总结
查看>>
MyBatis-Plus | 最简单的查询操作教程(Lambda)
查看>>
rpmfusion 的国内大学 NEU 源配置
查看>>
spring jpa 配置详解
查看>>
IOE,为什么去IOE?
查看>>
Storm中的Worker
查看>>
dangdang.ddframe.job中页面修改表达式后进行检查
查看>>
Web基础架构:负载均衡和LVS
查看>>
Linux下c/c++相对路径动态库的生成与使用
查看>>
SHELL实现跳板机,只允许用户执行少量允许的命令
查看>>
SpringBoot 整合Redis
查看>>
2014上半年大片早知道
查看>>
Android 6.0指纹识别App开发案例
查看>>
正文提取算法
查看>>
轻松学PHP
查看>>
Linux中的网络监控命令
查看>>