cocos2d-x之CCSceneTest以及TransitionsTest

| 分类 cocos2d-x  | 标签 cocos2d-x-2.2.3  CCScene  TestCpp 

好了,从今天开始会将以正常的速度更新博文,image …再也不那么懒了。。。

CCScene这东西在英语中叫做场景,当然在CoCos2d-x中也是一样的东东!!!下面就先参观下CCScene的代码吧:

  1. //初始化中将m_bIgnoreAnchorPointForPosition设置为true,同时描点设置为(0.5,0.5),但是此时描点不起作用啊
  2. CCScene::CCScene()
  3. {
  4. m_bIgnoreAnchorPointForPosition = true;
  5. setAnchorPoint(ccp(0.5f, 0.5f));
  6. } CCScene的其他方法没有实质性的作用,还是先看它的类图吧:
![image](/assets/classcocos2d_1_1_c_c_scene.png)

CCSceneTest图-1

![image](/assets/classcocos2d_1_1_c_c_transition_ease_scene.png)

CCSceneTest图-2

![image](/assets/classcocos2d_1_1_c_c_transition_scene_oriented.png)

CCSceneTest图-3

![image](/assets/classcocos2d_1_1_c_c_transition_progress.png)

CCSceneTest图-4

从上面四副图我们可以很直观的看到,CCScene有个子类叫做CCTransitionScene,他是场景切换的基类,所有场景过渡效果的类都继承自CCTransitionScene。具体的如下:

  1. =========第1种============
  2. CCTransitionFade 场景在指定的时间内淡入淡出到一个指定的颜色
  3. =========第2种============
  4. CCTransitionFadeTR 从屏幕的左下角向上卷起瓦片(tiles),从而显示场景
  5. CCTransitionFadeBL 从屏幕的右上角到左下角淡出场景的瓦片
  6. CCTransitionFadeUp 从屏幕的底部到顶部淡出场景的瓦片
  7. CCTransitionFadeDown 从屏幕的顶部到底部淡出场景的瓦片
  8. =========第3种============
  9. CCTransitionJumpZoom 现有场景跳动着变小,新场景跳动着变大
  10. =========第4种============
  11. CCTransitionMoveInL 旧的场景会移出,而新场景会从屏幕左侧移入
  12. CCTransitionMoveInB 新场景从屏幕的底部移入
  13. CCTransitionMoveInT 新场景从屏幕的上方移入
  14. CCTransitionMoveInR 新场景从屏幕的右侧移入
  15. =========第5种============
  16. CCTransitionSceneOriented 将整个场景翻转过来
  17. CCTransitionFlipX 场景横向翻转
  18. CCTransitionFlipY 场景纵向翻转
  19. CCTransitionFlipAngular 场景一半按横向,一半按纵向翻转
  20. CCTransitionZoomFlipAngular 场景一半按横向,一半按纵向翻转,同时有一定缩放
  21. CCTransitionZoomFlipX 场景纵向翻转,同时有一定缩放
  22. CCTransitionZoomFlipY 场景横向翻转,同时有一定缩放
  23. =========第6种============
  24. CCTransitionPageTurn 翻动书页的过渡效果
  25. =========第7种============
  26. CCTransitionRotoZoom 当前场景旋转变小,新场景旋转变大
  27. =========第8种============
  28. CCTransitionShrinkGrow 当前场景缩小,新场景在其之上变大
  29. =========第9种============
  30. CCTransitionSlideInL 新场景从左边滑入
  31. CCTransitionSlideInR 新场景从右边滑入
  32. CCTransitionSlideInB 新场景从底部滑入
  33. CCTransitionSlideInT 新场景从顶部滑入
  34. =========第10种============
  35. CCTransitionSplitCols 将当前场景切成竖条,上下移动显示新场景
  36. CCTransitionSplitRows 将当前场景切成横条,左右移动显示新场景
  37. =========第11种============
  38. CCTransitionTurnOffTiles 将当前场景分成方块,用分成方块的新场景随机地替换当前场景分出的方块
  39. =========第12种============
  40. CCTransitionProgressRadialCCW 新场景沿着径向逆时针显示
  41. CCTransitionProgressRadialCW 让新的场景沿着径向顺时针显示
  42. =========第13种============
  43. CCTransitionProgressHorizontal 新场景沿着横向逐渐显示
  44. CCTransitionProgressVertical 让新场景沿着纵向逐渐显示
  45. =========第14种============
  46. CCTransitionProgressInOut 新场景由内到外逐渐显示
  47. CCTransitionProgressOutIn 让新场景由外到内逐渐显示 好了,效果实在太多了,终于扯完了,了解下就好。下面我们还是先讲讲Scene生命周期:
  48. ===========屌丝切换场景=====CCScene=============
  49. 目前有三个CCLayer,分别是SceneTestLayer1SceneTestLayer2SceneTestLayer3。现在通过pushScene(CCScene *scene)从SceneTestLayer1调转到SceneTestLayer2,通过replaceScene(CCScene *scene)从SceneTestLayer2调转到SceneTestLayer3,下面看相关的跳转情况:
  50. =========从SceneTestLayer1调转到SceneTestLayer2===========
  51. Cocos2d: SceneTestLayer1#onExit
  52. Cocos2d: SceneTestLayer2#onEnter
  53. Cocos2d: SceneTestLayer2#onEnterTransitionDidFinish
  54. =========从SceneTestLayer2调转到SceneTestLayer3===========
  55. Cocos2d: SceneTestLayer2#onExit
  56. Cocos2d: SceneTestLayer2#~SceneTestLayer2
  57. Cocos2d: SceneTestLayer3#onEnter
  58. Cocos2d: SceneTestLayer3#onEnterTransitionDidFinish
  59. 从上面的跳转情况可以得出这么个结论:
  60. 1) .当通过pushScene(CCScene *scene)切换Scence的时候,正在运行的场景是不会clearn掉的,他仅仅被压到栈中,所以相应的生命周期是:1#onExit-》2#onEnter-》2#onEnterTransitionDidFinish
  61. 2) .当通过replaceScene(CCScene *scene)切换Scence的时候,正在运行的场景被新场景替换掉,所以正在运行的场景也就会clean掉,则相应的生命周期是:2#onExit-》2#~SceneTestLayer2-》3#onEnter-》3#onEnterTransitionDidFinish
  62. ===========高富帅切换场景=======CCTransitionScene===========
  63. 现在还是有三个CCLayer,分别是SceneTestLayer1SceneTestLayer2SceneTestLayer3。现在通过pushScene(CCTransitionScene *scene)从SceneTestLayer1调转到SceneTestLayer2,通过replaceScene(CCTransitionScene *scene)从SceneTestLayer2调转到SceneTestLayer3,下面看相关的跳转情况:
  64. =========从SceneTestLayer1调转到SceneTestLayer2===========
  65. Cocos2d: SceneTestLayer2#onEnter
  66. Cocos2d: SceneTestLayer1#onExit
  67. Cocos2d: SceneTestLayer2#onEnterTransitionDidFinish
  68. =========从SceneTestLayer2调转到SceneTestLayer3===========
  69. Cocos2d: SceneTestLayer3#onEnter
  70. Cocos2d: SceneTestLayer2#onExit
  71. Cocos2d: SceneTestLayer3#onEnterTransitionDidFinish
  72. Cocos2d: SceneTestLayer2#~SceneTestLayer2
  73. 从上面的跳转情况可以得出这么个结论:
  74. 1) .当通过pushScene(CCTransitionScene *scene)切换Scence的时候,通过和屌丝切换比较我们发现生命周期竟然变了,当然切换的时候也没发生剧变,正在运行的场景也仅仅就是被压到栈中,相应的生命周期:2#onEnter-》1#onExit-》2#onEnterTransitionDidFinish
  75. 2) .当通过replaceScene(CCTransitionScene *scene)切换Scence的时候,通过和屌丝切换比较周期也变了,正在运行的场景当然也被新场景替换掉,所以正在运行的场景也就会clean掉,则相应的生命周期是:3#onEnter-》2#onExit-》3#onEnterTransitionDidFinish-》2#~SceneTestLayer2
  76. 通过比较上诉两张切换方式,也不难理解,由于通过高富帅切换的时候需要场景A,B做动作,摆pose!不能太早把老场景扔到栈中,或者直接清掉!充分利用好你了,才把你扔一边去,现在的场景切换就很是表达这个观念。。。
  77. 对了,关于上诉的生命周期,还有一个欠缺的,没有init()方法,其实他也不是随便出现的,只有通过create(),才会有init(),而通过new出来的可惜了,不会运行你的。当然了,init()方法如果有,基本上是第一个运行的。
  78. 还有一个疑问,为啥生命周期通过高富帅切换场景突变了???下面的博文会解答这问题... 下面我们先不关注CCTransitionScene,还是跳转到CCDirector,再细细的品味下关于场景转换,以及渲染。具体渲染流程,查看[cocos2d-x-渲染](/cocos2d-x-渲染/)
  79. replaceScene以及pushScene具体实现是怎么工作的?为什么replace会清空,而push却不会。。。
  80. =====replaceScene 具体实现===
  81. void CCDirector::replaceScene(CCScene *pScene)
  82. {
  83. unsigned int index = m_pobScenesStack->count();//m_pobScenesStack是CCArray*类型
  84. m_bSendCleanupToScene = true; //replace和push的主要区别就在于m_bSendCleanupToScene是否为true
  85. m_pobScenesStack->replaceObjectAtIndex(index - 1, pScene);//把新加入的CCScene添加到最后一个,覆盖了原来的场景
  86. m_pNextScene = pScene;//m_pNextScene只有在replace以及push中赋值,在setNextScene中设置为NULL
  87. }
  88. =========pushScene 具体实现============
  89. void CCDirector::pushScene(CCScene *pScene)
  90. {
  91. m_bSendCleanupToScene = false;//replace和push的主要区别就在于m_bSendCleanupToScene是否为true
  92. m_pobScenesStack->addObject(pScene);//把新加入的CCScene添加到最后一个,比较下和replaceScene中的区别
  93. m_pNextScene = pScene;//m_pNextScene只有在replace以及push中赋值,在setNextScene中设置为NULL
  94. }
  95. 从上我们可以明白replaceScene以及pushScene两则的具体区别就在于m_bSendCleanupToScene,以及是否把原先的场景压入栈
  96. =============popScene 他能将top上的栈给挤出来==========
  97. void CCDirector::popScene(void)
  98. {
  99. m_pobScenesStack->removeLastObject();//移除最后一个
  100. unsigned int c = m_pobScenesStack->count();
  101. if (c == 0)
  102. {
  103. end();
  104. }
  105. else //else后面的处理方式跟replace比较下,熟悉吧,一摸一样
  106. {
  107. m_bSendCleanupToScene = true;
  108. m_pNextScene = (CCScene*)m_pobScenesStack->objectAtIndex(c - 1);
  109. }
  110. }
  111. =========明白了上面一系列的东西,我们就该去啃骨头了=====定位到CCDirector中的drawScene()====
  112. void CCDirector::drawScene(void)
  113. {
  114. ...
  115. if (m_pNextScene) //如果m_pNextScene不为空,这个m_pNextScene在刚才push,以及replace赋值过,在setNextScene中设置为NULL
  116. {
  117. setNextScene();//重点是setNextScene这个方法
  118. }
  119. ...
  120. }
  121. =======男主角===setNextScene上场=====
  122. void CCDirector::setNextScene(void)
  123. {
  124. bool runningIsTransition = dynamic_cast<CCTransitionScene*>(m_pRunningScene) != NULL;//将正在运行的Scene转化为CCTransitionScene,如果Scene为CCTransitionScene类型,则runningIsTransition为true,否则为false.m_pRunningScene在接下来会通过m_pNextScene赋值得到
  125. bool newIsTransition = dynamic_cast<CCTransitionScene*>(m_pNextScene) != NULL;//m_pNextScene为即将要运行的Scene。在pushScene或者replaceScene中赋值,如果m_pNextScene为CCTransitionScene类型,最newIsTransition为true,否则为false.
  126. if (! newIsTransition)//当m_pNextScene不是CCTransitionScene时候执行下面一段代码
  127. {
  128. if (m_pRunningScene)
  129. {
  130. m_pRunningScene->onExitTransitionDidStart();
  131. m_pRunningScene->onExit();
  132. }
  133. if (m_bSendCleanupToScene && m_pRunningScene)//当m_bSendCleanupToScene的时候,会清空m_pRunningScene
  134. {
  135. m_pRunningScene->cleanup();
  136. }
  137. }
  138. if (m_pRunningScene)
  139. {
  140. m_pRunningScene->release();
  141. }
  142. m_pRunningScene = m_pNextScene;//将m_pNextScene赋值给正在运行的场景
  143. m_pNextScene->retain();
  144. m_pNextScene = NULL;//设置为NULL,是因为在drawScene方法中有个判断,如果m_pNextScene不为null,就执行setNextScene
  145. if ((! runningIsTransition) && m_pRunningScene)//当m_pRunningScene(这个m_pRunningScene指的是没有赋值前的m_pRunningScene)不是CCTransitionScene时候执行下面一段代码
  146. {
  147. m_pRunningScene->onEnter();//如果m_pRunningScene为CCTransitionScene类型(其实此时的m_pRunningScene的值为m_pNextScene,因为在前面刚刚赋值),则会进入到CCTransitionScene子类的onEnter()中,在这方法中是真正的运行动画的实现的地方
  148. m_pRunningScene->onEnterTransitionDidFinish();
  149. }
  150. }
  151. 下面来总结下setNextScene这个方法吧。
  152. 1.如果当前正在运行的m_pRunningScene的场景为CCSence,则runningIsTransitionfalse,此时如果m_pNextScene是通过pushScene(CCTransitionScene *scene)赋值的CCTransitionScene场景类,则newIsTransition相应的就是true了,此时第一个if语句就不能运行,接着将CCTransitionScene类型的newIsTransition赋值给m_pRunningScene,此时m_pRunningScene就是CCTransitionScene类型,由于runningIsTransitionfalse,所以第二个if自然就能进去,接着m_pRunningScene就去运行onEnter,以及onEnterTransitionDidFinish().(如果此时有AB两个场景,A场景通过高富帅跳转到B,通过上文学习,我们知道没有立即运行AonExit,而是运行BonEnter,以及onEnterTransitionDidFinish),说道这里可能有人以为结束了,其实CCTransitionSceneonEnter方法中还是做了很多工作的,为什么这么讲啊?我们还是直接去看看CCTransitionSceneonEnter方法把。
  153. ======下面看到的方法是CCTransitionScene的子类CCTransitionRotoZoomonEnter方法===========
  154. void CCTransitionRotoZoom:: onEnter()
  155. {
  156. CCTransitionScene::onEnter();//真正m_pInScene的onEnter()方法,此处m_pInScene是CCScene类型,是我们初始化CCTransitionScene的时候传进来的CCScene传给m_pInScene
m_pInScene->setScale(0.001f);
  1. m_pOutScene->setScale(1.0f);
  2. m_pInScene->setAnchorPoint(ccp(0.5f, 0.5f));
  3. m_pOutScene->setAnchorPoint(ccp(0.5f, 0.5f));
  4. CCActionInterval *rotozoom = (CCActionInterval*)(CCSequence::create
  5. (
  6. CCSpawn::create
  7. (
  8. CCScaleBy::create(m_fDuration/2, 0.001f),
  9. CCRotateBy::create(m_fDuration/2, 360 * 2),NULL),CCDelayTime::create(m_fDuration/2), NULL));
  10. m_pOutScene->runAction(rotozoom);
  11. m_pInScene->runAction
  12. (
  13. CCSequence::create
  14. (
  15. rotozoom->reverse(),
  16. CCCallFunc::create(this, callfunc_selector(CCTransitionScene::finish)),
  17. NULL
  18. )
  19. );
  20. }
  21. onEnter的方法中我们看到m_pOutScene以及m_pInScene分别执行了相应的动画,同时结束的时候还运行finish()方法。在finish()方法中又这么一句话this->schedule(schedule_selector(CCTransitionScene::setNewScene), 0);重点就是setNewScene这个方法,运行完finish()方法接着又去运行了setNewScene()方法了,好了接着咱们就去看setNewScene()方法
  22. void CCTransitionScene::setNewScene(float dt)
  23. {
  24. this->unschedule(schedule_selector(CCTransitionScene::setNewScene));//解除这个定时器
  25. // Before replacing, save the "send cleanup to scene"
  26. CCDirector *director = CCDirector::sharedDirector();
  27. m_bIsSendCleanupToScene = director->isSendCleanupToScene();//在replace之前事先保存是否清空Scene的标志位,因为在cleanup()方法中,通过这个标志位判断是否清空m_pOutScene这个场景
  28. director->replaceScene(m_pInScene);
  29. //这句话是重点的重点,这里又去执行了replaceScene(),而且m_pInScene此时就是CCScene类型,因为他是在CCTransitionScene初始化的时候传入进来的CCScene的参数。
  30. //好了,我们冷静,思考下,发现了这么好玩的事情,又得去执行setNextScene方法,通过replaceScene(CCTransitionScene)或者pushScene(CCTransitionScene)切换场景竟然会出现这么神奇的情况,会出现两次把场景压入栈中,第一次replace或者push进去的是CCTransitionScene的场景,第二次是通过replace进去CCScene替换掉第一次的CCTransitionScene场景,所以也就会出现运行两次的setNextScene方法,所以setNextScene方法中的两个标志位也会出现有意思的情况.
  31. //我来用具体例子来解释下把:如果现在有两个场景A,B。A跳转到B场景是通过高富帅方式跳转的,在第一次进入setNextScene方法的时候,我们会发现runningIsTransition为false,newIsTransition为true,runningIsTransition为false表示正在运行的场景m_pRunningScene不是CCTransitionScene类型???为啥这么说呢,我们原先可没明确说明场景A不是CCTransitionScene类型啊,确实是个疑问,等下慢慢细说。。。而newIsTransition为true,表面m_pNextScene为CCTransitionScene类型,这个确实是CCTransitionScene类型,因为刚才我们通过replaceScene或者pushScene进来的时候就是CCTransitionScene类型啊,在setNextScene中我们发现,m_pRunningScene被赋值了,变成了CCTransitionScene类型,同时m_pNextScene赋值为NULL,现在要明确点啊,运行的场景变成了CCTransitionScene类型了!!!好了,这是第一次 进入setNextScene方法。
  32. //上面我们讲到由于通过replaceScene(m_pInScene),所以又第二次进入setNextScene方法,此时的话m_pRunningScene是CCTransitionScene类型,所以相应的runningIsTransition为true,而newIsTransition为false,因为m_pInScene为CCScene类型,看到了把,跟第一次比起来,两个标志位发生了改变了。在setNextScene方法中,同样的m_pRunningScene会被m_pInScene赋值,变成普通的CCScene类型,如果我们再一次切换场景,进入setNextScene方法,则m_pRunningScene就是CCScene类型,所以runningIsTransition就为false。好了这也解决了我们刚才的疑问,第一次进来的时候我很明确的就是A场景就是为CCScene类型。
  33. //上面说了这么一大段,啰嗦的,其实可以归纳为:通过高富帅转换的时候,会两次的进入setNextScene方法,而最终转换成功后显示的场景就是CCScene类型,而不是CCTransitionScene类型。
  34. // issue #267
  35. m_pOutScene->setVisible(true);
  36. }

关于场景变换的相关代码解析,还是看红孩儿的Cocos2d-x 2.0 TestCpp之场景切换动画深入分析

CCTransitionScene中具体的动画解析,在本文中,暂且不去伤身了,红孩儿大神已经讲的很仔细了。接下来,我还是说点我看到的知识点。

  1. CCRenderTexture *texture = CCRenderTexture::create((int)size.width, (int)size.height);//创建纹理
  2. texture->getSprite()->setAnchorPoint(ccp(0.5f,0.5f));
  3. texture->setPosition(ccp(size.width/2, size.height/2));
  4. texture->setAnchorPoint(ccp(0.5f,0.5f));
  5. // r将outScene渲染到纹理上
  6. texture->clear(0, 0, 0, 1);
  7. texture->begin();
  8. m_pSceneToBeModified->visit();
  9. texture->end();

上面这段代码主要将的是怎么将CCScene渲染到纹理上面,主要分成5部分:

  1. 创建一个新的CCRenderTexture. 这里,你可以指定将要创建的纹理的宽度和高度。
  2. 调用 CCRenderTexture:begin. 这个方法会启动OpenGL,并且接下来,任何绘图的命令都会渲染到CCRenderTexture里面去,而不是画到屏幕上。
  3. 绘制纹理. 你可以使用原始的OpenGL调用来绘图,或者你也可以使用cocos2d对象里面已经定义好的visit方法。(这个visit方法就会调用一些opengl命令来绘制cocos2d对象)
  4. 调用 CCRenderTexture:end. 这个方法会渲染纹理,并且会关闭渲染至CCRenderTexture的通道。
  5. 从生成的纹理中创建一个sprite. 你现在可以用CCRenderTexture的sprite.texture属性来轻松创建新的精灵了。

上面这五个步骤摘抄自:如何使用CCRenderTexture来创建动态纹理,可以看看这个例子,充分利用好纹理的特性。

对了,讲了半天都忘了这标题可是CCSceneTest以及TransitionsTest,好像整篇文章都没讲这,现在还是说点把。 有这么一句CCDirector::sharedDirector()->setDepthTest(false),关闭深度测试,有时候可以解决闪屏问题。在通过CCTransitionPageTurn切换场景的时候需要打开深度测试。

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


PREVIOUS     NEXT