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("#%02X%02X%02X",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的布景层类和它的子类,下节开始介绍精灵类及其子类。