1.LayerColor做遮罩,ClippingNode做亮点区域裁剪
2.剪裁了一个区域,但是只想传递点击这个区域的触摸事件到下层
//只有触摸在圆形区域才可以向下传递触摸时间
auto touchListener = EventListenerTouchOneByOne::create();
touchListener->onTouchBegan = CC_CALLBACK_2(CircleShelterLayer::onTouchBegan, this);
touchListener->onTouchBegan = [=](Touch *touch, Event *event){
Point point = touch->getLocation();
float distance = point.getDistance(pos);
if (distance > radius)
{
touchListener->setSwallowTouches(true);
return true;
}
else
{
touchListener->setSwallowTouches(false);
}
return true;
};
3.
clippingNode:
http://www.zaojiahua.com/ccclipping.html
CCClippingNode做个新手引导:
http://www.zaojiahua.com/beginner-guide.html
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1.ClippingNode
CCClippingNode在做新手引导的时候经常用到,所以要想做个新手引导就得先学学这个,当然它的功能不会仅限于此,明白了原理以后你可以自由发挥啊。在网上看到不少的文章,但是说的不够明白,我今天好好给大家说说吧,这里只说下原理,下次利用这个做个新手引导出来。
需要明确的一点就是CCClippingNode本质上是一个节点,什么是节点,节点就是可以存放东西,它也可以放到其他的节点上。大家在新手引导中看到的画面,大多数是一些背景层什么的,这个CCClippingNode也是放到了场景中的,只不过zOrder是靠前的。我么需要完成的这些个效果都是在这个层中实现的,不要被搞乱了。首先我们在这个node层中添加一些我们的元素,当然根据你自己的情况了,基本上都是一些精灵,那个看起来透明的遮罩层就是添加到这个node中的节点。接着,最重要的一点是这个node有一个模板节点,什么是模板节点呢,就是一个模子,我们要按照这个模子来裁剪我们的这个层,道理就像我们用剪刀按照一个模子来减一块布一样,我们最后可以设置我们是要留下这个模子裁剪出来的区域还是要留下这个模子剩下来的区域,CCClippingNode有专门的函数来设置这个模子。下面看看代码中怎么实现吧,具体的函数调用也加了注释,很好懂的。
bool HelloWorld::init()
{
if ( !CCLayer::init() )
{
return false;
}
CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize();
//背景图片
CCSprite * background = CCSprite::create("HelloWorld.png");
background->setPosition(ccp(visibleSize.width/2,visibleSize.height/2));
this->addChild(background,0);
//创建一个裁剪节点用来实现遮罩的效果
CCClippingNode * clippingNode = CCClippingNode::create();
this->addChild(clippingNode,1);
//向裁剪节点中加入内容,这里加入的是一个透明的层
CCLayerColor * layer = CCLayerColor::create(ccc4(0,0,0,200));
clippingNode->addChild(layer);
//继续向裁剪节点中加入内容,这里加入的是一个精灵
CCSprite * sprite = CCSprite::create("1.png");
sprite->setPosition(ccp(visibleSize.width/4,visibleSize.height/2));
clippingNode->addChild(sprite);
//向裁剪节点中加入精灵,精灵的位置和裁剪的位置相同,所以最后让裁剪掉了
CCSprite * sprite2 = CCSprite::create("icon.png");
sprite2->setPosition(ccp(visibleSize.width/2,visibleSize.height/2));
clippingNode->addChild(sprite2);
//创建模板,裁剪节点将按照这个模板来裁剪区域
CCSprite * stencil = CCSprite::create("icon.png");
stencil->setPosition(ccp(visibleSize.width/2,visibleSize.height/2));
clippingNode-><strong>setStencil</strong>(stencil);
//这个是用来设置显示裁剪区域还是非裁剪区域的
clippingNode-><strong>setInverted</strong>(true);
//我们之前放了一张裁剪的模板,按照这个模板裁剪的时候同时按照这个alpha的值裁剪,这个值的范围是0-1
//设为0就把透明的区域裁剪掉了
//clippingNode-><strong>setAlphaThreshold</strong>(0);
return true;
}
你看到的上边的俩种图片就是通过setInverted这个函数来设置的,它可以设置你最终看到的是裁剪掉的区域还是模板的区域,我想原理你应该明白了吧。下面我们再来看一下init函数中最后注释掉的那行代码,意思注释已经写了,打开这句话我们看到的效果如下。
有了上面的基础我们应该想想怎么去做这个新手引导了,个人认为这个CCClippingNode节点上只应该添加一个CCLayreColor,设置一下它的透明度,看起来就有了遮罩的效果了,而其他的元素都是在其他的层中的,然后我们为CCClippingNode设置一个模板,同时设置setAlphaThreshold这句话,将透明度为0的都裁剪掉,这样就留下了精灵的形状,然后做一下触摸操作的处理,应该是这样吧。下面这幅图就是这样做的。是不是有了新手引导的效果了!
http://www.zaojiahua.com/ccclipping.html
===================================================================================================================================
今天花了一下午的时间做了个新手引导,用到的知识就是上篇的博客CCClippingNode遮罩解析,还不明白CCClippingNode是怎么回事的就读一下上篇文章吧。先说一下整体的思路吧,游戏正常的界面代码都不改动,逻辑还是原来的逻辑,我们只是在正常的场景上加上一个层,这个层来负责完成新手引导的功能,使用完这个层的时候就将其从场景中清除掉。需要解决的一个重要的问题就是触摸,我们要让我们后添加的这个层注册触摸事件,当点击非模板区域的时候,将触摸事件吞噬掉,这样的话下层就接受不到了,给用户点击错误的感官,当点击了模板区域的时候我们记录一下当前引导的步数,然后向下层传递,下层按他们自己的逻辑处理触摸。最后完成了所有的引导步数后,将注册的触摸事件清除,将引导层清除。下面先看看我们要为哪个场景添加引导层,没添加之前的效果,具体代码实现如下。
bool HelloWorld::init()
{
if ( !CCLayer::init() )
{
return false;
}
CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize();
//添加背景图片
CCSprite * background = CCSprite::create("background.png");
background->setPosition(ccp(visibleSize.width/2,visibleSize.height/2));
this->addChild(background);
//添加如下的俩个按钮
CCControlButton * first = this->addButton("first",ccp(visibleSize.width/4,visibleSize.height/3));
//为按钮添加响应事件
first->addTargetWithActionForControlEvents(this,
cccontrol_selector(HelloWorld::first),CCControlEventTouchDown);
CCControlButton * second = this->addButton("second",ccp(visibleSize.width*3/4,visibleSize.height/3));
second->addTargetWithActionForControlEvents(this,
cccontrol_selector(HelloWorld::second),CCControlEventTouchDown);
//添加一个精灵,点击精灵的时候跟随手指移动
this->m_sprite = CCSprite::create("sprite.png");
m_sprite->setPosition(ccp(visibleSize.width/2,visibleSize.height/2));
this->addChild(m_sprite);
//添加新手引导层,到时候这里可以做一个判断,如果玩家首次完游戏就添加进这个新手引导,否则不添加
BeginnerGuide * gudie = BeginnerGuide::create();
this->addChild(gudie);
//开启触摸
this->setTouchEnabled(true);
return true;
}
//向helloworld层中添加按钮
CCControlButton * HelloWorld::addButton(std::string str,CCPoint point)
{
//创建俩张九妹图片做为底图
CCScale9Sprite * normalButton = CCScale9Sprite::create("buttonBackground.png");
CCScale9Sprite * selectedButton = CCScale9Sprite::create("buttonHighlighted.png");
CCLabelTTF * label = CCLabelTTF::create(str.c_str(),"",30);
CCControlButton * button = CCControlButton::create(label,normalButton);
//设置按钮被选中时候的九图
button->setBackgroundSpriteForState(selectedButton,CCControlStateSelected);
button->setPosition(point);
this->addChild(button);
return button;
}
//单击按钮时候的事件响应函数
void HelloWorld::first(CCObject * object,CCControlEvent evt)
{
CCLabelTTF * text = CCLabelTTF::create("first button clicked!","",24);
text->setPosition(ccp(400,400));
text->setColor(ccc3(0,255,255));
this->addChild(text);
}
//同上
void HelloWorld::second(CCObject * object,CCControlEvent evt)
{
CCLabelTTF * text = CCLabelTTF::create("second button clicked!","",24);
text->setPosition(ccp(400,200));
text->setColor(ccc3(0,255,255));
this->addChild(text);
}
//注册触摸
void HelloWorld::registerWithTouchDispatcher()
{
CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this,0,true);
}
//以下俩个函数实现拖动精灵移动的效果
bool HelloWorld::<strong>ccTouchBegan</strong>(CCTouch * touch,CCEvent * evt)
{
//获得手指点击的点的坐标
CCPoint point = touch->getLocation();
//获得精灵所在的区域,CCRect包括x,y,width,height
CCRect rect = this->m_sprite->boundingBox();
//判断手指点击的点是否点在了精灵上
if(rect.<strong>containsPoint</strong>(point))
{
//返回true则会接受其他的协议消息
return true;
}
return false;
}
void HelloWorld::ccTouchMoved(CCTouch * touch,CCEvent * evt)
{
//分别获得了手指现在的点击点和手指上次的点击点位置
CCPoint point = touch->getLocation();
CCPoint pointPre = touch->getPreviousLocation();
//ccpSub将俩个点相减,获得一个移动方向的向量
CCPoint direction = ccpSub(point,pointPre);
CCPoint spritePoint = m_sprite->getPosition();
//ccpAdd将精灵现在的位置点和移动方向的向量相加,获得精灵将要移动到的位置点
CCPoint spriteDirection = ccpAdd(spritePoint,direction);
m_sprite->setPosition(spriteDirection);
}
#ifndef __BEGINNER_GUIDE_SCENE_H__
#define __BEGINNER_GUIDE_SCENE_H__
#include "cocos2d.h"
using namespace cocos2d;
//创建枚举类型,记录用户单击到多少步
enum STEP
{
STEP_FIRST,
STEP_SECOND,
STEP_THIRD,
STEP_FORTH //最后一步,有特殊作用
};
class BeginnerGuide : public cocos2d::CCLayer
{
public:
virtual bool init();
static cocos2d::CCScene* scene();
CREATE_FUNC(BeginnerGuide);
//添加触摸事件
void registerWithTouchDispatcher();
bool ccTouchBegan(CCTouch * touch,CCEvent * evt);
//设置当前新手引导的步数,根据不同的步数使用不同的模板和坐标
void setStep(enum STEP);
//当新手引导退出的时候在onExit()中处理一些事情
void onExit();
private:
CCSprite * m_sprite;
//裁剪节点
CCClippingNode * clippingNode;
CCSize visibleSize;
//记录当前的步数
enum STEP m_step;
//创建一个手型精灵用来指导用户点击
CCSprite * m_hand;
};
#endif
bool BeginnerGuide::init()
{
if ( !CCLayer::init() )
{
return false;
}
visibleSize = CCDirector::sharedDirector()->getVisibleSize();
//创建一个遮罩层
CCLayerColor * layer = CCLayerColor::create(ccc4(0,0,0,150));
//创建一个裁剪节点
clippingNode = CCClippingNode::create();
this->addChild(clippingNode);
//设置一些属性
clippingNode->addChild(layer);
clippingNode->setInverted(true);
clippingNode->setAlphaThreshold(0);
//创建一个手型精灵用来指导用户点击
this->m_hand = CCSprite::create("hand.png");
this->addChild(m_hand);
//设置当前新手引导为第一步
this->m_step = STEP_FIRST;
this->setStep(m_step);
//开启触摸
this->setTouchEnabled(true);
return true;
}
void BeginnerGuide::registerWithTouchDispatcher()
{
//CCMenu的优先级是-128,而CCControlButton的优先级是0,这里的优先级至少要和helloWorld场景层中的
//优先级一样大,也可以设置为0,因为新手引导层是最后添加上去的,所以最先收到触摸的消息
CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this,0,true);
}
bool BeginnerGuide::ccTouchBegan(CCTouch * touch,CCEvent * evt)
{
//获取当前的模板
<strong>CCNode * stencil = clippingNode->getStencil();
CCRect rect = stencil->boundingBox();
CCPoint point = touch->getLocation();</strong>
//判断手指点击的位置是否为模板的位置
if(rect.containsPoint(point))
{
if(m_step == STEP_FORTH)
{
//this->m_sprite->setVisible(false);
this->removeFromParent();
return false;
}
//改变步骤
this->setStep((enum STEP)((int)m_step+1));
return false;
}
return true;
}
//设置当前新手引导的步骤,根据不同的步骤使用不同的模板和坐标
void BeginnerGuide::setStep(enum STEP step)
{
CCNode * stencil = NULL;
//创建手型精灵的动作,一下动作写的相对的恶心,就是为了实现效果,大家就别看了
CCScaleTo * scale1 = CCScaleTo::create(0.8f,0.8f);
CCScaleTo * scale2 = CCScaleTo::create(0.8f,0.7f);
CCSequence * sequence = CCSequence::create(scale1,scale2,NULL);
CCRepeatForever * repeat = CCRepeatForever::create(sequence);
CCMoveTo * move1 = CCMoveTo::create(3.0f,ccp(visibleSize.width*4/5,visibleSize.height/4));
CCRepeat * repeat1 = CCRepeat::create(sequence,50);
CCSequence * second = CCSequence::create(move1,repeat1,NULL);
CCMoveTo * move2 = CCMoveTo::create(2.0f,ccp(visibleSize.width*0.55,visibleSize.height/3));
CCRepeat * repeat2 = CCRepeat::create(sequence,2);
CCMoveTo * move2_2 = CCMoveTo::create(2.0f,ccp(visibleSize.width/3,visibleSize.height/3));
CCSequence * third = CCSequence::create(move2,repeat2,move2_2,NULL);
m_hand->stopAllActions();
switch(step)
{
case STEP_FIRST:
//设置模板和模板的位置
stencil = CCSprite::create("1.png");
stencil->setPosition(ccp(visibleSize.width/4,visibleSize.height/3));
//手型精灵执行动作
this->m_hand->runAction(repeat);
m_hand->setPosition(ccp(visibleSize.width/3,visibleSize.height/4));
break;
case STEP_SECOND:
//与上边的相同
stencil = CCSprite::create("2.png");
stencil->setPosition(ccp(visibleSize.width*3/4,visibleSize.height/3));
this->m_step = STEP_SECOND;
//手型精灵执行动作
this->m_hand->runAction(second);
break;
case STEP_THIRD:
this->m_sprite = CCSprite::create("sprite.png");
stencil = m_sprite;
stencil->setPosition(ccp(visibleSize.width/2,visibleSize.height/2));
this->m_step = STEP_THIRD;
//手型精灵执行动作
this->m_hand->runAction(third);
break;
case STEP_FORTH:
this->m_sprite = CCSprite::create("sprite.png");
this->m_sprite->setPosition(ccp(visibleSize.width/3,visibleSize.height/2));
stencil = m_sprite;
this->m_step = STEP_FORTH;
break;
}
//添加模板
clippingNode->setStencil(stencil);
}
//将当前新手引导层从父节点上移除的时候记住要remove掉触摸
void BeginnerGuide::onExit()
{
CCLayer::onExit();
CCDirector::sharedDirector()->getTouchDispatcher()->removeDelegate(this);
}
效果如图所示,当然程序还是有bug的,比如用户快速点击的时候动作能否跟的上,最主要的问题是当用户点击了最后一步的时候如何进行判断用户是否点击完成,在我的程序中,我需要用户再次点击一下才能退出新手引导。还有就是如果用户没有按照要求点击的话,CCClippingNode的模板应该回到上一步的位置处才对,这里没有实现,不过项目中用的时候就需要根据你自己的需要去做了,这里只是为了说明原理。