cocos2d-x学习笔记(一)塔防类游戏道具系统开发(上)定时炸弹和轰炸的实现

ufolr原创,转载请注明:

转载自ufolr的博客 原文连接:http://blog.csdn.net/ufolr/article/details/7300545


基于cocos2d的一款塔防游戏,需要一个道具系统,需求如下

我们来一一解决。

一、无消耗道具。

二、轰炸。

三、时间停止。

四、定时炸弹。

下面就切入正题,开始累码。

期望达到目的:直接加载相应工程文件在主程序上做尽量小的改动实现我们的系统。

    所以,单独给道具开一个类,单独给道具一个layer。

于是,在游戏主scene中添加我们的道具的layer:

在我们的Gamescene或者HelloWorld的CCScene* HelloWorld::scene()中加入:

			Daoju *daoju = Daoju::node();
			scene->addChild(daoju);

然后在Daoju.h中声明我们的道具类:

#ifndef __DAOJU_H__
#define __DAOJU_H__

#include "cocos2d.h"

using namespace cocos2d;
//long millisecondNow() ;
class Daoju :public cocos2d::CCLayer{
public:
	virtual bool init();

	//点击按钮触发事件
	void ChufaZhadan(CCObject* pSender);//触发炸弹事件
	void ChufaBombs(CCObject* pSender);//触发轰炸事件
	//重写触摸相关虚函数
	virtual void ccTouchesBegan(CCSet *pTouches, CCEvent *pEvent);
	//炸弹安放点坐标
	cocos2d::CCPoint touchPoint;
	//播放炸弹及轰炸效果的动画的函数
	bool ZhadanAnimate(float zx, float zy);
	bool BombsAnimate(float bx, float by);
	//回调函数,这里用于在动画播放完后clean屏幕
	void animBombsOverCallBack();
	void animZhadanOverCallBack();
	LAYER_NODE_FUNC(Daoju);
};
#endif//__DAOJU__
接着就开始分别解决我们的一切需要,当然不包括生理需要偷笑

首先是触发炸弹和轰炸的触发,道具系统要求使用某道具——达到某效果。

按钮无疑是最直观的表现方式:按下某按钮——执行某功能。

所以我们先定义两个按钮——定时炸弹、轰炸:

首先是文件头:

#include "Daoju.h"
#include "cocos2d.h"
然后别忘了使用命名空间:

USING_NS_CC;//using namespace cocos2d的宏定义,当然你也可以使用using namespace cocos2d
using namespace std;
紧接着我们在Daoju类的构造函数中初始化所需按钮:

bool Daoju::init()
{
	//设定一张图片-定时炸弹
	CCMenuItemImage *pZhadanItem = CCMenuItemImage::itemFromNormalImage(
										"ClockNormal.png",
										"ClockSelected.png",
										this,
										menu_selector(Daoju::ChufaZhadan) );//按下按钮触发的函数
	pZhadanItem->setPosition( ccp(CCDirector::sharedDirector()->getWinSize().width - 100, 50) );//按钮锚点坐标也就是按钮的位置

	//把图片设置为menu对象
	CCMenu* pMenuZhadan = CCMenu::menuWithItems(pZhadanItem, NULL);
	pMenuZhadan->setPosition( CCPointZero );
	//将按钮添加到我们道具的layer中
	this->addChild(pMenuZhadan, 1);
//然后下面同理,是“轰炸”道具的按钮
	//设定一张图片-轰炸
	CCMenuItemImage *pBombsItem = CCMenuItemImage::itemFromNormalImage(
										"BombsNormal.png",
										"BombsSelected.png",
										this,
										menu_selector(Daoju::ChufaBombs) );
	pBombsItem->setPosition( ccp(CCDirector::sharedDirector()->getWinSize().width - 200, 50) );

	CCMenu* pMenuBombs = CCMenu::menuWithItems(pBombsItem, NULL);
	pMenuBombs->setPosition( CCPointZero );
	this->addChild(pMenuBombs, 1);
	return true;
}
做好了按钮之后,我们就要给按钮添加内容了,也就是在按钮调用的函数中实现我们的功能。

从上面的代码中我们看到,两个按钮分别调用了ChufaZhadan和ChufaBombs这两个函数来实现功能,

下面来实现这两个功能,ChufaZhadan

void Daoju::ChufaZhadan(CCObject* pSender){
	//开启炸弹模式
	zmode = true;//定义一个外部变量(需要在Daoju类的init方法前定义:bool zmod;),表示当前状态,当zmode为true时即在炸弹模式时点击屏幕将安放炸弹,否者不执行动作,安放炸弹的函数紧接着在后面给出
}
然后我们需要一个实现,点击定时炸弹按钮,再点击屏幕,安放炸弹的功能。

利用上面的zmod值,我们可以判断是否点击了“定时炸弹按钮”,从而决定要不要执行安放动作:

安放动作解析:点击屏幕——获取点击点位置——在该位置放置炸弹。

//获取炸弹安放点坐标,绘制矩形(放置炸弹)
void Daoju::ccTouchesBegan(CCSet *pTouches, CCEvent *pEvent){
	if (zmode == true){
		CCSetIterator it = pTouches->begin();
		CCTouch* touch = (CCTouch*)(*it);
		touchPoint=touch->locationInView(touch->view());
		touchPoint = CCDirector::sharedDirector()->convertToGL(touchPoint);//转换坐标系

			float zx=touchPoint.x;//上面函数所获取的x坐标实际是touchPoint的x成员的值,这里将其赋给zx只是未来好看
			float zy=touchPoint.y;

		CCRect ZhadanKuang(touchPoint.x, touchPoint.y, 20, 50);//在点安放点画出一个矩形框,用于武器系统判断伤害范围,输出伤害
		ZhadanAnimate(zx, zy);//在安放点播放董事炸弹的动画

		zmode = false;//关闭炸弹模式,保证点击一次道具按钮只能执行一次安放
	}
}
到这里定时炸弹的点击——安放功能就做好了,接着就是轰炸效果了;

比起定时炸弹,轰炸就简单多了,不需要去寻找轰炸的坐标,轰炸点是固定的,但轰炸是多点的;

于是我们就可以把轰炸看作是多个定时炸弹来做,开始点击按钮,触发函数:

void Daoju::ChufaBombs(CCObject* pSender){
/***************轰炸矩形框位置*************/
	CCRect BombsKuang(100, 100, 20, 50);//在规定的5个位置画出矩形
	CCRect BombsKuang(200, 200, 20, 50);
	CCRect BombsKuang(200, 100, 20, 50);
	CCRect BombsKuang(100, 200, 20, 50);
	CCRect BombsKuang(300, 100, 20, 50);
/***************轰炸动画位置*************/
	BombsAnimate(200,200);//在规定的5个位置播放动画,使用相同的动画,可以在5个点播放
	BombsAnimate(100,100);
	BombsAnimate(200,100);
	BombsAnimate(100,200);
	BombsAnimate(300,100);
}
这样点击轰炸按钮时的动作也完成了,我们只需要把动画播放封装成传入坐标点——绘制动画的函数,也就是上面代码中的ZhadanAnimate和BombsAnimate:

ZhadanAnimate:

动画是的函数大部分是在加载资源,可以用循环和plist来减少代码量,但是如果资源图片每桢大小不一,用循环来做就不现实了。

这里手动加载,复制、粘贴,小修改偷笑

bool Daoju::ZhadanAnimate(float zx, float zy){
	CCTexture2D *texture = CCTextureCache::sharedTextureCache()->addImage("Zhadan.png");
	//加载资源(图片起点_左上角,图片大小)
	  //读取资源中的每一帧,暂存到fram
	CCSpriteFrame *frame0 = CCSpriteFrame::frameWithTexture(texture, CCRectMake(16*0, 16*0, 16, 16));//从总的资源图中剪下每一帧的资源,前两个值(16*0, 16*0)表示剪切起始点的坐标(也就是每一帧的左上角在资源图中的坐标),后两个值表示当前帧图片的长和高(16,16).如果每一帧都一样大(长、高固定),那么起始点可以写成(长*(行-1),(高*列-1),我们只需要改动行、列即可得到相应的坐标,同时代码也比较直观,出现问题也比较好找.
	CCSpriteFrame *frame1 = CCSpriteFrame::frameWithTexture(texture, CCRectMake(16*1, 16*0, 16, 16));
	CCSpriteFrame *frame2 = CCSpriteFrame::frameWithTexture(texture, CCRectMake(16*0, 16*1, 16, 16));
	CCSpriteFrame *frame3 = CCSpriteFrame::frameWithTexture(texture, CCRectMake(16*1, 16*1, 16, 16));
	CCSpriteFrame *frame4 = CCSpriteFrame::frameWithTexture(texture, CCRectMake(16*0, 16*2, 16, 16));
	CCSpriteFrame *frame5 = CCSpriteFrame::frameWithTexture(texture, CCRectMake(16*1, 16*2, 16, 16));
	CCSpriteFrame *frame6 = CCSpriteFrame::frameWithTexture(texture, CCRectMake(16*0, 16*3, 16, 16));
	CCSpriteFrame *frame7 = CCSpriteFrame::frameWithTexture(texture, CCRectMake(16*1, 16*3, 16, 16));
	//设置初始图片(第一帧)
	CCSprite* spZhadananim = CCSprite::spriteWithSpriteFrame(frame0);
	spZhadananim->setPosition(ccp(zx, zy));
	addChild(spZhadananim, 0, 200);//加到相应图层,设置标签为200,我们在回调函数中会利用标签找到对应的精灵,然后删除动画精灵

	//合成动画-炸弹等待
	CCMutableArray<CCSpriteFrame*> *animZhadanWaitFrames = new CCMutableArray<CCSpriteFrame*>(4);
	//animZhadanWaitFrames->addObject(frame0);
	//animZhadanWaitFrames->addObject(frame1);
	//animZhadanWaitFrames->addObject(frame2);
	//animZhadanWaitFrames->addObject(frame3);
	animZhadanWaitFrames->addObject(frame4);
	animZhadanWaitFrames->addObject(frame5);
	animZhadanWaitFrames->addObject(frame6);
	animZhadanWaitFrames->addObject(frame7);
	//合成动画-炸弹爆炸
	CCMutableArray<CCSpriteFrame*> *animZhadanExciteFrames = new CCMutableArray<CCSpriteFrame*>(4);
	animZhadanExciteFrames->addObject(frame0);
	animZhadanExciteFrames->addObject(frame1);
	animZhadanExciteFrames->addObject(frame2);
	animZhadanExciteFrames->addObject(frame3);

	
	//将4个动画侦生成CCAnimation对象,每0.1秒(float)播放一帧
	//生成炸弹等待动画
	CCAnimation *animationWait  = CCAnimation::animationWithFrames(animZhadanWaitFrames, 0.1f);
	CCAnimate *animateWait = CCAnimate::actionWithAnimation(animationWait, false);//动画模版生成动画

	//生成炸弹激发动画
	CCAnimation *animationExcit  = CCAnimation::animationWithFrames(animZhadanExciteFrames, 0.2f);
	CCAnimate *animateExcit = CCAnimate::actionWithAnimation(animationExcit, false);//此处值为false动画播放完是在最后一帧,为true则返回第一帧

	//创建回调Action
	CCCallFunc *animOverCallBack = CCCallFunc::actionWithTarget(this, callfunc_selector(Daoju::animBombsOverCallBack));
	//依次执行Action
	spZhadananim->runAction(CCSequence::actions(
		CCRepeat::actionWithAction( animateWait, 12),//第一段动画重复12次
		//CCDelayTime::actionWithDuration(0.001f), //暂停瞬间一般情况下不用
		CCRepeat::actionWithAction( animateExcit, 1), //第二段动画重复1次
		animOverCallBack,//回调函数,用于清屏,清除动画,否则动画播放玩后屏幕会残留在屏幕上(对应上面的值可能是最后一帧也可能是第一帧)
		NULL
	));

	animZhadanWaitFrames->release();//释放资源
	animZhadanExciteFrames->release();
	return true;
}
累了很长一串代码来加载动画,这是我找的替代资源图(另存为保存,改名为Zhadan.png即可):定时炸弹
接着是我们清理屏幕的会掉函数,上面代码已经说明,动画播放完会停留在屏幕,我们可以手动清除。

回调函数:

void Daoju::animBombsOverCallBack(){
	CCSprite* animOver = (CCSprite*)this->getChildByTag(200);//利用之前给精灵贴撒谎能够的标签来找到对应精灵
	animOver->stopAllActions();//停止加到精灵上的所有Action
	animOver->removeFromParentAndCleanup(true);//清理加载到动画的相应精灵

接下来的我们还需要一个轰炸的动画,来表现我们轰炸道具的效果。

动画的代码与上面炸弹动画的方法基本一样,都是加载资源,利用动画模版生成动画,然后输出动画,然后在回调函数中清清理动画。

bool Daoju::BombsAnimate(float bx, float by){
	CCTexture2D *texture = CCTextureCache::sharedTextureCache()->addImage("Zhadan.png");

	CCSpriteFrame *frame0 = CCSpriteFrame::frameWithTexture(texture, CCRectMake(16*0, 16*0, 16, 16));
	CCSpriteFrame *frame1 = CCSpriteFrame::frameWithTexture(texture, CCRectMake(16*1, 16*0, 16, 16));
	CCSpriteFrame *frame2 = CCSpriteFrame::frameWithTexture(texture, CCRectMake(16*0, 16*1, 16, 16));
	CCSpriteFrame *frame3 = CCSpriteFrame::frameWithTexture(texture, CCRectMake(16*1, 16*1, 16, 16));
	CCSpriteFrame *frame4 = CCSpriteFrame::frameWithTexture(texture, CCRectMake(16*0, 16*2, 16, 16));
	CCSpriteFrame *frame5 = CCSpriteFrame::frameWithTexture(texture, CCRectMake(16*1, 16*2, 16, 16));
	CCSpriteFrame *frame6 = CCSpriteFrame::frameWithTexture(texture, CCRectMake(16*0, 16*3, 16, 16));
	CCSpriteFrame *frame7 = CCSpriteFrame::frameWithTexture(texture, CCRectMake(16*1, 16*3, 16, 16));
	//(第一帧)
	CCSprite* spBombs = CCSprite::spriteWithSpriteFrame(frame0);

	spBombs->setPosition(ccp(bx, by));

	addChild(spBombs,0, 100);

	//合成动画-炸弹等待
	CCMutableArray<CCSpriteFrame*> *animBombsFrames = new CCMutableArray<CCSpriteFrame*>(8);
	animBombsFrames->addObject(frame0);
	animBombsFrames->addObject(frame1);
	animBombsFrames->addObject(frame2);
	animBombsFrames->addObject(frame3);
	animBombsFrames->addObject(frame4);
	animBombsFrames->addObject(frame5);
	animBombsFrames->addObject(frame6);
	animBombsFrames->addObject(frame7);


	CCAnimation *animationBombs  = CCAnimation::animationWithFrames(animBombsFrames, 0.1f);
	
	CCAnimate *animateBombs = CCAnimate::actionWithAnimation(animationBombs, false);
	//定义回调动作
	CCCallFunc *animOverCallBack = CCCallFunc::actionWithTarget(this, callfunc_selector(Daoju::animZhadanOverCallBack));
	//依次执行动作
	spBombs->runAction(CCSequence::actions(animateBombs, animOverCallBack, NULL));

	animBombsFrames->release();

	return true;
}
void Daoju::animZhadanOverCallBack(){
	CCSprite* animOver = (CCSprite*)this->getChildByTag(100);
	animOver->stopAllActions();
	animOver->removeFromParentAndCleanup(true);
}

到此我们的道具中的轰炸和定时炸弹的代码就累完了,当然,与主程序中汇总的时候还肯定还会有一些小的修改。

但大体的思路和方法就是这样。

在这个例子中,我们使用了cocos2d-x引擎提供的虚函数ccTouchesBegan(CCSet *pTouches, CCEvent *pEvent);来获取触目点坐标。

使用了基本的CCMenu和动画生成功能。其实动画的代码量在一些情况下是可以简略的、我们将在下个次来详细研究cocos2d-x的动画播放。敬请期待...




  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值