cocos2d-x v3.2 FlappyBird 各个类对象具体代码分析(5)

今天介绍的是管道层

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);
}


结束


郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。