《Cocos2D-x权威指南》——3.4 布景层类

    xiaoxiao2022-05-26  182

    3.4 布景层类

    布景层类CCLayer是CCNode类的子类,并且在此基础上实现触屏事件代理(TouchEventsDelegate)协议,可以实现CCNode类的功能,并且可以处理输入,包括触屏和加速度传感器。每个游戏场景中可以有很多层,每一层负责各自的任务,如专门负责显示地图的背景、专门负责显示敌人、专门负责机关和专门负责主角等;每一层上可以放置不同的元素,包括文本、精灵图片和菜单等。通过层与层之间的组合关系,就可以构成游戏显示的界面UI,游戏中等。当然为了看到每一层的东西,可把一些层设置为透明或半透明的,这样就可以看到不同布景层叠加到一起的效果了。CCLayer类的继承关系如图3-12所示。

    由图3-12可以看出CCLayer类继承自CCNode类,并且CCLayer类还遵照触屏代理协议、加速度传感器代理协议、键盘时间代理协议等协议。除此之外,CCLayer类还有子类,如图3-13所示。

    这些子类的功能如表3-8所示。

    首先来看CCLayer类的使用,然后再来看主要的子类使用。3.4.1 CCLayer类的函数CCLayer类的主要函数如表3-9所示。

    来看Cocos2D-x的HelloWorld项目中的HelloWorldScene.cpp文件,scene函数定义CCLayer类并把它加入场景中,如代码清单3-16所示。代码清单3-16 scene函数定义CCLayer类并把它加入场景中

    CCScene* HelloWorld::scene() { //新建场景类实例 CCScene *scene = CCScene::create(); //定义布景层 HelloWorld *layer = HelloWorld::create(); //将布景层加入场景 scene->addChild(layer); //返回场景类 return scene; }

    CCLayer类的init函数在创建布景层时被调用,如代码清单3-17所示。代码清单3-17 CCLayer类的init函数

    bool HelloWorld::init() { if ( !CCLayer::init() ) { return false; } CCMenuItemImage *pCloseItem = CCMenuItemImage::create( "CloseNormal.png", "CloseSelected.png", this, menu_selector(HelloWorld::menuCloseCallback) ); pCloseItem->setPosition( ccp(CCDirector::sharedDirector()->getWinSize().width - 20, 20) ); CCMenu* pMenu = CCMenu::create(pCloseItem, NULL); pMenu->setPosition( CCPointZero ); this->addChild(pMenu, 1); CCLabelTTF* pLabel = CCLabelTTF::create("Hello World", "Arial", 24); CCSize size = CCDirector::sharedDirector()->getWinSize(); pLabel->setPosition( ccp(size.width / 2, size.height - 50) ); this->addChild(pLabel, 1); CCSprite* pSprite = CCSprite::create("HelloWorld.png"); pSprite->setPosition( ccp(size.width/2, size.height/2) ); this->addChild(pSprite, 0); return true; }

    本书将在后面介绍具体显示在层次上的对象,目前只需要了解在init函数中定义要显示的对象并把它作为子类加入场景中。另外,关于触屏、键盘、加速度传感器等输入,将在后面的章节介绍。本节后面将介绍CCLayer类的子类。3.4.2 颜色布景层类CCLayerColor颜色布景层类CCLayerColor是CCLayer类的子类,包含CCLayer类的特性,并且有两个拓展功能:可以为布景层增添颜色,以及设置不透明度。首先看CCLayerColor类的定义初始化,如代码清单3-18所示。这段代码是tests项目下LayerTest.cpp文件中LayerTest1的onEnter函数。

    代码清单3-18 CCLayerColor类的定义初始化

    void LayerTest1::onEnter() { LayerTest::onEnter(); setTouchEnabled(true); CCSize s = CCDirector::sharedDirector()->getWinSize(); CCLayerColor* layer = CCLayerColor::create( ccc4(0xFF, 0x00, 0x00, 0x80), 200, 200); layer->ignoreAnchorPointForPosition(false); layer->setPosition( CCPointMake(s.width/2, s.height/2) ); addChild(layer, 1, kTagLayer); }

    create函数的第一个参数是颜色的ARGB值,使用ccc4定义,其中第一个参数是颜色a值,第二个参数是R值,第三个参数是G值,最后一个参数是B值。除此之外,create函数的后两个参数是布景层的宽和高。另外,使用ignoreAnchorPointForPosition将忽略锚点置为false。由于默认设置是忽略锚点,也就是以左下角为锚点,可以让布景层考虑锚点的影响(关于锚点在之前已经介绍过),这时默认的锚点在中心。运行效果如图3-14所示。

    另外,LayerTest.cpp文件中的LayerTest1的updateSize函数中,可以修改颜色布景层的大小,如代码清单3-19所示。 代码清单3-19 修改颜色布景层的大小 void LayerTest1::updateSize(CCPoint &touchLocation) { CCSize s = CCDirector::sharedDirector()->getWinSize(); CCSize newSize = CCSizeMake( fabs(touchLocation.x - s.width/2)*2, fabs(touchLocation.y - s.height/2)*2); CCLayerColor* l = (CCLayerColor*) getChildByTag(kTagLayer); l->setContentSize( newSize ); }

    通过setContentSize可以设置颜色布景层的大小。对于CCLayerColor类来说,还有一个比较常用的函数setBlendFunc,可以让布景层的颜色产生渐变效果。比如,需要在整屏添加全屏的一些覆盖效果、全屏变黑或者全屏变暗时,都可以使用这个方法。代码清单3-20所示是tests项目LayerTestBlend类中的newBlend函数,也就是使用setBlendFunc的示例。代码清单3-20 使用setBlendFunc的示例

    void LayerTestBlend::newBlend(float dt) { CCLayerColor *layer = (CCLayerColor*)getChildByTag(kTagLayer); GLenum src; GLenum dst; if( layer->getBlendFunc().dst == GL_ZERO ) { src = GL_SRC_ALPHA; dst = GL_ONE_MINUS_SRC_ALPHA; } else { src = GL_ONE_MINUS_DST_COLOR; dst = GL_ZERO; } ccBlendFunc bf = {src, dst}; layer->setBlendFunc( bf ); }

    传入的参数是一个有起始效果和结束效果的参数,运行之后的效果如图3-15和图3-16所示。

    CCLayerColor类作为一个带颜色的布景层,在开发中可以给我们做特效带来方便。下一节介绍多层布景层类。3.4.3 多层布景层类CCLayerMultiplex在游戏开发中,一般会把游戏分为两部分:一部分是游戏界面部分,也就是常说的UI部分(User Interface,用户界面);另一部分就是游戏本身部分。有时UI有很多页面,在页面中用的图也并不是很多,不需要使用切换场景,只需把不同页面做成不同的布景层,然后切换布景层。那么这就需要一个“管理者”来管理这些界面,这时候就要使用CCLayerMultiplex类。在很多游戏中都需要在不同的界面中使用相同的几个变量,如果不这样做,就需要做大量的保存工作。tests项目中MenuTest.cpp的MenuTestScene类的runThisTest函数中有CCLayerMultiplex类的定义初始化方法,如代码清单3-21所示。代码清单3-21 CCLayerMultiplex类的定义初始化

    void MenuTestScene::runThisTest() { CCLayer* pLayer1 = new MenuLayerMainMenu(); CCLayer* pLayer2 = new MenuLayer2(); CCLayer* pLayer3 = new MenuLayer3(); CCLayer* pLayer4 = new MenuLayer4(); CCLayer* pLayer5 = new MenuLayerPriorityTest(); CCLayerMultiplex* layer = CCLayerMultiplex::create(pLayer1, pLayer2, pLayer3, pLayer4, pLayer5, NULL); addChild(layer, 0); pLayer1->release(); pLayer2->release(); pLayer3->release(); pLayer4->release(); pLayer5->release(); CCDirector::sharedDirector()->replaceScene(this); }

    首先定义并初始化每个布景层类,然后将这些布景层实例以参数形式传给CCLayerMultiplex的create函数,最后以NULL(空)结束。这里在传入参数之后将这些布景层实例的指针释放,是为了防止内存泄露。至于Cocos2D-x的内存管理,本书将会在后面的章节介绍。然后把CCLayerMultiplex实例作为子节点传入场景中,最后运行场景。代码清单3-22所示是切换布景层的switchTo函数使用方法。代码清单3-22 switchTo函数使用方法

    void MenuLayerPriorityTest::menuCallback(CCObject* pSender) { ((CCLayerMultiplex*)m_pParent)->switchTo(0); }

    由于这个函数被CCLayerMultiplex实例的子布景,即初始化CCLayerMultiplex传入的布景类实例调用,所以它的m_pParent父节点就是CCLayerMultiplex实例本身。获得CCLayerMultiplex实例指针后,调用switchTo函数就可以转换到相应的子布景中。关于子布景中显示的菜单,下一节将会介绍。3.4.4 菜单类CCMenu游戏中常用的菜单如图3-17所示,其中菜单项可以是图片、系统字,或者自定义的字体。

    CCMenu是一个菜单项的容器,用来装载各种菜单项。代码清单3-23就是一个定义CCMenu类实例的过程,是tests项目中MenuTest.cpp的MenuLayer2的构造函数。代码清单3-23 定义CCMenu类实例

    MenuLayer2::MenuLayer2() { for( int i=0;i < 2;i++ ) { CCMenuItemImage* item1 = CCMenuItemImage::create(s_PlayNormal, s_PlaySelect, this, menu_selector(MenuLayer2::menuCallback)); CCMenuItemImage* item2 = CCMenuItemImage::create(s_HighNormal, s_HighSelect, this, menu_selector(MenuLayer2::menuCallbackOpacity) ); CCMenuItemImage* item3 = CCMenuItemImage::create(s_AboutNormal, s_AboutSelect, this, menu_selector(MenuLayer2::menuCallbackAlign) ); item1->setScaleX( 1.5f ); item2->setScaleX( 0.5f ); item3->setScaleX( 0.5f ); CCMenu* menu = CCMenu::create(item1, item2, item3, NULL); CCSize s = CCDirector::sharedDirector()->getWinSize(); menu->setPosition(ccp(s.width/2, s.height/2)); menu->setTag( kTagMenu ); addChild(menu, 0, 100+i); m_centeredMenu = menu->getPosition(); } m_alignedH = true; alignMenusH(); }

    首先定义菜单项(关于菜单项,本书会在后面的章节中做讲解),然后用它们定义初始化菜单CCMenu实例,最后将CCMenu实例加入CCLayer中显示出来,效果如图3-18所示。菜单类还提供了alignItemsVertically和align-ItemsHorizontally等函数。如代码清单3-24所示,tests项目中MenuTest.cpp的MenuLayer2的构造函数alignMenusH就是alignItems-Horizontally水平对齐两种方法对比,一种是alignItems-Horizontally水平对齐,底下是alignItemsHorizontallyWithPadding留空间水平对齐,效果对比请见之前的图3-18。代码清单3-24 对齐方法对比函数

    void MenuLayer2::alignMenusH() { for(int i=0;i<2;i++) { CCMenu *menu = (CCMenu*)getChildByTag(100+i); menu->setPosition( m_centeredMenu ); if(i==0) { // TIP: if no padding, padding = 5 menu->alignItemsHorizontally(); CCPoint p = menu->getPosition(); menu->setPosition( ccpAdd(p, CCPointMake(0,30)) ); } else { // TIP: but padding is configurable menu->alignItemsHorizontallyWithPadding(40); CCPoint p = menu->getPosition(); menu->setPosition( ccpSub(p, CCPointMake(0,30)) ); } } }

    使用方法比较简单,直接调用就可以。下一节介绍Cocos2D-x中的UI控件。3.4.5 控件类及其子类在应用的开发中,无论是Android操作系统还是iOS操作系统,其开发框架都提供了控件,包括按键、拖动滑块等,这样提高了开发效率。对于游戏的开发,UI的开发同样需要控件来提高开发效率。对Cocos2D-x来说,从2.0版本开始提供了很多控件类来帮助我们更好地开发UI。1 . 拖动滑块的控件类CCControlSlider首先来看拖动滑块的控件类CCControlSlider。tests项目中ControlExtensionTestCCControl-SliderTest目录下CCControlSliderTest.cpp中的代码如代码清单3-25所示。代码清单3-25 定义并初始化CCControlSliderTest类实例

    bool CCControlSliderTest::init() { if (CCControlScene::init()) { CCSize screenSize = CCDirector::sharedDirector()->getWinSize(); //定义标签的代码,考虑到篇幅而被省略 ... //定义CCControlSlider CCControlSlider *slider = CCControlSlider::create("extensions/sliderTrack.png","extensions/sliderProgress.png" ,"extensions/sliderThumb.png"); slider->setAnchorPoint(ccp(0.5f, 1.0f)); slider->setMinimumValue(0.0f); // 设置范围最小值 slider->setMaximumValue(5.0f); // 设置范围最大值 slider->setPosition(ccp(screenSize.width / 2.0f, screenSize.height / 2.0f)); //添加回调函数,当滑块被拖动时被调用 slider->addTargetWithActionForControlEvents(this, cccontrol_selector(CCControlSliderTest::valueChanged), CCControlEventValueChanged); addChild(slider); return true; } return false; } void CCControlSliderTest::valueChanged(CCObject *sender, CCControlEvent controlEvent) { CCControlSlider* pSlider = (CCControlSlider*)sender; m_pDisplayValueLabel->setString(CCString::createWithFormat("Slider value = %.02f", pSlider->getValue())->getCString()); }

    要定义拖动滑块对象,首先调用create函数,参数为图片路径,分别是滑块滑道图片路径、滑块滑动后覆盖滑道图片路径和滑块图片路径;之后设置锚点,并设置范围最小值和设置范围最大值,设置位置后给拖动注册拖动事件接受函数。在拖动事件中,可以通过pSlider->getValue())->getCString()来获取目前所在位置的值,运行效果如图3-19所示。

    2 . 颜色选择盘类CCControlColourPicker 颜色选择盘类CCControlColourPicker的定义和初始化如代码清单3-26所示。代码在tests项目中ControlExtensionTest CCControl-ColourPickerTest目录下的CCControlColourPicker-Test.cpp文件中。 代码清单3-26 CCControlColourPicker类的定义和初始化 bool CCControlColourPickerTest::init() { if (CCControlScene::init()) { CCSize screenSize = CCDirector::sharedDirector()->getWinSize(); CCNode *layer = CCNode::create(); layer->setPosition(ccp (screenSize.width / 2, screenSize.height / 2)); addChild(layer, 1); double layer_width = 0; //定义并初始化颜色选择盘 CCControlColourPicker *colourPicker = CCControlColourPicker::create(); colourPicker->setColor(ccc3(37, 46, 252)); colourPicker->setPosition(ccp (colourPicker->getContentSize().width / 2, 0)); //添加到层次中 layer->addChild(colourPicker); //注册事件 colourPicker->addTargetWithActionForControlEvents(this, cccontrol_selector(CCControlColourPickerTest::colourValueChanged), CCControlEventValueChanged); //以下定义其他控件的代码省略 ... return true; } return false; } void CCControlColourPickerTest::colourValueChanged(CCObject *sender, CCControlEvent controlEvent) { CCControlColourPicker* pPicker = (CCControlColourPicker*)sender; m_pColorLabel->setString(CCString::createWithFormat("#XXX",pPicker->getColorValue().r, pPicker->getColorValue().g, pPicker->getColorValue().b)->getCString()); }

    首先定义CCControlColourPicker,直接调用create函数就可以。这里需要把testsResourcesextensions目录下和CCControlColour-Picker相关的文件复制到Resourcesextensions目录,然后定义初始颜色,加入父节点中,并注册回调函数。回调函数通过pPicker->getColorValue().g, pPicker->getColorValue().b)->getCString()获得相应的颜色值字符串。运行效果如图3-20所示。

    3 . 开关按钮类CCControlSwitch 开关按钮类CCControlSwitch的定义和初始化如代码清单3-27所示。代码在tests项目中的ControlExtensionTest CCControlSwitchTest目录下的CCControlSwitchTest.cpp中。 代码清单3-27 CCControlSwitch的定义和初始化 bool CCControlSwitchTest::init() { if (CCControlScene::init()) { //定义其他控件,代码省略 ... // 定义开关控件 CCControlSwitch *switchControl = CCControlSwitch::create ( CCSprite::create("extensions/switch-mask.png"), CCSprite::create("extensions/switch-on.png"), CCSprite::create("extensions/switch-off.png"), CCSprite::create("extensions/switch-thumb.png"), CCLabelTTF::create("On", "Arial-BoldMT", 16), CCLabelTTF::create("Off", "Arial-BoldMT", 16) ); switchControl->setPosition(ccp (layer_width + 10 + switchControl->getContentSize().width / 2, 0)); layer->addChild(switchControl); switchControl->addTargetWithActionForControlEvents(this, cccontrol_selector(CCControlSwitchTest::valueChanged), CCControlEventValueChanged); //定义其他控件,代码省略 ... return true; } return false; }

    要定义开关对象,首先调用create函数,参数为图片路径和上面的文字标签。图片路径分别是背景图片路径、开状态背景图片路径、关状态背景图片路径和开关背景图片路径,文字标签是开文字标签、关文字标签。设置位置加入布景层后定义回调函数。运行效果如图3-21所示。

    4 . 按钮类CCControlButton 按钮类CCControlButton的定义和初始化如代码清单3-28所示。代码是tests项目中ControlExtensionTest CCControlButtonTest目录下CCControlButtonTest.cpp中的CCControlButtonTest_Event的init函数。 代码清单3-28 CCControlButton的定义和初始化 bool CCControlButtonTest_Event::init() { if (CCControlScene::init()) { //定义其他控件,代码省略 ... //定义并初始化按钮 CCControlButton *controlButton = CCControlButton::create(titleButton, backgroundButton); controlButton->setBackgroundSpriteForState(backgroundHighlightedButton, CCControlStateHighlighted); controlButton->setTitleColorForState(ccWHITE, CCControlStateHighlighted); controlButton->setAnchorPoint(ccp(0.5f, 1)); controlButton->setPosition(ccp(screenSize.width / 2.0f, screenSize.height / 2.0f)); addChild(controlButton, 1); //定义其他控件,代码省略 ... //加入回调函数 controlButton->addTargetWithActionForControlEvent(this, cccontrol_selector(CCControlButtonTest_Event::touchDownAction), CCControlEventTouchDown); controlButton->addTargetWithActionForControlEvent(this, cccontrol_selector(CCControlButtonTest_Event::touchDragInsideAction), CCControlEventTouchDragInside); controlButton->addTargetWithActionForControlEvent(this, cccontrol_selector(CCControlButtonTest_Event::touchDragOutsideAction), CCControlEventTouchDragOutside); controlButton->addTargetWithActionForControlEvent(this, cccontrol_selector(CCControlButtonTest_Event::touchDragEnterAction), CCControlEventTouchDragEnter); controlButton->addTargetWithActionForControlEvent(this, cccontrol_selector(CCControlButtonTest_Event::touchDragExitAction), CCControlEventTouchDragExit); controlButton->addTargetWithActionForControlEvent(this, cccontrol_selector(CCControlButtonTest_Event::touchUpInsideAction), CCControlEventTouchUpInside); controlButton->addTargetWithActionForControlEvent(this, cccontrol_selector(CCControlButtonTest_Event::touchUpOutsideAction), CCControlEventTouchUpOutside); controlButton->addTargetWithActionForControlEvent(this, cccontrol_selector(CCControlButtonTest_Event::touchCancelAction), CCControlEventTouchCancel); return true; } return false; }

    要定义按钮对象,首先调用create函数。该函数传入的参数可以是图片的路径,也可以是精灵对象。两个参数代表的分别是按钮标题字和背景。之后可以设置按钮的参数,包括设置按钮位置和加入定义的回调函数等。这里的回调函数可以有多个,根据需要的操作定义,包括按下、拖动和抬起等。运行效果如图3-22所示。

    本节介绍了Cocos2D-x的布景层类和它的子类,下节开始介绍精灵类及其子类。

    相关资源:七夕情人节表白HTML源码(两款)

    最新回复(0)