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

结束

时间: 2024-10-05 16:02:55

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

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

今天我们要讲三个类,这三个类应该算比较简单的 HelpLayer类 NumberLayer类 GetLocalScore类 HelpLayer类,主要放了两个图形精灵上去,一个是游戏的名字,一个是提示游戏怎么玩的,就一张图: NumberLayer类,涉及到自定义字体的制作,我们提取出来的资源里,有很多数字图片: 现在我们要把它们做成这样子的: 这个跟游戏图片资源差不多,做成这样就可以直接拿来用,省了很多事情,那怎么做了,这里我们要用到一个叫软件,具体使用方法和过程,我这里就不介绍了,大家直接戳

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

好久没写博客了,今天早上起来不知怎么的就打开了csdn,工作简历也投了好几家,都石沉大海,在学校也闲着没事,就打算把以前写的Flappy Bird里面的一些代码分析发出来. Flappy Bird 网上也有很多的教程,我其中的有些代码也是参考网上的,不能算是完全原创吧,但是也有很大一部分代码都是按照我自己的想法,设计写出来的,接触cocos2d-x已经差不多有一年了,从以前的2x到现在的3x,改变了很多东西,作为一个新手我还有很多东西要去学习. 开始前有几点注意事项要说明: 1)我的Flappy

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

今天我们介绍最后两个类 GameOverLayer类 GameLayer类 GameLayer类是整个游戏中最重要的类,因为是整个游戏的中央系统,控制着各个类(层)之间的交互,这个类中实现了猪脚小鸟和它的敌人(管道和草地- . -)碰撞检测,说道物理引擎的碰撞检测,我也是第一次接触,也没多大难度,就直接调用了cocos2d-x的接口,这个类就是游戏的主场景,游戏就是在这里进行的. GameOverLayer类,游戏结束后一些分数的显示,还有就是奖牌的实现(楼主写的很简单......),这边比较有

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

今天要讲的使我们猪脚类 SpriteBird.h SpriteBird.cpp 猪脚类要注意的是三种状态的切换,和单点触控侦听的设置,还有就是小鸟在飞行的过程中,头部会上下的摆动,物理模型这里就不说了,小鸟头部的摆动是靠小鸟Y轴的速度来判定的,当小鸟向上飞的时候,速度是正的,所以头部摆动的旋转角度也是向上,当小鸟下落的时候,速度是负的,所以头部摆动的旋转角度是向下的. 小鸟的三种状态: 下面是代码 //SpriteBird.h #pragma once #include "cocos2d.h&q

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

今天介绍两个比较简单的类 GameBegin.h LandLayer.h 需要注意的有两点,一个是草地的滚动,还有一个是物理模型,都在LandLayer.h中实现,分析都在代码注释中. 先上GameBegin场景的效果图 这张图可以解析为 背景图的添加 游戏名字 小鸟以及小鸟飞行动画 按钮 草地(草地是会滚动的) //GameBegin.h #pragma once #include "cocos2d.h" class GameBegin:public cocos2d::Layer {

面向对象--多继承&amp;派生类对象内存布局分析&amp;各基类指针所指向的位置分析

背景 原文链接:ordeder  http://blog.csdn.net/ordeder/article/details/25477363 关于非虚函数的成员函数的调用机制,可以参考: http://blog.csdn.net/yuanyirui/article/details/4594805 成员函数的调用涉及到面向对象语言的反射机制. 虚函数表机制可以查看下面这个blog: http://blog.csdn.net/haoel/article/details/1948051 总结为: 其一

黑马程序员_类对象创建代码的执行顺序测试总结

class StaticCode { // 类的非静态成员变量(实例变量)定义语句: // 作用:用来存储对象的特有数据的成员变量. // 运行:当类对象被创建时就执行,按照顺序自上而下执行,和构造代码块平级.(即当有多个构造函数 // 代码块和多个非静态成员变量时,JVM按照自上而下的顺序来执行所有语句,包括构造代码块里的代码 // 和非静态成员的声明.) // 存储位置:非静态变量位于堆内存中的对象实体中. int num = 3; // 构造代码块 { System.out.println

java 类对象使用

在学习反射机制时,总结一下获得类对象方式: 第一种方式:通过类本身来获得对象 Class<?> classname = this.getClass(); 或者this.class 第二种方式:通过子类的实例获取父类对象 ClassName cn = new ClassName(); UserClass = cn.getClass(); Class<?> SubUserClass = UserClass.getSuperclass(); 第三种方式:通过类名加.class获取对象 C

C++实现根据类名动态生成类对象

在开发后台服务的过程中,我们常常需要从数据库中取数据,并将数据缓存在本地中,另外,我们的服务还需要有更新数据的能力:包括定时的主动更新以及数据库数据更新时服务收到通知的被动更新. 之前在需要用到以上功能的时候,模仿着组内通用的数据Cache部分的代码来写,十分方便,基本上只需要自己写两个类:一个是取数据并缓存数据的类XXXData,一个是扇出数据的类XXXFetcher. 在需要使用数据的时候,通过: FetcherFactory::getFetcher<XXXFetcher>() 即可获取一