今天介绍的是管道层
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); }
结束
时间: 2024-10-05 16:02:55