~~~~我的生活,我的点点滴滴!!
1、Layer层
Layer 游戏中的背景容器,Layer类是Node类的一个子类,它实现了触屏事件代理(TouchEventsDelegate)协议。
LayerColor是Layer的一个子类,它实现了RGBAProtocol协议。
LayerGradient是LayerColor的一个子类,它在背景上画渐变效果。
LayerMultiplex类是一个能够使它的子类进行复用的布景层类。
Sprite精灵不能直接放入舞台中,它需要作为Layer的子节点,通过Layer加入舞台场景中显示。
cpp-tests例子中Node::Layer测试了很多Layer及其子类的作用与用法,比如递归设置Cascading与opacity来让在Layer层上面的精灵具有及相应的属
性等等,LayerMultiple类在MenuTest里面讲解,下面讲解一下渐变Layer即LayerGradient。
LayerGradient它实现了LayerColor类的所有功能,还添加了以下这些新功能:
1、渐变方向
2、渐变最终颜色
3、插值模式
颜色沿着给定的向量插在起始颜色和终止颜色之间(从起点开始,到终点结束)。如果没有提供向量,则默认到(0,-1)点一个从顶部到底部的淡入
淡出。
如果“compressedInterpolation”不可用,你将看不到非基本向量的起始颜色和终止颜色;不过无论如何,一个平滑的渐变 (包括终点)最终还是会
呈现出来。
如果“compressedInterpolation”是可用的(默认模式),你将看到渐变的起始颜色和终止颜色。
看下面代码:
LayerGradientTest::LayerGradientTest()
{
//渐变,第一个参数是起始颜色,第二个参数是终止颜色,第三个参数是渐变方向是通过与原点减来求矢量的;
//这里(0.9f,0.9f)与(0,0)计算后的夹角是45度往x轴正方向,也就是向右;
//如果没有第三个参数,则默认到(0,-1)一个从顶部到底部的淡入淡出;
auto layer1 = LayerGradient::create(Color4B(255,0,0,255), Color4B(0,255,0,255), Vec2(0.9f, 0.9f));
addChild(layer1, 0, kTagLayer);
auto listener = EventListenerTouchAllAtOnce::create();
listener->onTouchesMoved = CC_CALLBACK_2(LayerGradientTest::onTouchesMoved, this);
_eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);
auto label1 = Label::createWithTTF("Compressed Interpolation: Enabled", "fonts/Marker Felt.ttf", 26);
auto label2 = Label::createWithTTF("Compressed Interpolation: Disabled", "fonts/Marker Felt.ttf", 26);
auto item1 = MenuItemLabel::create(label1);
auto item2 = MenuItemLabel::create(label2);
//设置一个toggle开关,来演示Compressed,本人目测效果不明显;
auto item = MenuItemToggle::createWithCallback( CC_CALLBACK_1(LayerGradientTest::toggleItem, this), item1, item2, nullptr);
auto menu = Menu::create(item, nullptr);
addChild(menu);
auto s = Director::getInstance()->getWinSize();
menu->setPosition(Vec2(s.width / 2, 100));
}
void LayerGradientTest::toggleItem(Ref *sender)
{
auto gradient = static_cast<LayerGradient*>( getChildByTag(kTagLayer) );
gradient->setCompressedInterpolation(! gradient->isCompressedInterpolation());
}
void LayerGradientTest::onTouchesMoved(const std::vector<Touch*>& touches, Event *event)
{
auto s = Director::getInstance()->getWinSize();
auto touch = touches[0];
auto start = touch->getLocation();
auto diff = Vec2(s.width/2,s.height/2) - start;
diff = diff.getNormalized();
auto gradient = static_cast<LayerGradient*>( getChildByTag(1) );
//设置一个移动矢量方向
gradient->setVector(diff);
}
上面代码会产生一个从左下角红色慢慢变到右上角绿色的Layer层,当鼠标点击移动后,根顺着鼠标移动方向切换渐变颜色位置。
效果图:
2、Menu菜单
菜单按钮有好多了,像MenuItemSprite、MenuItemFont、MenuItemLabel、MenuItemImage、MenuItemToggle等等,都是大同小异,直接看后缀也大概
知道分别代表什么意思,怎么使用了。
下面代码列出了上面的使用方法:
MenuLayerMainMenu::MenuLayerMainMenu()
{
_touchListener = EventListenerTouchOneByOne::create();
_touchListener->setSwallowTouches(true);
_touchListener->onTouchBegan = CC_CALLBACK_2(MenuLayerMainMenu::onTouchBegan, this);
_touchListener->onTouchMoved = CC_CALLBACK_2(MenuLayerMainMenu::onTouchMoved, this);
_touchListener->onTouchEnded = CC_CALLBACK_2(MenuLayerMainMenu::onTouchEnded, this);
_touchListener->onTouchCancelled = CC_CALLBACK_2(MenuLayerMainMenu::onTouchCancelled, this);
//注意这里使用的是FixedPriority而不是SceneGraphPriority;
//FixedPriority会依据手动设置的值的顺序来决定触摸的优先级,但是不能为0;
//0已经留给scene了
_eventDispatcher->addEventListenerWithFixedPriority(_touchListener, 1);
// Font Item
// 这里其实是一张图片有三个等分小图片,通过计算其位置来获得图片,plist文件里面其实也是这样的;
// 这里的Rect值算的有问题,x,y坐标要缩小一倍,width与height也要缩小一倍;
// 要不然显示出来的就是一个空白黄色立方形;
auto spriteNormal = Sprite::create(s_MenuItem, Rect(0,23*0.5*2,115*0.5,23*0.5));
auto spriteSelected = Sprite::create(s_MenuItem, Rect(0,23*0.5*1,115*0.5,23*0.5));
auto spriteDisabled = Sprite::create(s_MenuItem, Rect(0,23*0.5*0,115*0.5,23*0.5));
auto item1 = MenuItemSprite::create(spriteNormal, spriteSelected, spriteDisabled , CC_CALLBACK_1(MenuLayerMainMenu::menuCallback, this) );
// Image Item
auto item2 = MenuItemImage::create(s_SendScore, s_PressSendScore, CC_CALLBACK_1(MenuLayerMainMenu::menuCallback2, this) );
// Label Item (LabelAtlas)
auto labelAtlas = LabelAtlas::create("0123456789", "fonts/labelatlas.png", 16, 24, '.');
auto item3 = MenuItemLabel::create(labelAtlas, CC_CALLBACK_1(MenuLayerMainMenu::menuCallbackDisabled, this) );
/*设置一层覆盖颜色,上一行代码给item3设置了一个触摸回调函数,后面代码;
_disabledItem = item3;
item3->retain();
_disabledItem->setEnabled( false );
把_disabledItem指向item3并且设置setEnabled为false让其不可用状态;
这样相当于在item3上面覆盖一层,这样触摸就可控制了;
为什么不直接使用item3????;;;;
*/
item3->setDisabledColor( Color3B(232,0,0) );
//设置字体颜色;
item3->setColor( Color3B(200,200,255) );
// Font Item
auto item4 = MenuItemFont::create("I toggle enable items", [&](Ref *sender) {
//设置是否可用;
_disabledItem->setEnabled(! _disabledItem->isEnabled() );
//此处能不能直接用item3了????????;
//当然可以呀,但是需要把item3设置为成员变量,这样在lambda表达式中就能捕获到;
//item3->setEnabled( !item3->isEnabled() );
});
item4->setFontSizeObj(20);
item4->setFontName("fonts/Marker Felt.ttf");
// Label Item (LabelBMFont)
auto label = Label::createWithBMFont("fonts/bitmapFontTest3.fnt", "configuration");
auto item5 = MenuItemLabel::create(label, CC_CALLBACK_1(MenuLayerMainMenu::menuCallbackConfig, this));
// Testing issue #500
item5->setScale( 0.8f );
// Events
MenuItemFont::setFontName("fonts/Marker Felt.ttf");
// Bugs Item
auto item6 = MenuItemFont::create("Bugs", CC_CALLBACK_1(MenuLayerMainMenu::menuCallbackBugsTest, this));
// Font Item
auto item7= MenuItemFont::create("Quit", CC_CALLBACK_1(MenuLayerMainMenu::onQuit, this));
auto item8 = MenuItemFont::create("Remove menu item when moving", CC_CALLBACK_1(MenuLayerMainMenu::menuMovingCallback, this));
//设置item7 "Quit"一闪一闪效果;
auto color_action = TintBy::create(0.5f, 0, -255, -255);
auto color_back = color_action->reverse();
auto seq = Sequence::create(color_action, color_back, nullptr);
item7->runAction(RepeatForever::create(seq));
auto menu = Menu::create( item1, item2, item3, item4, item5, item6, item7, item8, nullptr);
menu->alignItemsVertically();
//设置出场时动态效果;
// elastic effect
auto s = Director::getInstance()->getWinSize();
int i=0;
for(const auto &child : menu->getChildren()) {
//这里得到的是相应Menu的位置,其值是很小的,x轴基本上都是0,y轴从正到负(竖直排列从上往下);
auto dstPoint = child->getPosition();
int offset = (int) (s.width/2 + 50);
if( i % 2 == 0)
offset = -offset;
child->setPosition( Vec2( dstPoint.x + offset, dstPoint.y) );
//EaseElasticOut效果
child->runAction(
EaseElasticOut::create(MoveBy::create(2, Vec2(dstPoint.x - offset,0)), 0.35f)
);
i++;
}
_disabledItem = item3;
item3->retain();
_disabledItem->setEnabled( false );
addChild(menu);
menu->setPosition(Vec2(s.width/2, s.height/2));
menu->setScale(0);
//效果图从右上角出来时特别小,然后慢慢变大,通过ScaleTo实现这样就造成了从远到近的效果;
menu->runAction(ScaleTo::create(1,1));
}
bool MenuLayerMainMenu::onTouchBegan(Touch *touch, Event * event)
{
return true;
}
void MenuLayerMainMenu::onTouchEnded(Touch *touch, Event * event)
{
}
void MenuLayerMainMenu::onTouchCancelled(Touch *touch, Event * event)
{
}
void MenuLayerMainMenu::onTouchMoved(Touch *touch, Event * event)
{
}
MenuLayerMainMenu::~MenuLayerMainMenu()
{
//FixedPriority需要手动释放监听
_eventDispatcher->removeEventListener(_touchListener);
_disabledItem->release();
}
void MenuLayerMainMenu::menuCallback(Ref* sender)
{
//LayerMultiplex是一个能够使它的子类进行复用的布景层类;
//它支持一个或多个子类,但是一次仅能激活一个孩子;
//下标从0开始;
static_cast<LayerMultiplex*>(_parent)->switchTo(1);
}
void MenuLayerMainMenu::menuCallbackConfig(Ref* sender)
{
static_cast<LayerMultiplex*>(_parent)->switchTo(3);
}
void MenuLayerMainMenu::allowTouches(float dt)
{
//还原;
_eventDispatcher->setPriority(_touchListener, 1);
//取消了所有定时器,不过对Actions的动作特效没有影响;
//这里其实只要取消那个5s的定时器就好了;
unscheduleAllSelectors();
log("TOUCHES ALLOWED AGAIN");
}
void MenuLayerMainMenu::menuCallbackDisabled(Ref* sender)
{
// hijack all touch events for 5 seconds
//当前的zorder是0,这里设置成-1,他的优先级高;
//所以这个时候_touchListener的四个函数touchBegan、touchMoved、TouchEnded、TouchCancelled优先被触发;
//而这四个函数里面什么也没有干,只是把touchBegan返回true(一定要返回true不然就会往下传递了,因为我们设置了setSwallowTouches(true));
//这样就形成了一个天然的触摸屏蔽了;
_eventDispatcher->setPriority(_touchListener, -1);
//5秒过后,在allowTouches()回调里面在设置回来了;
schedule(schedule_selector(MenuLayerMainMenu::allowTouches), 5.0f);
log("TOUCHES DISABLED FOR 5 SECONDS");
}
看上面的注释,我们能知道怎么屏蔽触摸穿透,怎么又能恢复触摸事件,并且也展示了MenuItem的各种使用方法,大家在用的时候去看下源码接口就知
道怎么使用了,里面那个屏蔽触摸是5s内任务触摸无效,5s后就一切正常,场景出来的画面也很有意思。
效果图:
前面在讲Layer时我们说过LayerMultiplex 会在这里讲解,老实说这个类方法在切换layer时没有任何过渡效果,感觉有点鸡肋,但总还是有他的用处,喜
欢的就用,不喜欢的就不用,他是一个把多个layer层添加在一起的一个容器,他里面是互斥的一次只能激活(显示)一个layer,但是其他的layer里面设置
依然保存,也就是上一次是什么样子的,下一次切换回来时依然是那个样子。
下面看简单的代码:
void MenuTestScene::runThisTest()
{
MenuItemFont::setFontSize(20);
//产生了各种layer层,然后在下面全添加到LayerMultiplex中存放。
auto layer1 = new MenuLayerMainMenu();
auto layer2 = new MenuLayer2();
auto layer3 = new MenuLayer3();
auto layer4 = new MenuLayer4();
auto layer5 = new BugsTest();
auto layer6 = new RemoveMenuItemWhenMove();
/*在这里创建的LayerMultiplex;
一个能够使它的子类进行复用的布景层类。 功能:
它支持一个或多个子类;
一次仅能激活一个孩子;
*/
auto layer = LayerMultiplex::create(layer1, layer2, layer3, layer4, layer5, layer6, nullptr);
addChild(layer, 0);
//?????为什么要手动release
layer1->release();
layer2->release();
layer3->release();
layer4->release();
layer5->release();
layer6->release();
Director::getInstance()->replaceScene(this);
}
这样加完后,中间很多代码像Lyaer2、Layer3、Layer4等代码这里都省略了,大家想看直接去cpp-tests例子下面MenuTest里面去看
效果图如下:
========================================================
当没有切换Layer层的时候,刚开始出来的Layer上"Quit"菜单是有动态效果的,一闪一闪的,但是只要切换一次,在切回来就不会闪了,
也就是说使用LayerMultiplex时会使用运动的actions停止,这只适合于静态layer层,感觉太鸡肋了吧。
========================================================