cpp-tests Node::Layer及Node::Menu

~~~~我的生活,我的点点滴滴!!



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层,感觉太鸡肋了吧。


========================================================



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值