cocos2d-x-3.2塔防游戏开发2:建塔、角度的旋转、发射箭
2014-12-07 09:58
295 查看
1、 创建箭塔
完成基类建设以后,接下来我们来创建一个最普通的炮塔——ArrowTower 箭塔。
一个箭塔最起码应该由以下的三部分组成,1)箭塔底座,2)弓箭,3)子弹。如下图所示:
2、 放塔的逻辑思路和过程:
1.选中屏幕位置
2.判断该位置是否可以放塔
3.如果不可以放则显示 X 1秒钟
如果可以放并且在当前则在用户选中的行列没有放过(此时需要一个map[r][c]来记录)
显示3种可以选择的塔
4.选择要放的塔
5.在选中位置出现塔 (添加到图层 添加到集合 修改map[r][c]的标记)
6.建塔的面板
设定坐标
三个塔就是三个按钮,
• autoImage= ImageView::create ("towerPos.png");
• bt01=Button::create ("ArrowTower1.png");
• bt02=Button::create ("AttackTower1.png");
• bt02=Button::create ("MultiDirTower1.png");
• 使用Button 如果选中某种塔则在+位置创建
• 如果点击了屏幕其他位置整个Layout消失
3、 在GameScene中添加触摸侦听
.h文件定义
virtual bool onTouchBegan(Touch *touch,
Event*unused_event); virtual
bool onTouchBegan(Touch *touch,
Event *unused_event);
.cpp中实现
//触摸
auto listener=EventListenerTouchOneByOne::create();
listener->onTouchBegan=CC_CALLBACK_2(GameScene::onTouchBegan,this);
listener->onTouchMoved=CC_CALLBACK_2(GameScene::onTouchMoved,this);
listener->onTouchEnded=CC_CALLBACK_2(GameScene::onTouchEnded,this);
Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(listener,
this);
bool
GameScene::onTouchBegan(Touch *touch,
Event*unused_event){
if(this->getChildByTag(1001)!=NULL){
this->removeChildByTag(1001); //1001是那个精灵。当我们每次点击的时候,我们就移除前一个塔的面板
}
// CCLOG("点击了%f,%f",touch->getLocation().x,touch->getLocation().y);
// CCLOG("点击了地图的第%d行,第%d列",(int)touch->getLocation().y/71,
// (int)touch->getLocation().x/71);
nowRow=8-(int)(touch->getLocation().y/71);//确定点击的位置,tiled地图编辑器的0,0点是从左上角开始的
nowCol=(int)(touch->getLocation().x/71);//确定你点击点得行列,71是我缩小之后算出的尺寸
auto map=(TMXTiledMap*)this->getChildByTag(888);//得到地图
// CCLOG("点击了地图的第%d行,第%d列,地图的编号是%d",nowRow,nowCol,map->getLayer("bg")->getTileGIDAt(Vec2(nowCol,nowRow)));
bool canTouch=false;
int tid=map->getLayer("bg")->getTileGIDAt(Vec2(nowCol,nowRow));//得到的是点下点得编号
if(!map->getPropertiesForGID(tid).isNull()){ //获取属性
auto tileTemp=map->getPropertiesForGID(tid).asValueMap();//获取属性的值
if(!tileTemp.empty()){
//如果值不为空,就获取canTouch编号是1
//tileTemp.at("canTouch").asInt();
canTouch=true; //canTouch=1这里就可以放塔
//CCLOG("这里可以放塔tidcanTouch=%d",tileTemp.at("canTouch").asInt());
}
}
if (canTouch) {
CCLOG("塔的选择面板");
addTDSelect(8-nowRow,nowCol);//弹出箭塔的提示
}else{
//显示那个叉号
auto tips=Sprite::createWithSpriteFrameName("no.png");//根据帧来添加一个精灵
tips->setPosition(nowCol*71,(8-nowRow)*71);
tips->setAnchorPoint(Vec2(0,0));
this->addChild(tips);
auto act=DelayTime::create(0.5);//必须加延迟否则就会刚产生就消失
auto act1=CallFunc::create(CC_CALLBACK_0(Sprite::removeFromParent,tips));
tips->runAction(Sequence::create(act,act1,
NULL));
}
return
true;
}
4、 弹出塔的选择面板
5、 GameScene.h中
void addTDSelect(int r,int c);//添加塔的选择面板
GameScene.cpp中
void
GameScene::addTDSelect(int r,int c){
auto Image=
Sprite::createWithSpriteFrameName("towerPos.png");//创建一个精灵
int height=Image->getContentSize().height;
int width=Image->getContentSize().width;
auto bt01=
Sprite::createWithSpriteFrameName("ArrowTower1.png");
auto bt01_select=
Sprite::createWithSpriteFrameName("ArrowTower1.png");
bt01_select->setScale(1.1);
auto bt02=
Sprite::createWithSpriteFrameName("AttackTower1.png");
auto bt02_select=
Sprite::createWithSpriteFrameName("AttackTower1.png");
bt02_select->setScale(1.1);
auto bt03=
Sprite::createWithSpriteFrameName ("MultiDirTower1.png");
auto bt03_select=
Sprite::createWithSpriteFrameName ("MultiDirTower1.png");
bt03_select->setScale(1.1);
//将3个Sprite转为Menu接收用户事件
auto mitem01=MenuItemSprite::create(bt01,bt01_select,
CC_CALLBACK_1(GameScene::selectTD,
this));
auto mitem02=MenuItemSprite::create(bt02,bt02_select,
CC_CALLBACK_1(GameScene::selectTD,
this));
auto mitem03=MenuItemSprite::create(bt03,bt03_select,
CC_CALLBACK_1(GameScene::selectTD,
this));//回调selectTD函数
mitem01->setTag(10);
mitem02->setTag(11);
mitem03->setTag(12);
mitem01->setAnchorPoint(Vec2(1,0));
mitem02->setAnchorPoint(Vec2(0.5,0));
mitem03->setAnchorPoint(Vec2(0,0));
auto menuTD=Menu::create(mitem01,mitem02,mitem03,nullptr);
menuTD->setPosition(Vec2::ZERO);
Image->addChild(menuTD);
mitem01->setPosition(Vec2(0,height));
mitem02->setPosition(Vec2(width/2,height));
mitem03->setPosition(Vec2(width,height));
Image->setTag(1001);
this->addChild(Image);
Image->setAnchorPoint(Vec2(0,0));
Image->setPosition(c*71,r*71);
}
//选择塔的时候会回调这个selectTD,建塔---首先要在GameSCene。h中定义记录的塔
GameScene。H中:
void selectTD(Ref*obj);//回调是用的--建塔
int mapinfo[9][16]={
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
};
static Vector<Bullet *>allBullet;-----保存所有的子弹
staticVector<Enemy *>allEnemy;----这是为了做检测时保存所有的怪物
//我们在cpp中进行初始化,
void
GameScene::selectTD(Ref*obj){
auto item=(MenuItemSprite*)obj;
switch (item->getTag()) {
case 10://第一种类型的塔
{
auto newTd=TD::createTD(1,8-nowRow,nowCol);
this->addChild(newTd);
if (this->money>=newTd->price) {//判断剩余money是否大于第一种类型塔的价格
//是---箭塔,,,不是---不能建设
mapinfo[nowRow][nowCol]=1;//标记这个位置已经有塔----这时候我们要在GameSCene中定义mapinfo
this->money-=newTd->price;//如果钱够的话,就钱数-=塔的价格---花钱了就是
auto moneyLabel=(Label*)this->getChildByTag(2000)->getChildByTag(2002);
moneyLabel->setString(StringUtils::format("%d",money));//改变钱数的标签
}else{
//充值
this->removeChild(newTd);//钱不够就移除刚建的
auto tips =
Sprite::createWithSpriteFrameName("nomoney_mark.png");
tips->setAnchorPoint(Vec2(0,0));
tips->setPosition(nowCol*71,(8-nowRow)*71);
this->addCh
1630f
ild(tips);
tips->runAction(Sequence::create(DelayTime::create(0.8f),
CallFunc::create(CC_CALLBACK_0(Sprite::removeFromParent,
tips)),
NULL));
}
} break;
case 11://第er种类型的塔
{
auto newTd=TD::createTD(2,nowRow,nowCol);
this->addChild(newTd);
} break;
case 12://第san种类型的塔
{
auto newTd=TD::createTD(3,nowRow,nowCol);
this->addChild(newTd);
} break;
default:
break;
}
//移除旧的塔的选择
if(this->getChildByTag(1001)!=nullptr)
{
this->getChildByTag(1001)->removeFromParentAndCleanup(true);
}
}
6.定义一个TD的类
//在TD的.h文件中
#include"cocos2d.h"
#include"GameScene.h"
USING_NS_CC;
class TD:public
Node{
public:
int tx,ty;
int trow,tcol;
int type;
int price;
int dx,dy;//目标点
int act;
int ang;//角度
CREATE_FUNC(TD);
bool init();
static
TD *createTD(int t,int r,int c);
void moveAndShot(float t);//移动
void fire();//开火
};
//旋转和攻击敌人:这里我们需要算出旋转角度,
//检测炮塔视线范围内距离它最近的敌人。
//如果最近的敌人nearestEnemy存在,弓箭则会旋转,所以我们需要计算弓箭旋转的角度和旋转时间。 __关于旋转角度,可以利用三角正切函数来计算,如下图所示:
//炮塔与敌人的之间的角度关系可以表示为: tan a = offY/offX,而rotateVector
=(offX,offY)。 __getAngle方法将返回rotateVector向量与X轴之间的弧度数。但旋转弓箭我们需要的是角度,所以这就需要把弧度rotateRadians转化为角度。不过还好,Cocos2d-x中提供了能把弧度转化为角度的宏CC_RADIANS_TO_DEGREES,这样我们就可以很方便的转化了。 __另外,Cocos2d-x中规定顺时针方向为正,这显然与我们计算出的角度方向相反,所以转化的时候需要把角度a变为-a。
//speed表示炮塔旋转的速度,0.5 / M_PI其实就是 1 / 2PI,它表示1秒钟旋转1个圆。__rotateDuration表示旋转特定的角度需要的时间,计算它用弧度乘以速度
TD.cpp文件中
#include"TD.h"
bool
TD::init(){
if (!Node::init()) {
return
false;
}
return
true;
}
TD *TD::createTD(int t,int r,int c){
TD * td=TD::create();
switch (t) {
case 1://弓箭塔
{ td->price=200;
auto plate1 =
Sprite::createWithSpriteFrameName("baseplate.png");//底座
plate1->setAnchorPoint(Vec2::ZERO);
td->addChild(plate1);
plate1->setTag(10);
td->setAnchorPoint(Vec2::ZERO);
td->setPosition(c*71,r*71);
td->type=t;
td->trow=r;
td->tcol=c;
td->tx=c*71;
td->ty=r*71;
auto rotateArrow =
Sprite::createWithSpriteFrameName("arrow.png");//剑
rotateArrow->setPosition(36,55);
rotateArrow->setTag(11);
plate1->addChild(rotateArrow);
break;
}
default:
break;
}
// 计划任务每隔0.5秒旋转这个塔发射子弹---1—每各塔都会产生子弹—所以需要定义一个所有子弹的向量
td->schedule(schedule_selector(TD::moveAndShot),0.5);
return td;
}
void
TD::moveAndShot(float t){
if (GameScene::allEnemy.size()==0) {
return;
}
//找到理你最近的敌人攻击
int index=0;
int min=GameScene::allEnemy.at(0)->getPosition().getDistance(this->getPosition());
for (int i=0; i<GameScene::allEnemy.size(); i++) {
int far=GameScene::allEnemy.at(i)->getPosition().getDistance(this->getPosition());
if (far<min) {
index=i;
min=far;
}
}
dx=GameScene::allEnemy.at(index)->getPosition().x;//敌人的坐标就是目标点
dy=GameScene::allEnemy.at(index)->getPosition().y;//目标点
//2
Vec2 rotateVector =
GameScene::allEnemy.at(index)->getPosition() -
this->getPosition();//得到距离
float rotateRadians = rotateVector.getAngle();//旋转获得弧度----getAngle方法将返回rotateVector与x轴间的弧度数
float rotateDegrees =
CC_RADIANS_TO_DEGREES(-1 * rotateRadians);//我们把弧度数转化成角度
ang= rotateDegrees;
// 3
float speed = 0.5 /
M_PI;// speed表示炮塔旋转的速度,0.5 / M_PI其实就是 1 / 2PI,它表示1秒钟旋转1个圆。
float rotateDuration =
fabs(rotateRadians * speed);
// rotateDuration表示旋转特定的角度需要的时间,计算它用弧度乘以速度。
// 4
//这句话的意思是0.5秒的时间内箭也要旋转这么大得角度
this->getChildByTag(10)->getChildByTag(11)->runAction(
Sequence::create(RotateTo::create(rotateDuration, rotateDegrees),CallFunc::create(CC_CALLBACK_0(TD::fire,
this)), NULL));
}//移动和攻击
void
TD::fire(){
Bullet*b=Bullet::createBullet(1,
ang, this->tx,
this->ty,
dx, dy);
GameScene::allBullet.pushBack(b);
this->getParent()->addChild(b);
}//开火
6、 这时我们同样需要定义一个子弹类
//Bullet。H文件中
#include"cocos2d.h"
USING_NS_CC;
class Bullet:public
Node{
public:
int bx,by;
int objx,objy;//目标点
int type;
CREATE_FUNC(Bullet);
bool init();
static
Bullet*createBullet(int t,int ang,int x,int y,int dx,int dy);
void killMe();
};
//Bullet.cpp文件中
#include"Bullet.h"
Bullet*Bullet::createBullet(int t,int ang,int x,int
y,int dx,int dy){
Bullet*newb=Bullet::create();
switch (t) {
case 1:{
Sprite*spbullet=Sprite::createWithSpriteFrameName("arrowBullet.png");
newb->bx=x;
newb->by=y;
newb->setPosition(x+35,y+45);
spbullet->setRotation(ang);//角度
newb->addChild(spbullet);
newb->objx=dx;
newb->objy=dy;
} break;
default:
break;
}
float far=Vec2(x,y).getDistance(Vec2(dx,dy));
float time=far/300;
auto act1=MoveTo::create(time,Vec2(dx,dy));
auto act2=CallFunc::create(CC_CALLBACK_0(Bullet::killMe,
newb));//如果是移动到目标点,那么我们就让子弹消失自杀
newb->runAction(Sequence::create(act1,act2,
NULL));
return newb;
}//发射子弹到塔里去发
bool
Bullet::init(){
if (!Node::init()){
return
false;
}
return
true;
}
void
Bullet::killMe(){
this->removeFromParent();
}
7.//我们发射子弹实在塔中发射,所以在塔中有一个计划任务
// 计划任务每隔0.5秒旋转这个塔发射子弹---1—每各塔都会产生子弹—所以需要定义一个所有子弹的向量
td->schedule(schedule_selector(TD::moveAndShot),0.5);
staticVector<Bullet *>allBullet;-----保存所有的子弹
staticVector<Enemy *>allEnemy;----这是为了做检测时保存所有的怪物
//我们需要在GameSCene.cpp中进行初始化,--并且每当产生一个敌人,我们都要把它添加到敌人的向量中—
而且在敌人的类中,敌人如果消失,那么记得要让向量中的敌人也消失
Vector<Bullet *>GameScene::allBullet;
Vector<Enemy *> GameScene::allEnemy;
因为我们把敌人添加到了向量中,所以我们就可以找到敌人---当我们找到离我们最近的敌人,我们就调用fire的方法
8.在GameScene中加入碰撞检测—--游戏逻辑
void update(float t);//碰撞
GameScene.cpp中
//碰撞
this->scheduleUpdate();
void
GameScene::update(float t){
for (int i=0; i<GameScene::allBullet.size(); i++) {
Bullet*b=GameScene::allBullet.at(i);
for (int j=0; j<GameScene::allEnemy.size(); j++) {
Enemy*e=GameScene::allEnemy.at(j);
Rect rb(b->getPosition().x,b->getPosition().y,35,32);
Rect re(e->getPosition().x,e->getPosition().y,127,151);
if (rb.intersectsRect(re)){
e->hp--;
e->changeHp();
if(e->hp<=0){//如果怪物的hp<0,敌人死了
//赚钱--可以做一个switch
this->money+=100;
auto moneyLabel=(Label *)this->getChildByTag(2000)->getChildByTag(2002);
moneyLabel->setString(StringUtils::format("%d",money));
//爆炸效果
auto boom=Boom::newBoom(e->getPosition().x,e->getPosition().y);
this->addChild(boom);
//移除敌人
e->removeFromParent();
allEnemy.eraseObject(e);
}
b->removeFromParent();
GameScene::allBullet.eraseObject(b);
i--;
break;//记得break;
}
}
}
}
9.需要添加一个爆炸的效果,即定义一个爆炸的雷
#include"cocos2d.h"
USING_NS_CC;
class Boom:public
Node
{
public:
int bx,by;
CREATE_FUNC(Boom);
bool init();
static
Boom* newBoom(int x,int y);
void killMe();
};
#include"Boom.h"
Boom*
Boom::newBoom(int x,
int y){
Boom*boom=Boom::create();
Vector<SpriteFrame*>allf;
Sprite*sp=Sprite::create();
boom->addChild(sp);
sp->setPosition(x,y);
for (int i=1; i<6; i++) {
SpriteFrame*sf=SpriteFrameCache::getInstance()->getSpriteFrameByName(StringUtils::format("explode1_%d.png",i));
allf.pushBack(sf);
}
auto animation=Animation::createWithSpriteFrames(allf);
animation->setDelayPerUnit(0.3);
auto animate=Animate::create(animation);
auto act2=CallFunc::create(CC_CALLBACK_0(Boom::killMe,
boom));
sp->runAction(Sequence::create(animate,act2,
NULL));
return boom;
}
bool
Boom::init(){
if (!Node::init()) {
return
false;
}
return
true;
}
void
Boom::killMe(){
this->removeFromParent();
}
//这样的话打死敌人时就会产生爆炸效果
10.给敌人添加血槽
//添加血槽,记得在敌人类中引用ui;
auto hp=LoadingBar::create("sliderProgress2.png");
hp->setTag(1000);
hp->setPercent(100);//满血100
newe->addChild(hp,1);
hp->setPositionY(60)
void changeHp();//改变血
int fullhp;//满血
void
Enemy::changeHp(){
auto hp=(LoadingBar*)this->getChildByTag(1000);
int progress=(this->hp/(float)fullhp*100);//当前的hp除以fullhp*100
hp->setPercent(progress);//剩余的血量
}
//在碰撞检测中,敌人的hp--,那么就改变hp
//在碰撞检测中调用chanehp;
e->chanep();
11.添加钱数的显示
//在GameScene中
//初始化钱数
this->money=500;
auto spritetool =
Sprite::createWithSpriteFrameName("toolbg.png");//建显示钱工具
spritetool->setAnchorPoint(Point(0.5f,1));
spritetool->setPosition (Vec2(Director::getInstance()->getWinSize().width
/2,
Director::getInstance()->getWinSize().height));
this->addChild(spritetool);
spritetool->setTag(2000);
//
auto moneyLabel =
Label::createWithBMFont("bitmapFontChinese.fnt",
" ");//显示钱数量的标签
moneyLabel->setPosition(Vec2(spritetool->getContentSize().width /8, spritetool->getContentSize().height
/2));
moneyLabel->setAnchorPoint(Point(0,0.5f));
auto moneyText =
std::to_string(money);
moneyLabel->setString(moneyText);
moneyLabel->setTag(2002);
spritetool->addChild(moneyLabel);
//在建塔的逻辑处判断减钱
if (this->money>=newTd->price) {//判断剩余money是否大于第一种类型塔的价格
//是---箭塔,,,不是---不能建设
mapinfo[nowRow][nowCol]=1;//标记这个位置已经有塔
this->money-=newTd->price;//如果钱够的话,就钱数-=塔的价格---花钱了就是
auto moneyLabel=(Label*)this->getChildByTag(2000)->getChildByTag(2002);
moneyLabel->setString(StringUtils::format("%d",money));//改变钱数的标签
}else{
//充值
this->removeChild(newTd);//钱不够就移除刚建的
auto tips =
Sprite::createWithSpriteFrameName("nomoney_mark.png");
tips->setAnchorPoint(Vec2(0,0));
tips->setPosition(nowCol*71,(8-nowRow)*71);
this->addChild(tips);
tips->runAction(Sequence::create(DelayTime::create(0.8f),
CallFunc::create(CC_CALLBACK_0(Sprite::removeFromParent,
tips)),NULL));}
完成基类建设以后,接下来我们来创建一个最普通的炮塔——ArrowTower 箭塔。
一个箭塔最起码应该由以下的三部分组成,1)箭塔底座,2)弓箭,3)子弹。如下图所示:
2、 放塔的逻辑思路和过程:
1.选中屏幕位置
2.判断该位置是否可以放塔
3.如果不可以放则显示 X 1秒钟
如果可以放并且在当前则在用户选中的行列没有放过(此时需要一个map[r][c]来记录)
显示3种可以选择的塔
4.选择要放的塔
5.在选中位置出现塔 (添加到图层 添加到集合 修改map[r][c]的标记)
6.建塔的面板
设定坐标
三个塔就是三个按钮,
• autoImage= ImageView::create ("towerPos.png");
• bt01=Button::create ("ArrowTower1.png");
• bt02=Button::create ("AttackTower1.png");
• bt02=Button::create ("MultiDirTower1.png");
• 使用Button 如果选中某种塔则在+位置创建
• 如果点击了屏幕其他位置整个Layout消失
3、 在GameScene中添加触摸侦听
.h文件定义
virtual bool onTouchBegan(Touch *touch,
Event*unused_event); virtual
bool onTouchBegan(Touch *touch,
Event *unused_event);
.cpp中实现
//触摸
auto listener=EventListenerTouchOneByOne::create();
listener->onTouchBegan=CC_CALLBACK_2(GameScene::onTouchBegan,this);
listener->onTouchMoved=CC_CALLBACK_2(GameScene::onTouchMoved,this);
listener->onTouchEnded=CC_CALLBACK_2(GameScene::onTouchEnded,this);
Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(listener,
this);
bool
GameScene::onTouchBegan(Touch *touch,
Event*unused_event){
if(this->getChildByTag(1001)!=NULL){
this->removeChildByTag(1001); //1001是那个精灵。当我们每次点击的时候,我们就移除前一个塔的面板
}
// CCLOG("点击了%f,%f",touch->getLocation().x,touch->getLocation().y);
// CCLOG("点击了地图的第%d行,第%d列",(int)touch->getLocation().y/71,
// (int)touch->getLocation().x/71);
nowRow=8-(int)(touch->getLocation().y/71);//确定点击的位置,tiled地图编辑器的0,0点是从左上角开始的
nowCol=(int)(touch->getLocation().x/71);//确定你点击点得行列,71是我缩小之后算出的尺寸
auto map=(TMXTiledMap*)this->getChildByTag(888);//得到地图
// CCLOG("点击了地图的第%d行,第%d列,地图的编号是%d",nowRow,nowCol,map->getLayer("bg")->getTileGIDAt(Vec2(nowCol,nowRow)));
bool canTouch=false;
int tid=map->getLayer("bg")->getTileGIDAt(Vec2(nowCol,nowRow));//得到的是点下点得编号
if(!map->getPropertiesForGID(tid).isNull()){ //获取属性
auto tileTemp=map->getPropertiesForGID(tid).asValueMap();//获取属性的值
if(!tileTemp.empty()){
//如果值不为空,就获取canTouch编号是1
//tileTemp.at("canTouch").asInt();
canTouch=true; //canTouch=1这里就可以放塔
//CCLOG("这里可以放塔tidcanTouch=%d",tileTemp.at("canTouch").asInt());
}
}
if (canTouch) {
CCLOG("塔的选择面板");
addTDSelect(8-nowRow,nowCol);//弹出箭塔的提示
}else{
//显示那个叉号
auto tips=Sprite::createWithSpriteFrameName("no.png");//根据帧来添加一个精灵
tips->setPosition(nowCol*71,(8-nowRow)*71);
tips->setAnchorPoint(Vec2(0,0));
this->addChild(tips);
auto act=DelayTime::create(0.5);//必须加延迟否则就会刚产生就消失
auto act1=CallFunc::create(CC_CALLBACK_0(Sprite::removeFromParent,tips));
tips->runAction(Sequence::create(act,act1,
NULL));
}
return
true;
}
4、 弹出塔的选择面板
5、 GameScene.h中
void addTDSelect(int r,int c);//添加塔的选择面板
GameScene.cpp中
void
GameScene::addTDSelect(int r,int c){
auto Image=
Sprite::createWithSpriteFrameName("towerPos.png");//创建一个精灵
int height=Image->getContentSize().height;
int width=Image->getContentSize().width;
auto bt01=
Sprite::createWithSpriteFrameName("ArrowTower1.png");
auto bt01_select=
Sprite::createWithSpriteFrameName("ArrowTower1.png");
bt01_select->setScale(1.1);
auto bt02=
Sprite::createWithSpriteFrameName("AttackTower1.png");
auto bt02_select=
Sprite::createWithSpriteFrameName("AttackTower1.png");
bt02_select->setScale(1.1);
auto bt03=
Sprite::createWithSpriteFrameName ("MultiDirTower1.png");
auto bt03_select=
Sprite::createWithSpriteFrameName ("MultiDirTower1.png");
bt03_select->setScale(1.1);
//将3个Sprite转为Menu接收用户事件
auto mitem01=MenuItemSprite::create(bt01,bt01_select,
CC_CALLBACK_1(GameScene::selectTD,
this));
auto mitem02=MenuItemSprite::create(bt02,bt02_select,
CC_CALLBACK_1(GameScene::selectTD,
this));
auto mitem03=MenuItemSprite::create(bt03,bt03_select,
CC_CALLBACK_1(GameScene::selectTD,
this));//回调selectTD函数
mitem01->setTag(10);
mitem02->setTag(11);
mitem03->setTag(12);
mitem01->setAnchorPoint(Vec2(1,0));
mitem02->setAnchorPoint(Vec2(0.5,0));
mitem03->setAnchorPoint(Vec2(0,0));
auto menuTD=Menu::create(mitem01,mitem02,mitem03,nullptr);
menuTD->setPosition(Vec2::ZERO);
Image->addChild(menuTD);
mitem01->setPosition(Vec2(0,height));
mitem02->setPosition(Vec2(width/2,height));
mitem03->setPosition(Vec2(width,height));
Image->setTag(1001);
this->addChild(Image);
Image->setAnchorPoint(Vec2(0,0));
Image->setPosition(c*71,r*71);
}
//选择塔的时候会回调这个selectTD,建塔---首先要在GameSCene。h中定义记录的塔
GameScene。H中:
void selectTD(Ref*obj);//回调是用的--建塔
int mapinfo[9][16]={
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
};
static Vector<Bullet *>allBullet;-----保存所有的子弹
staticVector<Enemy *>allEnemy;----这是为了做检测时保存所有的怪物
//我们在cpp中进行初始化,
void
GameScene::selectTD(Ref*obj){
auto item=(MenuItemSprite*)obj;
switch (item->getTag()) {
case 10://第一种类型的塔
{
auto newTd=TD::createTD(1,8-nowRow,nowCol);
this->addChild(newTd);
if (this->money>=newTd->price) {//判断剩余money是否大于第一种类型塔的价格
//是---箭塔,,,不是---不能建设
mapinfo[nowRow][nowCol]=1;//标记这个位置已经有塔----这时候我们要在GameSCene中定义mapinfo
this->money-=newTd->price;//如果钱够的话,就钱数-=塔的价格---花钱了就是
auto moneyLabel=(Label*)this->getChildByTag(2000)->getChildByTag(2002);
moneyLabel->setString(StringUtils::format("%d",money));//改变钱数的标签
}else{
//充值
this->removeChild(newTd);//钱不够就移除刚建的
auto tips =
Sprite::createWithSpriteFrameName("nomoney_mark.png");
tips->setAnchorPoint(Vec2(0,0));
tips->setPosition(nowCol*71,(8-nowRow)*71);
this->addCh
1630f
ild(tips);
tips->runAction(Sequence::create(DelayTime::create(0.8f),
CallFunc::create(CC_CALLBACK_0(Sprite::removeFromParent,
tips)),
NULL));
}
} break;
case 11://第er种类型的塔
{
auto newTd=TD::createTD(2,nowRow,nowCol);
this->addChild(newTd);
} break;
case 12://第san种类型的塔
{
auto newTd=TD::createTD(3,nowRow,nowCol);
this->addChild(newTd);
} break;
default:
break;
}
//移除旧的塔的选择
if(this->getChildByTag(1001)!=nullptr)
{
this->getChildByTag(1001)->removeFromParentAndCleanup(true);
}
}
6.定义一个TD的类
//在TD的.h文件中
#include"cocos2d.h"
#include"GameScene.h"
USING_NS_CC;
class TD:public
Node{
public:
int tx,ty;
int trow,tcol;
int type;
int price;
int dx,dy;//目标点
int act;
int ang;//角度
CREATE_FUNC(TD);
bool init();
static
TD *createTD(int t,int r,int c);
void moveAndShot(float t);//移动
void fire();//开火
};
//旋转和攻击敌人:这里我们需要算出旋转角度,
//检测炮塔视线范围内距离它最近的敌人。
//如果最近的敌人nearestEnemy存在,弓箭则会旋转,所以我们需要计算弓箭旋转的角度和旋转时间。 __关于旋转角度,可以利用三角正切函数来计算,如下图所示:
//炮塔与敌人的之间的角度关系可以表示为: tan a = offY/offX,而rotateVector
=(offX,offY)。 __getAngle方法将返回rotateVector向量与X轴之间的弧度数。但旋转弓箭我们需要的是角度,所以这就需要把弧度rotateRadians转化为角度。不过还好,Cocos2d-x中提供了能把弧度转化为角度的宏CC_RADIANS_TO_DEGREES,这样我们就可以很方便的转化了。 __另外,Cocos2d-x中规定顺时针方向为正,这显然与我们计算出的角度方向相反,所以转化的时候需要把角度a变为-a。
//speed表示炮塔旋转的速度,0.5 / M_PI其实就是 1 / 2PI,它表示1秒钟旋转1个圆。__rotateDuration表示旋转特定的角度需要的时间,计算它用弧度乘以速度
TD.cpp文件中
#include"TD.h"
bool
TD::init(){
if (!Node::init()) {
return
false;
}
return
true;
}
TD *TD::createTD(int t,int r,int c){
TD * td=TD::create();
switch (t) {
case 1://弓箭塔
{ td->price=200;
auto plate1 =
Sprite::createWithSpriteFrameName("baseplate.png");//底座
plate1->setAnchorPoint(Vec2::ZERO);
td->addChild(plate1);
plate1->setTag(10);
td->setAnchorPoint(Vec2::ZERO);
td->setPosition(c*71,r*71);
td->type=t;
td->trow=r;
td->tcol=c;
td->tx=c*71;
td->ty=r*71;
auto rotateArrow =
Sprite::createWithSpriteFrameName("arrow.png");//剑
rotateArrow->setPosition(36,55);
rotateArrow->setTag(11);
plate1->addChild(rotateArrow);
break;
}
default:
break;
}
// 计划任务每隔0.5秒旋转这个塔发射子弹---1—每各塔都会产生子弹—所以需要定义一个所有子弹的向量
td->schedule(schedule_selector(TD::moveAndShot),0.5);
return td;
}
void
TD::moveAndShot(float t){
if (GameScene::allEnemy.size()==0) {
return;
}
//找到理你最近的敌人攻击
int index=0;
int min=GameScene::allEnemy.at(0)->getPosition().getDistance(this->getPosition());
for (int i=0; i<GameScene::allEnemy.size(); i++) {
int far=GameScene::allEnemy.at(i)->getPosition().getDistance(this->getPosition());
if (far<min) {
index=i;
min=far;
}
}
dx=GameScene::allEnemy.at(index)->getPosition().x;//敌人的坐标就是目标点
dy=GameScene::allEnemy.at(index)->getPosition().y;//目标点
//2
Vec2 rotateVector =
GameScene::allEnemy.at(index)->getPosition() -
this->getPosition();//得到距离
float rotateRadians = rotateVector.getAngle();//旋转获得弧度----getAngle方法将返回rotateVector与x轴间的弧度数
float rotateDegrees =
CC_RADIANS_TO_DEGREES(-1 * rotateRadians);//我们把弧度数转化成角度
ang= rotateDegrees;
// 3
float speed = 0.5 /
M_PI;// speed表示炮塔旋转的速度,0.5 / M_PI其实就是 1 / 2PI,它表示1秒钟旋转1个圆。
float rotateDuration =
fabs(rotateRadians * speed);
// rotateDuration表示旋转特定的角度需要的时间,计算它用弧度乘以速度。
// 4
//这句话的意思是0.5秒的时间内箭也要旋转这么大得角度
this->getChildByTag(10)->getChildByTag(11)->runAction(
Sequence::create(RotateTo::create(rotateDuration, rotateDegrees),CallFunc::create(CC_CALLBACK_0(TD::fire,
this)), NULL));
}//移动和攻击
void
TD::fire(){
Bullet*b=Bullet::createBullet(1,
ang, this->tx,
this->ty,
dx, dy);
GameScene::allBullet.pushBack(b);
this->getParent()->addChild(b);
}//开火
6、 这时我们同样需要定义一个子弹类
//Bullet。H文件中
#include"cocos2d.h"
USING_NS_CC;
class Bullet:public
Node{
public:
int bx,by;
int objx,objy;//目标点
int type;
CREATE_FUNC(Bullet);
bool init();
static
Bullet*createBullet(int t,int ang,int x,int y,int dx,int dy);
void killMe();
};
//Bullet.cpp文件中
#include"Bullet.h"
Bullet*Bullet::createBullet(int t,int ang,int x,int
y,int dx,int dy){
Bullet*newb=Bullet::create();
switch (t) {
case 1:{
Sprite*spbullet=Sprite::createWithSpriteFrameName("arrowBullet.png");
newb->bx=x;
newb->by=y;
newb->setPosition(x+35,y+45);
spbullet->setRotation(ang);//角度
newb->addChild(spbullet);
newb->objx=dx;
newb->objy=dy;
} break;
default:
break;
}
float far=Vec2(x,y).getDistance(Vec2(dx,dy));
float time=far/300;
auto act1=MoveTo::create(time,Vec2(dx,dy));
auto act2=CallFunc::create(CC_CALLBACK_0(Bullet::killMe,
newb));//如果是移动到目标点,那么我们就让子弹消失自杀
newb->runAction(Sequence::create(act1,act2,
NULL));
return newb;
}//发射子弹到塔里去发
bool
Bullet::init(){
if (!Node::init()){
return
false;
}
return
true;
}
void
Bullet::killMe(){
this->removeFromParent();
}
7.//我们发射子弹实在塔中发射,所以在塔中有一个计划任务
// 计划任务每隔0.5秒旋转这个塔发射子弹---1—每各塔都会产生子弹—所以需要定义一个所有子弹的向量
td->schedule(schedule_selector(TD::moveAndShot),0.5);
staticVector<Bullet *>allBullet;-----保存所有的子弹
staticVector<Enemy *>allEnemy;----这是为了做检测时保存所有的怪物
//我们需要在GameSCene.cpp中进行初始化,--并且每当产生一个敌人,我们都要把它添加到敌人的向量中—
而且在敌人的类中,敌人如果消失,那么记得要让向量中的敌人也消失
Vector<Bullet *>GameScene::allBullet;
Vector<Enemy *> GameScene::allEnemy;
因为我们把敌人添加到了向量中,所以我们就可以找到敌人---当我们找到离我们最近的敌人,我们就调用fire的方法
8.在GameScene中加入碰撞检测—--游戏逻辑
void update(float t);//碰撞
GameScene.cpp中
//碰撞
this->scheduleUpdate();
void
GameScene::update(float t){
for (int i=0; i<GameScene::allBullet.size(); i++) {
Bullet*b=GameScene::allBullet.at(i);
for (int j=0; j<GameScene::allEnemy.size(); j++) {
Enemy*e=GameScene::allEnemy.at(j);
Rect rb(b->getPosition().x,b->getPosition().y,35,32);
Rect re(e->getPosition().x,e->getPosition().y,127,151);
if (rb.intersectsRect(re)){
e->hp--;
e->changeHp();
if(e->hp<=0){//如果怪物的hp<0,敌人死了
//赚钱--可以做一个switch
this->money+=100;
auto moneyLabel=(Label *)this->getChildByTag(2000)->getChildByTag(2002);
moneyLabel->setString(StringUtils::format("%d",money));
//爆炸效果
auto boom=Boom::newBoom(e->getPosition().x,e->getPosition().y);
this->addChild(boom);
//移除敌人
e->removeFromParent();
allEnemy.eraseObject(e);
}
b->removeFromParent();
GameScene::allBullet.eraseObject(b);
i--;
break;//记得break;
}
}
}
}
9.需要添加一个爆炸的效果,即定义一个爆炸的雷
#include"cocos2d.h"
USING_NS_CC;
class Boom:public
Node
{
public:
int bx,by;
CREATE_FUNC(Boom);
bool init();
static
Boom* newBoom(int x,int y);
void killMe();
};
#include"Boom.h"
Boom*
Boom::newBoom(int x,
int y){
Boom*boom=Boom::create();
Vector<SpriteFrame*>allf;
Sprite*sp=Sprite::create();
boom->addChild(sp);
sp->setPosition(x,y);
for (int i=1; i<6; i++) {
SpriteFrame*sf=SpriteFrameCache::getInstance()->getSpriteFrameByName(StringUtils::format("explode1_%d.png",i));
allf.pushBack(sf);
}
auto animation=Animation::createWithSpriteFrames(allf);
animation->setDelayPerUnit(0.3);
auto animate=Animate::create(animation);
auto act2=CallFunc::create(CC_CALLBACK_0(Boom::killMe,
boom));
sp->runAction(Sequence::create(animate,act2,
NULL));
return boom;
}
bool
Boom::init(){
if (!Node::init()) {
return
false;
}
return
true;
}
void
Boom::killMe(){
this->removeFromParent();
}
//这样的话打死敌人时就会产生爆炸效果
10.给敌人添加血槽
//添加血槽,记得在敌人类中引用ui;
auto hp=LoadingBar::create("sliderProgress2.png");
hp->setTag(1000);
hp->setPercent(100);//满血100
newe->addChild(hp,1);
hp->setPositionY(60)
void changeHp();//改变血
int fullhp;//满血
void
Enemy::changeHp(){
auto hp=(LoadingBar*)this->getChildByTag(1000);
int progress=(this->hp/(float)fullhp*100);//当前的hp除以fullhp*100
hp->setPercent(progress);//剩余的血量
}
//在碰撞检测中,敌人的hp--,那么就改变hp
//在碰撞检测中调用chanehp;
e->chanep();
11.添加钱数的显示
//在GameScene中
//初始化钱数
this->money=500;
auto spritetool =
Sprite::createWithSpriteFrameName("toolbg.png");//建显示钱工具
spritetool->setAnchorPoint(Point(0.5f,1));
spritetool->setPosition (Vec2(Director::getInstance()->getWinSize().width
/2,
Director::getInstance()->getWinSize().height));
this->addChild(spritetool);
spritetool->setTag(2000);
//
auto moneyLabel =
Label::createWithBMFont("bitmapFontChinese.fnt",
" ");//显示钱数量的标签
moneyLabel->setPosition(Vec2(spritetool->getContentSize().width /8, spritetool->getContentSize().height
/2));
moneyLabel->setAnchorPoint(Point(0,0.5f));
auto moneyText =
std::to_string(money);
moneyLabel->setString(moneyText);
moneyLabel->setTag(2002);
spritetool->addChild(moneyLabel);
//在建塔的逻辑处判断减钱
if (this->money>=newTd->price) {//判断剩余money是否大于第一种类型塔的价格
//是---箭塔,,,不是---不能建设
mapinfo[nowRow][nowCol]=1;//标记这个位置已经有塔
this->money-=newTd->price;//如果钱够的话,就钱数-=塔的价格---花钱了就是
auto moneyLabel=(Label*)this->getChildByTag(2000)->getChildByTag(2002);
moneyLabel->setString(StringUtils::format("%d",money));//改变钱数的标签
}else{
//充值
this->removeChild(newTd);//钱不够就移除刚建的
auto tips =
Sprite::createWithSpriteFrameName("nomoney_mark.png");
tips->setAnchorPoint(Vec2(0,0));
tips->setPosition(nowCol*71,(8-nowRow)*71);
this->addChild(tips);
tips->runAction(Sequence::create(DelayTime::create(0.8f),
CallFunc::create(CC_CALLBACK_0(Sprite::removeFromParent,
tips)),NULL));}
相关文章推荐
- cocos2d游戏 旋转角度
- 【iphone游戏开发】iphone-Cocos2d游戏开发之四:精灵实现缩放,旋转,跳动,移动等动画
- 【iphone游戏开发】iphone-Cocos2d游戏开发之四:精灵实现缩放,旋转,跳动,移动等动画
- 【iphone游戏开发】iphone-Cocos2d游戏开发之四:精灵实现缩放,旋转,跳动,移动等动画
- cocos2d-x-3.2塔防游戏开发3:动态的从配置文件中设置怪物的关卡,总波数,初始化钱数
- cocos2d-x-3.2塔防游戏开发1:背景,地图,obj,产生移动NPC的实现
- 【iOS-cocos2d游戏引擎开发之一】搭建cocos2d游戏引擎环境,创建第一个HelloWorld! 推荐
- iPhone开发之游戏篇(1) --- cocos2d的基本概念
- 如何用cocos2d-x来开发简单的Uphone游戏:(一) 下载安装和HelloWorld
- 如何用cocos2d-x来开发简单的Uphone游戏:(二) 移动的精灵
- 【Cocos2d游戏开发之二】Cocos2D 游戏开发资源贴(教程以及源码)
- cocos2d-x开源游戏引擎,C++开发iphone/android/uphone/win32游戏
- (译)如何使用cocos2d开发一个简单的iphone游戏:旋转炮塔。(第二部分)
- iphone游戏开发 资料索引 cocos2d, OpenGL ES
- 【Cocos2d游戏开发之一】搭建cocos2d游戏引擎环境HelloWorld!
- 【iOS-Cocos2d游戏开发之一】搭建cocos2d游戏引擎环境HelloWorld!
- iPhone开发之游戏篇(2) --- cocos2d的Actions
- iphone游戏开发 资料索引 cocos2d, OpenGL ES
- iphone游戏开发 资料索引 cocos2d, OpenGL ES
- 如何用cocos2d-x来开发简单的Uphone游戏:(四) 音乐音效 & 最后的润色