今天介绍的是管道层
PipeLayer.h
PipeLayer.cpp
管道层主要实现的是管道从右边往左边平移,结束后移除,而且管道还要长短高低不一样,然后就是如何判断小鸟通过一个管道。先说管道的平移,这个很简单,用一个函数把两跟管道封装好,让它moveby或者moveto好了,平移结束后,用一个回调函数移除自己就够了,当然封装好管道后,我们要把每一个管道放到一个数组里,方便管理嘛;然后就是管道高低不一样这里用一张图表示:
最后就是小鸟通过管道判断,这里我们是判断一个完整的管道是否通过屏幕中心线(因为小鸟是固定在屏幕中心,上下移动的)。这个类大致就这些内容。
下面这张图解释了为什么管道停止移动后,要取消向上管道的物理模型,防止小鸟死亡后直接掉在管道上:
下面是代码分析:
//PipeLayer.h
#pragma once
#include "cocos2d.h"
class PipeLayer:public cocos2d::Layer
{
public:
PipeLayer();
~PipeLayer();
bool init();
//添加一个移动的组合管道
void addPipe(float);
//管道移动结束后的回调函数
void pipeMoveOver(cocos2d::Ref *);
//管道停止移动
void stopPipe();
//计时器执行函数
void update(float);
//管道开始移动
void startPipe();
CREATE_FUNC(PipeLayer);
private:
//管道数组一
cocos2d::Array * pipe_arr;
//管道数组二
cocos2d::Array * pipe_arr2;
bool btn;
};
//PipeLayer.cpp
#include "PipeLayer.h"
#include "define.h"
#include "NumberLayer.h"
USING_NS_CC;
PipeLayer::PipeLayer()
{
}
PipeLayer::~PipeLayer()
{
//析构函数释放数组
pipe_arr->release();
pipe_arr2->release();
}
bool PipeLayer::init()
{
if (!Layer::init())
{
return false;
}
//两个数组初始化,其实只要一个数组就够了,楼主刚开始写的时候傻逼了
//pipe_arr 这个数组是用来检测加分的
//pipe_arr2 这个数组是为了方便统一处理管道
pipe_arr=Array::create();
//防止被自动释放
pipe_arr->retain();
pipe_arr2=Array::create();
pipe_arr2->retain();
btn=true;
return true;
}
void PipeLayer::addPipe( float )
{
log("this pipe");
//向上管道初始化
auto pipe_up=Sprite::createWithSpriteFrameName("pipe_up.png");
pipe_up->setPosition(Point(pipe_up->getContentSize().width/2,pipe_up->getContentSize().height/2));
auto body_up=PhysicsBody::create();
auto body_shape_up=PhysicsShapeBox::create(pipe_up->getContentSize());
body_up->addShape(body_shape_up);
body_up->setDynamic(false);
body_up->setGravityEnable(false);
body_up->setCategoryBitmask(1);
body_up->setCollisionBitmask(-1);
body_up->setContactTestBitmask(-1);
pipe_up->setPhysicsBody(body_up);
//向下管道初始化,这边的THROUGH_HEIGHT是两根管道之间的空隙
auto pipe_down=Sprite::createWithSpriteFrameName("pipe_down.png");
pipe_down->setPosition(Point(pipe_down->getContentSize().width/2,pipe_down->getContentSize().height/2+pipe_up->getContentSize().height+THROUGH_HEIGHT));
auto body_down=PhysicsBody::create();
auto body_shape_down=PhysicsShapeBox::create(pipe_down->getContentSize());
body_down->addShape(body_shape_down);
body_down->setDynamic(false);
body_down->setGravityEnable(false);
body_down->setCategoryBitmask(1);
body_down->setCollisionBitmask(-1);
body_down->setContactTestBitmask(-1);
pipe_down->setPhysicsBody(body_down);
//这边的node相当于一个容器把这两个管道封装在一个节点中并设置target
auto node=Node::create();
node->addChild(pipe_up,0,PIPE_UP);
node->addChild(pipe_down,0,PIPE_DOWN);
node->setAnchorPoint(Point::ANCHOR_BOTTOM_LEFT);
//关于管道Y坐标的设置(就是管道上下长度不一样的处理),大家还是看图例,说不清楚
//管道是从右边移动到左边,所以PIPE_X的值肯定比游戏的width要大这里设定是300
int range=rand()%PIPE_RANGE;
node->setPosition(Point(PIPE_X,PIPE_Y+range));
//管道移动的时间,距离,以及方向
auto moveby=MoveBy::create(PIPE_TIME,PIPE_VELOCITY);
//管道移动结束后执行的回调函数
auto callback=CallFuncN::create(CC_CALLBACK_1(PipeLayer::pipeMoveOver,this));
//把管道移动和结束后的回调放到列队中
auto sequence=Sequence::create(moveby,callback,NULL);
node->runAction(sequence);
this->addChild(node);
//把管道放到两个数组中
pipe_arr->addObject(node);
pipe_arr2->addObject(node);
//启动计时器,这个计时器是用来判断得分的
//也就是小鸟是否通过管道
//只要启动一次就够了,所以这边有个btn
if (btn)
{
this->scheduleUpdate();
btn=false;
}
}
//移动结束后,从这个类和数组中移除管道
void PipeLayer::pipeMoveOver(Ref * r)
{
Sprite * sp=(Sprite* )r;
this->removeChild(sp);
pipe_arr2->removeObject(sp);
}
//停止管道移动(小鸟死亡后)
void PipeLayer::stopPipe()
{
this->unschedule(schedule_selector(PipeLayer::addPipe));
this->unscheduleUpdate();
//这里取消了向上管道的物理结构
//这样子做是为了当小鸟撞到向下管道死亡了,不跌落在向上管道上
//见图例
Ref * p;
CCARRAY_FOREACH(pipe_arr2,p)
{
auto n=(Node*)p;
//管道停止移动
n->stopAllActions();
//向上管道取消物理结构
n->getChildByTag(PIPE_UP)->getPhysicsBody()->setEnable(false);
}
}
//积分
void PipeLayer::update(float)
{
auto origin=Director::getInstance()->getVisibleOrigin();
auto visibleSize=Director::getInstance()->getVisibleSize();
//如果管道数量为0,则直接返回
if (pipe_arr->count()<=0)
{
return;
}
//取管道数组中的第一个管道(也就是第一生成的管道)
//它在管道运动的最前面
Node * tn=(Node*)pipe_arr->getObjectAtIndex(0);
//如果管道整体过了场景的一半,那就加分
//同时在这个数组中,移除了这个管道
//这样,位于这个管道后的下一管道就变成最前面的管道了
//如:
//index: 0 1 2 3 4
//array: A B C D E
// ↑
// 这是序列为0的管道A,当它过完场景中点时,它在数组中就被移除了,然后就变成
//index: 0 1 2 3 4
//arrat: B C D E F
//管道B就变成序列0了,然后再检测
if ( tn->getPositionX()<(visibleSize.width/2-52))
{
log("X:%f",tn->getContentSize().width);
pipe_arr->removeObjectAtIndex(0);
CocosDenshion::SimpleAudioEngine::getInstance()->playEffect("sounds/sfx_point.mp3");
NumberLayer::getInstance()->addScore();
}
}
//管道开始移动
void PipeLayer::startPipe()
{
this->schedule(schedule_selector(PipeLayer::addPipe),PIPE_FRE);
}
结束