cocos2d-x-2.2.3-CCLayer源码学习(三)

| 分类 cocos2d-x  | 标签 CCLayerColor  CCLayer  KeypadTest  CCKeypadDelegate 

本文主要讲解的是关于CCLayer在TestCPP中的例子,关于CCLayer源码解析,参见cocos2d-x-2.2.3-CCLayer源码学习(一)

下面我们还是先学习学习KeypadTest这个例子

  1. //重点方法其实就只有一句setKeypadEnabled(true);还是先看看这方法怎么实现的把,跳转到CCLayer中
  2. void CCLayer::setKeypadEnabled(bool enabled)
  3. {
  4. if (enabled != m_bKeypadEnabled)//先判断当前CCLayer的m_bKeypadEnabled是否一样,默认m_bKeypadEnabled为false
  5. {
  6. m_bKeypadEnabled = enabled;
  7. if (m_bRunning)
  8. {
  9. CCDirector* pDirector = CCDirector::sharedDirector();
  10. if (enabled)
  11. {
  12. pDirector->getKeypadDispatcher()->addDelegate(this);//把CCKeypadDelegate加入到CCKeypadDispatcher中统一管理。由于CCLayer实现了CCKeypadDelegate接口
  13. }
  14. else
  15. {
  16. pDirector->getKeypadDispatcher()->removeDelegate(this);//把CCKeypadDelegate从CCKeypadDispatcher中移除 }
  17. }
  18. }
  19. }

其实看到这个我们就应该想到CCTouchDelegete,是的,keypad和touch处理流程是一样的,先把delegate统一放到Handler,再把handler加入到dispach中的CCArray中,最后统一在一个方法中处理消息,再把接受到的消息分发出去。

下面我们还是看看CCKeypadDelegate这个接口:

  1. class CC_DLL CCKeypadDelegate
  2. {
  3. public:
  4. virtual void keyBackClicked() {}//点击返回键时候触发
  5. virtual void keyMenuClicked() {};//点击menu键的时候触发
  6. };
  7. KeypadTest例子中,只要用户点击返回键或者menu键,都会回调相应的方法。

下面我们接着看CCKeypadDispatcher,看看是怎么统一管理CCKeypadDelegate的

  1. 有个枚举类ccKeypadMSGType,来区分点击事件
  2. typedef enum {
  3. // the back key clicked msg
  4. kTypeBackClicked = 1,
  5. kTypeMenuClicked,
  6. } ccKeypadMSGType;
  7. CCArray* m_pDelegates;//保存delegates的指针
  8. bool m_bLocked;//是否被锁住,默认false
  9. bool m_bToAdd;//是否添加,默认false
  10. bool m_bToRemove;//是否移除,默认false
  11. struct _ccCArray *m_pHandlersToAdd;//即将要加入到m_pDelegates的delegates,事先加入到m_pHandlersToAdd,当m_bLocked为true的时候
  12. struct _ccCArray *m_pHandlersToRemove;//即将要移除到m_pDelegates的delegates,事先加入到m_pHandlersToRemove指针中,当m_bLocked为true的时候
  13. typedef struct _ccCArray {
  14. unsigned int num, max;//num表示arr指向的数组所含有的成员个数,该数组的空间
  15. void** arr;//指向数组的指针
  16. } ccCArray;//ccCArray是一个结构体,后面内容会详细介绍他。
  17. 下面看重点方法讲解
  18. void CCKeypadDispatcher::addDelegate(CCKeypadDelegate* pDelegate)//在CCLayer中直接调用方法,站在最前线的战士
  19. {
  20. if (!pDelegate)//如果为null,返回
  21. {
  22. return;
  23. }
  24. if (! m_bLocked)//如果没有被锁住,则直接添加到m_pDelegates中,在dispatchKeypadMSG方法中有把锁判断是否锁定
  25. {
  26. forceAddDelegate(pDelegate);//该方法才是真正的添加CCKeypadDelegate的家伙
  27. }
  28. else
  29. {
  30. ccCArrayAppendValue(m_pHandlersToAdd, pDelegate);//把pDelegate添加到m_pHandlersToAdd中,该方法是ccCArray.h中的方法
  31. m_bToAdd = true;//表明有东西添加到m_pHandlersToAdd了,标志作用
  32. }
  33. }
  34. 还是先来看ccCArrayAppendValue方法把。
  35. void ccCArrayAppendValue(ccCArray *arr, void* value)//有两个参数,m_pHandlersToAdd, pDelegate
  36. {
  37. arr->arr[arr->num] = value;//把pDelegate指针保存到数组中,通过_ccCArray结构体的void** arr来索引
  38. arr->num++;//数组长度+1
  39. // double the capacity for the next append action
  40. // if the num >= max
  41. if (arr->num >= arr->max)//如果数组长度大于等于最大容量,那就扩容啊
  42. {
  43. ccCArrayDoubleCapacity(arr);
  44. }
  45. }
  46. 下面欣赏forceAddDelegate方法
  47. void CCKeypadDispatcher::forceAddDelegate(CCKeypadDelegate* pDelegate)
  48. {
  49. CCKeypadHandler* pHandler = CCKeypadHandler::handlerWithDelegate(pDelegate);//pDelegate竟然是存放在CCKeypadHandler里面的
  50. if (pHandler)
  51. {
  52. m_pDelegates->addObject(pHandler);//m_pDelegates添加的是CCKeypadHandler,而不是pDelegate,为啥需要这样做呢???
  53. }
  54. }
  55. 下面看看CCKeypadHandler的源码,看了半天发觉其实CCKeypadHandler没干什么事情,纯粹是多余的,感觉其实是作者是为了配套CCTouchHandler才这样搞的
  56. CCKeypadHandler* CCKeypadHandler::handlerWithDelegate(CCKeypadDelegate *pDelegate)
  57. {
  58. CCKeypadHandler* pHandler = new CCKeypadHandler;
  59. if (pHandler)
  60. {
  61. if (pHandler->initWithDelegate(pDelegate))//CCKeypadHandler持有一个CCKeypadDelegate,赋值给m_pDelegate
  62. {
  63. pHandler->autorelease();
  64. }
  65. else
  66. {
  67. CC_SAFE_RELEASE_NULL(pHandler);//宏定义pHandler设为null
  68. #define CC_SAFE_RELEASE_NULL(p) do { if(p) { (p)->release(); (p) = 0; } } while(0)
  69. }
  70. }
  71. return pHandler;
  72. }
  73. 下面还是看看CCKeypadDispatcher怎么分发消息的把。
  74. bool CCKeypadDispatcher::dispatchKeypadMSG(ccKeypadMSGType nMsgType)
  75. {
  76. CCKeypadHandler* pHandler = NULL;
  77. CCKeypadDelegate* pDelegate = NULL;
  78. m_bLocked = true;//当分发消息的时候,把开关关了
  79. if (m_pDelegates->count() > 0)//这个数组>0,说明存放了CCKeypadHandler
  80. {
  81. CCObject* pObj = NULL;
  82. CCARRAY_FOREACH(m_pDelegates, pObj)//循环迭代
  83. {
  84. CC_BREAK_IF(!pObj);
  85. pHandler = (CCKeypadHandler*)pObj;
  86. pDelegate = pHandler->getDelegate();//看到没pHandler就是多余的
  87. switch (nMsgType)//根据消息类型,分发
  88. {
  89. case kTypeBackClicked:
  90. pDelegate->keyBackClicked();//回调keyBackClicked()方法
  91. break;
  92. case kTypeMenuClicked:
  93. pDelegate->keyMenuClicked();//回调kTypeMenuClicked()方法
  94. break;
  95. default:
  96. break;
  97. }
  98. }
  99. }
  100. m_bLocked = false;//消息分发完毕,解锁了
  101. if (m_bToRemove)//判断是否有东西需要移除,因为锁被锁住,临时存放在m_pHandlersToRemove中
  102. {
  103. m_bToRemove = false;//立马设置为false
  104. for (unsigned int i = 0; i < m_pHandlersToRemove->num; ++i)//
  105. {
  106. forceRemoveDelegate((CCKeypadDelegate*)m_pHandlersToRemove->arr[i]);//把存放在数组中的CCKeypadDelegate*取出来,m_pHandlersToRemove->arr[i]移除
  107. }
  108. ccCArrayRemoveAllValues(m_pHandlersToRemove);//清空m_pHandlersToRemove,仅仅只需要arr->num = 0;就可以
  109. }
  110. if (m_bToAdd))//判断是否有东西需要添加,因为锁被锁住,临时存放在m_pHandlersToAdd中
  111. {
  112. m_bToAdd = false;//立马设置为false
  113. for (unsigned int i = 0; i < m_pHandlersToAdd->num; ++i)
  114. {
  115. forceAddDelegate((CCKeypadDelegate*)m_pHandlersToAdd->arr[i]);//把存放在数组中的CCKeypadDelegate*取出来,m_pHandlersToAdd->arr[i]添加
  116. }
  117. ccCArrayRemoveAllValues(m_pHandlersToAdd);//清空m_pHandlersToAdd,仅仅只需要arr->num = 0;就可以
  118. }
  119. return true;
  120. }
  121. 好了CCKeypadDelegate消息分发大体流程讲完了,还有一个疑问,随把消息传递给dispatchKeypadMSG啊???据说只有android才有实体键,所以android上是这么传递的,在TouchesJni.cpp
  122. #define KEYCODE_BACK 0x04
  123. #define KEYCODE_MENU 0x52
  124. JNIEXPORT jboolean JNICALL Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeKeyDown(JNIEnv * env, jobject thiz, jint keyCode) {
  125. CCDirector* pDirector = CCDirector::sharedDirector();
  126. switch (keyCode) {//keyCode是android上按键的编码,在nativeKeyDown方法中,他是直接把keyCode传递过来的,如果我们还有特殊需求,也可以添加其他事件
  127. case KEYCODE_BACK:
  128. if (pDirector->getKeypadDispatcher()->dispatchKeypadMSG(kTypeBackClicked);//终于看到dispatchKeypadMSG了,传递了ccKeypadMSGType中的kTypeBackClicked
  129. return JNI_TRUE;
  130. break;
  131. case KEYCODE_MENU:
  132. if (pDirector->getKeypadDispatcher()->dispatchKeypadMSG(kTypeMenuClicked))////终于看到dispatchKeypadMSG了,传递了ccKeypadMSGType中的kTypeMenuClicked
  133. return JNI_TRUE;
  134. break;
  135. default:
  136. return JNI_FALSE;
  137. }
  138. return JNI_FALSE;
  139. }

上面就是完整的按键回调流程,没有具体讲KeypadTest,但也差不多了!

下面就看看AccelerometerTest把。看看Accelerometer的流程是啥样的?下面仅仅只针对于Android( ⊙ o ⊙ )啊!

  1. setAccelerometerEnabled(true);//在onEnter()仅仅只需要设置这样就可以。
  2. //接着就可以到didAccelerate方法中接受了。该方法是CCAccelerometerDelegate中的类,CCLayer继承于CCAccelerometerDelegate中
  3. void AccelerometerTest::didAccelerate(CCAcceleration* pAccelerationValue)
  4. {
  5. CCDirector* pDir = CCDirector::sharedDirector();
  6. /*FIXME: Testing on the Nexus S sometimes m_pBall is NULL */
  7. if ( m_pBall == NULL ) {
  8. return;
  9. }
  10. CCSize ballSize = m_pBall->getContentSize();//获取球球的大小
  11. CCPoint ptNow = m_pBall->getPosition();//获取球球的位置,由于该球直接添加到CCLayer的,所以不需要转换成世界坐标系
  12. CCPoint ptTemp = pDir->convertToUI(ptNow);//直接转成UI坐标系:左上为原点,x轴向右,y轴向下
  13. ptTemp.x += pAccelerationValue->x * 9.81f;//pAccelerationValue->x * 9.81f才是加速度
  14. ptTemp.y -= pAccelerationValue->y * 9.81f;//注意是-啊
  15. CCPoint ptNext = pDir->convertToGL(ptTemp);//转换成世界坐标系
  16. FIX_POS(ptNext.x, (VisibleRect::left().x+ballSize.width / 2.0), (VisibleRect::right().x - ballSize.width / 2.0));
  17. FIX_POS(ptNext.y, (VisibleRect::bottom().y+ballSize.height / 2.0), (VisibleRect::top().y - ballSize.height / 2.0));
  18. m_pBall->setPosition(ptNext);//设置坐标
  19. }
  20. //FIX_POS确保球运动不超出屏幕
  21. #define FIX_POS(_pos, _min, _max) \
  22. if (_pos < _min) \
  23. _pos = _min; \
  24. else if (_pos > _max) \
  25. _pos = _max; \

关于坐标转换的,参考下这:cocos2dx进阶学习之坐标转换

  1. void CCLayer::setAccelerometerEnabled(bool enabled)
  2. {
  3. if (enabled != m_bAccelerometerEnabled)
  4. {
  5. m_bAccelerometerEnabled = enabled;
  6. if (m_bRunning)
  7. {
  8. CCDirector* pDirector = CCDirector::sharedDirector();
  9. if (enabled)
  10. {
  11. pDirector->getAccelerometer()->setDelegate(this);//设置代理类,CCLayer继承于CCAccelerometerDelegate
  12. }
  13. else
  14. {
  15. pDirector->getAccelerometer()->setDelegate(NULL);//注意此处直接传的是NULL,在下面看看为啥传NULL啊
  16. }
  17. }
  18. }
  19. }
  20. class CCAcceleration
  21. {
  22. public:
  23. double x;//X轴加速度
  24. double y;//Y轴加速度
  25. double z;//Z轴加速度
  26. double timestamp;//时间戳
  27. };
  28. class CC_DLL CCAccelerometerDelegate//代理类,CCLayer继承于她
  29. {
  30. public:
  31. virtual void didAccelerate(CCAcceleration* pAccelerationValue) {CC_UNUSED_PARAM(pAccelerationValue);}
  32. };
  33. 下面我们就看看CCAccelerometer
  34. CCAccelerometerDelegate* m_pAccelDelegate;//有他,那肯定就有setDelegate方法,否则怎么赋值啊。
  35. CCAcceleration m_obAccelerationValue;//这个变量就是为了存储加速度以及时间戳的
  36. //好了,终于看到setDelegate了,从CCLayer一直追到这
  37. void CCAccelerometer::setDelegate(CCAccelerometerDelegate* pDelegate)
  38. {
  39. m_pAccelDelegate = pDelegate;
  40. if (pDelegate)//看setDelegate设置为NULL的作用了没,为了判断运行不同的方法
  41. {
  42. enableAccelerometerJNI();
  43. }
  44. else
  45. {
  46. disableAccelerometerJNI();
  47. }
  48. }
  49. 下面先看enableAccelerometerJNI方法。
  50. void enableAccelerometerJNI() {
  51. JniMethodInfo t;
  52. if (JniHelper::getStaticMethodInfo(t, CLASS_NAME, "enableAccelerometer", "()V")) {
  53. t.env->CallStaticVoidMethod(t.classID, t.methodID);//CLASS_NAME "org/cocos2dx/lib/Cocos2dxHelper",会运行类名为org/cocos2dx/lib/Cocos2dxHelper下的enableAccelerometer方法。
  54. t.env->DeleteLocalRef(t.classID);
  55. }
  56. }
  57. 下面继续看enableAccelerometer方法
  58. public static void enableAccelerometer() {
  59. Cocos2dxHelper.sAccelerometerEnabled = true;
  60. Cocos2dxHelper.sCocos2dxAccelerometer.enable();//继续查看
  61. }
  62. public void enable() {
  63. this.mSensorManager.registerListener(this, this.mAccelerometer, SensorManager.SENSOR_DELAY_GAME);//注册了一个监听器,其实这都是android的知识了啊。this代表Cocos2dxAccelerometer,他实现了SensorEventListener接口,他会回调public void onSensorChanged(final SensorEvent pSensorEvent) 方法。
  64. }
  65. //在onSensorChanged(final SensorEvent pSensorEvent)回调方法中
  66. @Override
  67. public void onSensorChanged(final SensorEvent pSensorEvent) {
  68. if (pSensorEvent.sensor.getType() != Sensor.TYPE_ACCELEROMETER) {
  69. return;
  70. }
  71. float x = pSensorEvent.values[0];//获取相应方向的加速度
  72. float y = pSensorEvent.values[1];
  73. final float z = pSensorEvent.values[2];
  74. final int orientation = this.mContext.getResources().getConfiguration().orientation;
  75. if ((orientation == Configuration.ORIENTATION_LANDSCAPE) && (this.mNaturalOrientation != Surface.ROTATION_0)) {//判断旋转方向,横屏
  76. final float tmp = x;
  77. x = -y;
  78. y = tmp;
  79. } else if ((orientation == Configuration.ORIENTATION_PORTRAIT) && (this.mNaturalOrientation != Surface.ROTATION_0)) {//竖屏
  80. final float tmp = x;
  81. x = y;
  82. y = -tmp;
  83. }
  84. Cocos2dxGLSurfaceView.queueAccelerometer(x,y,z,pSensorEvent.timestamp);//把加速度的值传递到queueAccelerometer
  85. }
  86. //定位到queueAccelerometer
  87. public static void queueAccelerometer(final float x, final float y, final float z, final long timestamp) {
  88. mCocos2dxGLSurfaceView.queueEvent(new Runnable() {//queueEvent可以实现主线程和渲染线程之间的交互
  89. @Override
  90. public void run() {
  91. Cocos2dxAccelerometer.onSensorChanged(x, y, z, timestamp);//我们发现该方法是个native方法,在C++文件中,我们找到了具体实现
  92. }
  93. });
  94. //下面就是具体实现
  95. JNIEXPORT void JNICALL Java_org_cocos2dx_lib_Cocos2dxAccelerometer_onSensorChanged(JNIEnv* env, jobject thiz, jfloat x, jfloat y, jfloat z, jlong timeStamp) {
  96. CCDirector* pDirector = CCDirector::sharedDirector();
  97. pDirector->getAccelerometer()->update(x, y, z, timeStamp);//我们发现又回到了CCAccelerometer,而这次是update方法,一个轮回啊。
  98. }
  99. //下面前往CCAccelerometer的update方法。
  100. void CCAccelerometer::update(float x, float y, float z, long sensorTimeStamp)
  101. {
  102. if (m_pAccelDelegate)
  103. {
  104. m_obAccelerationValue.x = -((double)x / TG3_GRAVITY_EARTH);//TG3_GRAVITY_EARTH 9.80665f为重力加速度,感觉是为了和其他平台相适应,所以除以重力加速度
  105. m_obAccelerationValue.y = -((double)y / TG3_GRAVITY_EARTH);
  106. m_obAccelerationValue.z = -((double)z / TG3_GRAVITY_EARTH);
  107. m_obAccelerationValue.timestamp = (double)sensorTimeStamp;
  108. m_pAccelDelegate->didAccelerate(&m_obAccelerationValue);//看到回调了啊,就是上文,例子中处理逻辑的啊
  109. }
  110. }

好了,关于CCAccelerometer的逻辑也讲的差不多了,通过CCAccelerometer设置setDelegate,又通过他接受返回数据。

青春是一场大雨,即使感冒了,还盼回头再淋一次...image...微笑永远是一个人身上最好看的东西...


PREVIOUS     NEXT