飘字特效主要常见与游戏里面的掉血表示伤害输出等。碰撞检测常见于一些横版过关或者跑酷的手游,判断是否有碰到食物,或者什么障碍物等,都是比较常见的功能,下面就用一个小例子,来说明这两个特效如何完成。
如下图所示,有两个按钮,点击/触摸屏幕,则下方的按钮则会跳起来。碰到处于屏幕中央的按钮,则会弹出飘字特效,显示碰撞。
同时,左上角的时刻,这两个按钮是否碰撞,也就是是否交汇,相互接触,相交,intersection就是这个意思。
0、首先利用(cocos2d-x-2.2.6安装目录).\tools\project-creator下的create_project.py创建一个FlowWords的Cocos2dx工程,之后打开其中的proj.win32中的HelloCpp.sln利用vs2010进行编辑,先在AppDelegate.h关闭调试信息,设置窗口大小。这些简单的创建Cocos2dx工程步骤,我就不再赘述了。详情可以参考《【Cocos2dx】Windows平台下Cocos2dx 2.x的下载、安装、配置,打造自己的Helloworld》(点击打开链接)
1、之后新建一个飘字特效的类,包含FlowWord.h与FlowWord.cpp,工程的目录如下图所示:
飘字特效,本质其实就是一个标签文本CCLabelTTF的放大->移动->缩小的动作序列,没什么大不了,关于Cocos2dx的动作序列具体可以参考《【Cocos2dx】基本动作、动作序列与动作合并》(点击打开链接),FlowWord.h代码如下,主要用于一些函数的声明,showWord是暴露给其它类调用的方法,private是完成showWord中的一些必须的类成员变量与类成员函数,这里由于用到了CCLabelTTF,注意引入cocos2d.h的头文字,当然using namespace cocos2d;也就可以的,看上去没什么区别,当然建议使用#include "cocos2d.h",因为你不用在FlowWord.cpp再写一次using namespace cocos2d;了:
#ifndef _FlowWord_H_ #define _FlowWord_H_ #include "cocos2d.h" USING_NS_CC; class FlowWord : public CCNode { public: void showWord(const char* text,CCPoint pos);//飘字方法,text为飘字的内容,pos为飘字的位置 private: CCLabelTTF* label;//类成员 void flowEnd();//飘字结束时的回调(处理)函数,主要用于删除自己 }; #endif之后的FlowWord.cpp则按照如下的逻辑完成一个动作序列,完成飘字特效:
#include "FlowWord.h" void FlowWord::showWord( const char* text,CCPoint position){//text为飘字的内容,pos为飘字的位置 /*初始化*/ label=CCLabelTTF::create(text,"Arial",18);//创建一个字体为Arial,字号为18,内容为text的CCLabelTTF,也就是标签文本 label->setColor(ccc3(255,255,0));//设置其颜色为黄色 label->setPosition(position);//设置其位置 this->addChild(label);//在场景上添加这个标签文本 /*三个动作,放大->移动->缩小*/ CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize();//获取屏幕的尺寸、位置信息等 CCFiniteTimeAction* action1=CCScaleTo::create(0.2f,3.0f,3.0f);//0.2s内在x、y上方向皆放大为原尺寸的3倍 CCFiniteTimeAction* action2=CCMoveTo::create(0.3f,ccp(visibleSize.width/4,3*visibleSize.height/4));//在0.3s内,移动到坐标为(x=屏幕宽度的25%,y=屏幕高度的75%处) CCFiniteTimeAction* action3 = CCScaleTo::create(0.2f,0.1f,0.1f);//之后在0.2s内在x、y上皆缩小为原尺寸的0.1倍 CCCallFunc* callFunc = CCCallFunc::create(this,callfunc_selector(FlowWord::flowEnd));//声明一个回调(处理)函数,为FlowWord类中的flowEnd() CCFiniteTimeAction* action = CCSequence::create(action1,action2,action3,callFunc,NULL);//以上的所有动作组成动作序列action /*对label实行action这个动作*/ label->runAction(action); } void FlowWord::flowEnd() {//动作结束,从父节点中删除自身 label->setVisible(false);//先隐藏显示 label->removeFromParentAndCleanup(true);//再删除 }2、完成了飘字特效的构建,我们要在HelloWorld这个场景中,完成效果图的所有内容。实质是在《【Cocos2dx】精灵触摸跳跃功能》( 点击打开链接)这个工程进一步衍生,在里面多增多一个精灵,再增加一个类似《【Cocos2dx】连续滚动的场景》( 点击打开链接)中的即时更新事件完成碰撞检测的功能。
首先HelloWorldScene.h修改成如下的样子,删去原有的关闭程序的按钮的回调函数,添加一些我们需要的声明:
#ifndef __HELLOWORLD_SCENE_H__ #define __HELLOWORLD_SCENE_H__ #include "cocos2d.h" using namespace cocos2d;//精灵等属于Cocos2dx东西,因此要在此文件的开始声明使用cocos2dx的命名空间,即使有#include "cocos2d.h"也要引用,不知道为什么 class HelloWorld : public cocos2d::CCLayer { public: // Here's a difference. Method 'init' in cocos2d-x returns bool,instead of returning 'id' in cocos2d-iphone virtual bool init(); // there's no 'id' in cpp,so we recommend returning the class instance pointer static cocos2d::CCScene* scene(); // implement the "static node()" method manually CREATE_FUNC(HelloWorld); void ccTouchesBegan(CCSet *pTouches,CCEvent *pEvent);//触摸事件的函数声明 void jumpEnd();//声明跳跃结束的回调函数 virtual void update(float delta);//即时更新事件 private: CCLabelTTF *label;//文字1 CCSprite* sprite1;//精灵1 bool isJumping;//精灵1,是否跳跃的flag CCSprite* sprite2;//精灵2 bool isFlowing;//即时更新事件中,判断此时是否正在飘字的flag }; #endif // __HELLOWORLD_SCENE_H__最后是HelloWorldScene.cpp,也就是整个工程的核心了。
1、将原有的HelloWorldScene.cpp中的初始化函数bool HelloWorld::init(){}中的所有内容删去。我们要在里面声明一个位于左上角标签文本CCLabelTTF与2个按钮精灵。
2、在声明左上角标签文本CCLabelTTF的时候用到setAnchorPoint,这个设置中心点的函数,里面的参数的效果具体如下图所示,以下的按钮精灵的中心店都处于屏幕的中央,然而由于中心点的不同,导致,按钮精灵的位置最终不同。
3、碰撞检测之所以搞这么复杂,要根据中心点与精灵尺寸创建矩形,也是没有办法,这不是单纯的两只的中心点是否相交的问题,比如如下图,两飞机的中心点并没有相交,然而两飞机是相交、已经碰撞的。Cocos2dx自带矩形创建函数已经算给面子了,在原生的JavaScript中,《【JavaScript】黑点捉红点并躲绿点游戏》(点击打开链接)if里面N个无聊的、同质化的不等式写得我想打人。
4、飘字特效必须有个flag控制它,不然在下方的按钮精灵跳跃经过上方的按钮精灵的时候,会疯狂地飘字,极其占有资源,我们仅仅是让其飘一次!
5、其余在注释写明了,个人感觉基本都说明清楚了。
#include "HelloWorldScene.h" #include "FlowWord.h" //飘字特效 USING_NS_CC; CCScene* HelloWorld::scene() { // 'scene' is an autorelease object CCScene *scene = CCScene::create(); // 'layer' is an autorelease object HelloWorld *layer = HelloWorld::create(); // add layer as a child to scene scene->addChild(layer); // return the scene return scene; } // on "init" you need to initialize your instance /*初始化*/ bool HelloWorld::init() { //获取屏幕的尺寸、位置信息等 CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize(); //声明这个场景是存在触摸事件的 this->setTouchEnabled(true); //声明这个场景是存在即时更新事件的 this->scheduleUpdate(); //声明一个表示是否碰撞的文字 label = CCLabelTTF::create("Collision:false","arial",36);//声明文字内容 label->setAnchorPoint(ccp(0,1));//设置label的中心点在左上角,默认是label->setAnchorPoint(ccp(0.5,0.5));中心点在精灵的中央,label->setAnchorPoint(ccp(1,0));中心点则去了右下角了 label->setPosition(ccp(0,visibleSize.height));//把中心点摆在屏幕的左上交 this->addChild(label);//添加此文字到场景中 isFlowing=false; //声明2只精灵 //精灵1 sprite1 = CCSprite::create("CloseSelected.png"); sprite1->setPosition(ccp(visibleSize.width / 2,visibleSize.height / 6)); this->addChild(sprite1); //开始精灵处于未跳跃的状态,由于头文件中“只有静态常量整型数据成员才可以在类中初始化”,因此在这里初始化声明变量 isJumping=false; //精灵2 sprite2 = CCSprite::create("CloseNormal.png"); sprite2->setPosition(ccp(visibleSize.width / 2,visibleSize.height / 2)); this->addChild(sprite2); return true; } /*触摸导致精灵1跳跃*/ //开始触摸 void HelloWorld::ccTouchesBegan(cocos2d::CCSet *pTouches,cocos2d::CCEvent *pEvent){ //获取屏幕的尺寸、位置信息等 CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize(); //如果主角还在跳跃中,则不重复执行 if(isJumping) { return; } //标记主角为跳跃状态 isJumping = true; //在2.0秒内,先跳起 屏幕尺寸的2/3 再落下0px,该动作重复1次 CCJumpBy* jump = CCJumpBy::create(1.5f,ccp(0,0),2* visibleSize.height / 3,1); //创建回调函数,声明跳跃结束后调用jumpEnd函数 CCCallFunc* callFunc = CCCallFunc::create(this,callfunc_selector(HelloWorld::jumpEnd)); //将回调函数与跳跃动作结合起来 CCActionInterval* jumpActions = CCSequence::create(jump,NULL); //执行动作 sprite1->runAction(jumpActions); }; void HelloWorld::jumpEnd() { isJumping = false; } /*碰撞检测*/ //判断两只精灵是否相交的函数 bool is_collision(CCSprite* sprite1,CCSprite* sprite2){ //建立精灵1的矩形 CCSize sprite_size1=sprite1->getContentSize();//求精灵的尺寸 CCPoint sprite_position1=sprite1->getPosition();//求精灵的中心点坐标 CCRect sprite_rect1=CCRectMake(//以精灵的中心点为中心点、以精灵的尺寸为宽与高,建立一个矩形。 sprite_position1.x-sprite_size1.width/2,sprite_position1.y-sprite_size1.height/2,sprite_size1.width,sprite_size1.height); //建立精灵2的矩形,同理 CCSize sprite_size2=sprite2->getContentSize(); CCPoint sprite_position2=sprite2->getPosition(); CCRect sprite_rect2=CCRectMake( sprite_position2.x-sprite_size2.width / 2,sprite_position2.y-sprite_size2.height / 2,sprite_size2.width,sprite_size2.height); return sprite_rect1.intersectsRect(sprite_rect2);//如果是判断矩形与一个像素点则用Rect实例.containsPoint(像素点);这里是判断两个矩形是否相交 } //即时更新事件,就是在程序一开始就不停地执行这个函数 void HelloWorld::update(float delta){//这里的函数名、参数都不能改,即时你没有用到。 if(is_collision(sprite1,sprite2)){//判断精灵1是否碰到精灵2 if(!isFlowing){//如果现在不是在飘字 label->setString("Collision:true");//左上角的文字内容改变 FlowWord* flowWord=new FlowWord();//初始化FlowWord this->addChild(flowWord);//将FlowWord飘字特效放上舞台,尽管飘字特效,一不是精灵,二在showWord中同样有将字体放上舞台的代码,然而没有这一句,HelloWorldScene这个场景根本不会与FlowWord联系起来,也就是FlowWord中添加的Label根本无法在HelloWorldScene中显示。 flowWord->showWord("collision!",sprite2->getPosition());//执行飘字collision!,位置在不动的sprite2位置开始。 isFlowing=true;//表示现在正在飘字 } } else{ label->setString("Collision:false");//精灵1没有碰到精灵2才允许继续飘字 isFlowing=false; } }当然,其实还可以写得更好一些,将精灵1与精灵1本身的触摸跳跃事件打包成一个事件,以致于不用在HelloWorldScene.cpp中写这么长的代码。至此,整个程序最终完成。