【Cocos2dx】飘字特效与碰撞检测

飘字特效主要常见与游戏里面的掉血表示伤害输出等。碰撞检测常见于一些横版过关或者跑酷的手游,判断是否有碰到食物,或者什么障碍物等,都是比较常见的功能,下面就用一个小例子,来说明这两个特效如何完成。

如下图所示,有两个按钮,点击/触摸屏幕,则下方的按钮则会跳起来。碰到处于屏幕中央的按钮,则会弹出飘字特效,显示碰撞。

同时,左上角的时刻,这两个按钮是否碰撞,也就是是否交汇,相互接触,相交,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中写这么长的代码。至此,整个程序最终完成。

相关文章

    本文实践自 RayWenderlich、Ali Hafizji 的文章《...
Cocos-code-ide使用入门学习地点:杭州滨江邮箱:appdevzw@1...
第一次開始用手游引擎挺激动!!!进入正题。下载资源1:从C...
    Cocos2d-x是一款强大的基于OpenGLES的跨平台游戏开发...
1.  来源 QuickV3sample项目中的2048样例游戏,以及最近《...
   Cocos2d-x3.x已经支持使用CMake来进行构建了,这里尝试...