我们根据上一次介绍的触屏事件和事件队列等知识来实现触屏的单击,双击,三连击事件。
下图为我们实现的效果图:
单击精灵跳跃一个高度,
双击精灵跳跃的高度比单击的高
三连击精灵跳跃的跟高
好了,开始动手吧。新建项目,删除多余的代码
接着在Classes目录下建立两个文件,这里我就命名为Human了
(在Classes上右键添加新建项,选择头文件,名称Human.h,位置浏览到Classes目录下)
因为要实现精灵能接受触屏事件,所以我们不能用CCSprite ,需要自己写个类继承CCSprite和CCTouchDelegate
还记得CCTouchDelegate类吗??
OK 我们先实现头文件
#ifndef _Human_H_
#include "cocos2d.h"
USING_NS_CC;
class Human:public CCSprite,public CCTouchDelegate
{
public:
virtual bool ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent);
virtual void ccTouchMoved(CCTouch *pTouch, CCEvent *pEvent);
virtual void ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent);
virtual void onEnter();
virtual void onExit();
//创建静态方法,用于创建Human精灵
static Human* create(const char *pszFileName);
private:
};
#endif // !_Human_H_
重写的这5个方法应该不用解释了,上一节介绍过的
(教大家一个小技巧,比如我不记得触屏事件的名称或参数怎么写来着,没事,不用死机硬背的,鼠标放在CCTouchDelegate上F12,就可以看到它们都在,多点,单点等等,你复制一份就哦了。)
接着我们来实现CPP文件
#include "Human.h"
bool Human::ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent)
{
CCLOG("ccTouchBegan");
return true;
}
void Human::ccTouchMoved(CCTouch *pTouch, CCEvent *pEvent)
{
CCLOG("ccTouchMoved");
}
void Human::ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent)
{
CCLOG("ccTouchEnded");
}
void Human::onEnter()
{
//继承,重载方法的步骤我们实现了,接下来就是注册
//第一步:将我们需要处理触屏事件的类添加到事件队列中dispatcher
CCSprite::onEnter();
CCTouchDispatcher *dispatcher= CCDirector::sharedDirector()->getTouchDispatcher();
dispatcher->addTargetedDelegate(this,0,true);
}
void Human::onExit()
{
//第二步:卸载
CCTouchDispatcher *dispatcher= CCDirector::sharedDirector()->getTouchDispatcher();
dispatcher->removeDelegate(this);
}
Human* Human::create(const char *pszFileName)
{
Human *pobSprite = new Human();
if (pobSprite && pobSprite->initWithFile(pszFileName))
{
pobSprite->autorelease();
return pobSprite;
}
CC_SAFE_DELETE(pobSprite);
return NULL;
}
这里注释很详细了,也许大家写不出create方法里面的代码吧?写不出没关系,看得懂就行了,以后熟练了就可以写出来了,我们找到CCSprite中的create方法的实现代码。照着写就行了,主要将
CCSprite *pobSprite = new CCSprite();
改为
Human *pobSprite = new Human();
成功了?那接着
bool HelloWorld::init()
{
//
// 1. super init first
if ( !CCLayer::init() )
{
return false;
}
Human *humanSprite=Human::create("Icon.png");
humanSprite->setPosition(ccp(240,30));
this->addChild(humanSprite);
return true;
}
这里不是CCSprite了,是我们自定义的精灵,它可以接受触屏事件的响应
OK 运行,点击屏幕,看输出窗口
有个小问题,不一定点击精灵才触发事件,点击屏幕的任何地方都会触发事件,这不是我们想要的结果。
我们需要的是只有触摸点在精灵图片的范围内才触发事件
OK,我们在新增一个方法 containsTouchLocation
//判断触摸点是否在精灵范围内
bool containsTouchLocation(CCTouch *pTouch);
实现
#ifndef _Human_H_
#include "cocos2d.h"
USING_NS_CC;
class Human:public CCSprite,public CCTouchDelegate
{
public:
virtual bool ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent);
virtual void ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent);
virtual void onEnter();
virtual void onExit();
//创建静态方法,用于创建Human精灵
static Human* create(const char *pszFileName);
//判断触摸点是否在精灵范围内
bool containsTouchLocation(CCTouch *pTouch);
private:
};
#endif // !_Human_H_
CPP
#include "Human.h"
bool Human::ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent)
{
return true;
}
void Human::ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent)
{
if (containsTouchLocation(pTouch))
{
CCLOG("ccTouchEnded");
}
}
bool Human::containsTouchLocation(CCTouch *pTouch)
{
//首先需要获得精灵的大小,将它转换为一个CCRect(矩形)
CCSize spriteSize= this->getContentSize();
CCRect rect=CCRectMake(-spriteSize.width/2,-spriteSize.height/2,spriteSize.width,spriteSize.height);
//通过CCRect中的containsPoint,来判断这个矩形是否包含这个点
//这个点为我们的触摸点
if(!rect.containsPoint(this->convertTouchToNodeSpaceAR(pTouch)))
{
return false;
}
return true;
}
void Human::onEnter()
{
//继承,重载方法的步骤我们实现了,接下来就是注册
//第一步:将我们需要处理触屏事件的类添加到事件队列中dispatcher
CCSprite::onEnter();
CCTouchDispatcher *dispatcher= CCDirector::sharedDirector()->getTouchDispatcher();
dispatcher->addTargetedDelegate(this,0,true);
}
void Human::onExit()
{
//第二步:卸载
CCTouchDispatcher *dispatcher= CCDirector::sharedDirector()->getTouchDispatcher();
dispatcher->removeDelegate(this);
}
Human* Human::create(const char *pszFileName)
{
Human *pobSprite = new Human();
if (pobSprite && pobSprite->initWithFile(pszFileName))
{
pobSprite->autorelease();
return pobSprite;
}
CC_SAFE_DELETE(pobSprite);
return NULL;
}
这里都有注释了,
第一,先获得精灵的大小(ContentSize)将其转换为CCRect(矩形)
第二,利用CCRect中的containsPoint方法来判断这个矩形是否包含这个点,包含返回true否则反之
(这里需要把触摸点的位置转换为相对当前精灵节点的相对位置
我们介绍坐标系统的时候貌似只提到过UIKIT坐标,OPGL坐标,其实还有个相对坐标和世界坐标
我们在讲完这个实列之后再介绍,现在先这样写着)
这里还有一句
CCRect rect=CCRectMake(-spriteSize.width/2,-spriteSize.height/2,spriteSize.width,spriteSize.height);
很纳闷为什么是 -spriteSize.width/2
刚开始我也很郁闷,根本看不懂,后来直接盯着这段代码不眨眼盯了半小时,瞬间懂了,
我们学习坐标系统都知道默认锚点是在中心点吧?精灵也是
我们得求出左下角点的x和y坐标,然后后面两个参数都懂的
假设精灵是40X40的图片,锚点在中心,所以中心点为0,0坐标 然后四个角的坐标如上图。
现在我们要的出左下角的坐标,瞬间懂了吗??
现在我们差不多完成一半了,其实我们在CCTouchEnded方法中完成我们的跳跃效果,但是这只是单击跳跃,多连击还是得我们自己实现
#ifndef _Human_H_
#include "cocos2d.h"
USING_NS_CC;
class Human:public CCSprite,public CCTouchDelegate
{
public:
virtual bool ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent);
virtual void ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent);
virtual void onEnter();
virtual void onExit();
//创建静态方法,用于创建Human精灵
static Human* create(const char *pszFileName);
//判断触摸点是否在精灵范围内
bool containsTouchLocation(CCTouch *pTouch);
//构造方法,初始化变量
Human();
//单击调用
void SignleClicked(float dt);
//双击
void DoubleClicked(float dt);
//三连击
void ThreeClicked();
private:
//记录点击的次数
int clickedCount;
};
#endif // !_Human_H_
这里增加了些方法
//构造方法,初始化变量
Human::Human():clickedCount(0)
{}
//单击调用
void Human::SignleClicked(float dt){
if (clickedCount==1)
{
clickedCount=0;
CCActionInterval* jumpto= CCJumpBy::create(1.4f,CCPointZero,50,1);
this->runAction(jumpto);
}
}
//双击
void Human::DoubleClicked(float dt){
if (clickedCount==2)
{
clickedCount=0;
CCActionInterval* jumpto= CCJumpBy::create(1.4f,CCPointZero,120,1);
this->runAction(jumpto);
}
}
//三连击
void Human::ThreeClicked(){
clickedCount=0;
CCActionInterval* jumpto= CCJumpBy::create(1.4f,CCPointZero,220,1);
this->runAction(jumpto);
}
方法的实现大致是这样,判断变量的值,如果是1则执行单击跳跃高度为50,后面的不说了
接着我们在ccTouchEnded函数中用延迟的一个方法来实现各种点击
void Human::ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent)
{
if (!containsTouchLocation(pTouch))
{
return;
}
//如果点击在精灵上,则执行
if (clickedCount==2)
{
ThreeClicked();
}
else if (clickedCount==1)
{
//延迟0.25秒执行双击
scheduleOnce(schedule_selector(Human::DoubleClicked),0.25f);
clickedCount++;
}
else
{
//延迟0.25秒执行单击
scheduleOnce(schedule_selector(Human::SignleClicked),0.25f);
clickedCount++;
}
}
这里逻辑有点转不过来,
假设,我们点击一次,它将会执行最后的else ,而单击的方法要在0.25秒后才执行但是此时变量的值已经为1了
然后我们在0.25秒的时间内在继续点击,它将执行第二个IF中的语句,同理,双击的方法也要在0.25秒后执行,但是此时变量的值已经是2了
这个大家多断点,自己多想想,说不清楚
OK,截止我们的多连击已经搞定了
总结一下:
首先,我们学会了如何制定自己的精灵
如何使自己的精灵能处理触屏事件
理解如何完成处理触屏事件的几个步骤
如何判断触摸点是否是点击在指定的矩形区域中,这个很重要哦
利用计时器延迟巧妙实现处理多连击事件
(还有哪些不懂的代码,自己可以先baidu一下)