上一篇:【一】仿微信飞机大战的cocos2d-x3.0rc1
今天我们来完成
1,场景替换
2,游戏背景无限滚动
3,添加可以移动的我机
一,首先是完成后的效果展示
飞机是我大学舍长设计的。这图的效果是后面的背景自上往下无限循环,飞机可以用鼠标随意拖动,不过不能拖出屏幕之外。
二,工程目录
相对【一】的目录,我们多出了四个文件
三,场景替换
GameScene的两个文件,是用来承载整个我机打敌机。还记得HelloWorldScene文件中的void HelloWorld::loadingDone( Node* pNode )吗?
这就是用了替换场景的,由游戏的开始场景,替换为游戏场景,替换场景的具体代码如下:
void HelloWorld::loadingDone( Node* pNode )
{
auto scene = GameLayer::createScene();
TransitionCrossFade *pAnimateScene = TransitionCrossFade::create(1, scene);
Director::getInstance()->replaceScene(pAnimateScene);
}
1.我们先创建游戏场景 auto scene = GameLayer::createScene(); GameLayer是GameScene文件中的类
2.TransitionCrossFade *pAnimateScene = TransitionCrossFade::create(1, scene); 创建一个以某种形式进入的场景。在一秒内,scene以自上而下形式替换前场景。TransitionCrossFade不是自上而下。
3.Director::getInstance()->replaceScene(pAnimateScene); 导演要求场景切换。如果你不想以某种形式切换场景,那就用
Director::getInstance()->replaceScene(scene);直接场景替换。
这样替换场景就解决了,下面让我们说下GameScene类类文件中的实现吧。
四,游戏开始之后,背景图的无限循环
我们先创建GameScene.h文件
#ifndef __GAME_SCENE_H__
#define __GAME_SCENE_H__
#include "cocos2d.h"
#include "PlaneLayer.h"
USING_NS_CC;
enum EnBackground
{
e_BackgroundA = 1, // 背景1
e_BackgroundB = 2, // 背景2 与背景一样,只是用来循环用
};
class GameLayer : public Layer
{
public:
static cocos2d::Scene* createScene();
virtual bool init();
CREATE_FUNC(GameLayer);
public:
void backgroundMove(float dt); // 背景移动
public:
PlaneLayer *planeLayer;
};
#endif // __GAME_SCENE_H__
结构和HelloWorldScene.h文件差不多,可以参考HelloWorldScene文件。PlaneLayer是创建飞机layer的,等下会说,可以注释掉,先实现背景循环移动效果。
和HelloWorldScene一样,我们也是在GameLayer::init()函数中加入背景
bool GameLayer::init()
{
if (!Layer::init())
{
return false;
}
// 启动触摸机制
this->setTouchEnabled(true);
// 背景无限滚动
auto backgroundA = Sprite::create("ui/shoot_background/background.png");
backgroundA->setTag(e_BackgroundA);
backgroundA->setAnchorPoint(Point::ZERO);
backgroundA->setPosition(Point::ZERO);
this->addChild(backgroundA);
auto backgroundB = Sprite::create("ui/shoot_background/background.png");
backgroundB->setTag(e_BackgroundB);
backgroundB->setAnchorPoint(Point::ZERO);
backgroundB->setPosition(Point::ZERO);
this->addChild(backgroundB);
// 每帧都调用的函数
this->schedule(schedule_selector(GameLayer::backgroundMove));
// 加入飞机
planeLayer = PlaneLayer::create();
this->addChild(planeLayer);
}
1 this->setTouchEnabled(true);触摸机制在编译成的apk文件后,可以实现手指触屏。
2 backgroundA - > setTag(e_BackgroundA);设置标志,这样的话,在本场景scene中就可以通过这个标志找到这个背景精灵
如:this- > getChildByTag(EnBackground :: e_BackgroundA);
3 backgroundA->setAnchorPoint(Point::ZERO);设置描点,就是设置物体的中心,可以查下描点的定义。
4 this->schedule(schedule_selector(GameLayer::backgroundMove));如果你想要时时检测东西,就可以用这个。它每帧都调用GameLayer::backgroundMove函数。如果你要2秒调用一次GameLayer::backgroundMove,就这样写:
this->schedule(schedule_selector(GameLayer::backgroundMove,2));
因为GameScene是主游戏场景,所以把所有的Layer元素放在进来,我机就是其中之一。不过在这里我们可以先把我机的部分屏蔽掉。等写完
PlaneLayer文件再打开。
本节最重要的部分:如何让背景无限循环~让我们实现这个每帧都被调用的GameLayer::backgroundMove
void GameLayer::backgroundMove(float dt)
{
Sprite *pBackgroundA = (Sprite*)this->getChildByTag(EnBackground::e_BackgroundA);
Sprite *pBackgroundB = (Sprite*)this->getChildByTag(EnBackground::e_BackgroundB);
pBackgroundA->setPositionY(pBackgroundA->getPositionY() - 2);
pBackgroundB->setPositionY(pBackgroundA->getPositionY() + pBackgroundA->getContentSize().height);
int a = pBackgroundA->getPositionY();
int b = pBackgroundB->getPositionY();
if (0 == pBackgroundB->getPositionY())
{
pBackgroundA->setPositionY(0);
}
}
通过刚才的标志,我们可以找到场景里面的背景A和背景B。backgroundMove(一帧调用一次,60帧是一秒,director->setAnimationInterval(1.0 / 60))。
我们通过资源resource路径找到ui/shoot_background/background.png图片,右键查看图片信息,发现背景图高度是842像素,背景高度每次-2,这样就可以达到0。因为一开始背景A的PositionY = 0 - 2 (图片下拉2个像素),那么背景B的PositionY = 842 +(-2)= 840(图片向上拉840像素),刚好无缝连接。还是不懂得同学,好好想一下,或者固定背景位置,看下效果就懂了。当背景B的图片位置到0了,这时候把背景A的再次设置为0,重复之前的过程。大家现在可以去试下背景无限循环的效果了~
GameScene.cpp文件代码:
#include "GameScene.h"
cocos2d::Scene* GameLayer::createScene()
{
auto scene = Scene::create();
auto layer = GameLayer::create();
scene->addChild(layer);
return scene;
}
bool GameLayer::init()
{
if (!Layer::init())
{
return false;
}
// 启动触摸机制
this->setTouchEnabled(true);
// 背景无限滚动
auto backgroundA = Sprite::create("ui/shoot_background/background.png");
backgroundA->setTag(e_BackgroundA);
backgroundA->setAnchorPoint(Point::ZERO);
backgroundA->setPosition(Point::ZERO);
this->addChild(backgroundA);
auto backgroundB = Sprite::create("ui/shoot_background/background.png");
backgroundB->setTag(e_BackgroundB);
backgroundB->setAnchorPoint(Point::ZERO);
backgroundB->setPosition(Point::ZERO);
this->addChild(backgroundB);
// 每帧都调用的函数
this->schedule(schedule_selector(GameLayer::backgroundMove));
// 加入飞机
planeLayer = PlaneLayer::create();
this->addChild(planeLayer);
}
void GameLayer::backgroundMove(float dt)
{
Sprite *pBackgroundA = (Sprite*)this->getChildByTag(EnBackground::e_BackgroundA);
Sprite *pBackgroundB = (Sprite*)this->getChildByTag(EnBackground::e_BackgroundB);
pBackgroundA->setPositionY(pBackgroundA->getPositionY() - 2);
pBackgroundB->setPositionY(pBackgroundA->getPositionY() + pBackgroundA->getContentSize().height);
int a = pBackgroundA->getPositionY();
int b = pBackgroundB->getPositionY();
if (0 == pBackgroundB->getPositionY())
{
pBackgroundA->setPositionY(0);
}
}
五,场景内加入可以移动的我机
老规矩看下PlaneLayer.h文件
#include "cocos2d.h"
USING_NS_CC;
enum Enum_Plane
{
AIRPLANE = 1,
};
class PlaneLayer : public Layer
{
public:
PlaneLayer();
~PlaneLayer();
virtual bool init();
CREATE_FUNC(PlaneLayer);
public:
void checkBorder(float dt); // 边界检测
public:
bool isAlive; // 飞机是否活着
};
和之前的GameScene.h文件比,少了什么呢?没错,少了static cocos2d::Scene* createScene();场景创建。因为我们的主场景就是GameScene,现在需要的是往主场景中加入Layer、sprite等等元素。因此我们只要实现layer就可以了。
还是那句话,老规矩〜我们先在PlaneLayer::init()中加入我机:
// 我机
Size winSize = Director::getInstance()->getWinSize();
auto sprite = Sprite::create("ui/shoot/hero1.png");
sprite->setPosition(Point(winSize.width/2,sprite->getContentSize().height/2));
sprite->setTag(AIRPLANE);
this->addChild(sprite);
我方飞机的位置是底部正中央。
加完飞机,我们需要处理什么呢?想想~~1.可以触摸拖动~~2.不能跑到屏幕外面去~~
1,拖动实现也是在PlaneLayer::init()中实现:
// 我机触摸
auto listener = EventListenerTouchOneByOne::create();
listener->setSwallowTouches(true);
listener->onTouchBegan = [](Touch* touch, Event *event){
auto target = static_cast<Sprite*>(event->getCurrentTarget());
Point locationInNode = target->convertToNodeSpace(touch->getLocation());
Size s = target->getContentSize();
Rect rect = Rect(0,0,s.width,s.height);
if (rect.containsPoint(locationInNode))
{
return true;
}
else
{
return false;
}
};
listener->onTouchMoved =[](Touch* touch, Event *event){
auto target = static_cast<Sprite*>(event->getCurrentTarget());
target->setPosition(target->getPosition() + touch->getDelta());
};
listener->onTouchEnded = [=](Touch* touch, Event* event){
};
以上就是触摸机制的实现,以后有空给你们解释一下,或者你们自己看看网上资料吧~
之后就是把我们的飞机加入到触摸机制中去(在PlaneLayer::init()中):
//将触摸监听添加到eventDispacher中去
_eventDispatcher->addEventListenerWithSceneGraphPriority(listener, sprite);
2,时时检测飞机是否飞出天外天〜,大家还记得这个函数吗?
// 每帧都调用的函数
this->schedule(schedule_selector(PlaneLayer::checkBorder));
1秒内调用60次PlaneLayer :: checkBorder。
void PlaneLayer::checkBorder( float dt )
{
//进行边界判断,不可超出屏幕
Point location = this->getChildByTag(AIRPLANE)->getPosition();
Size winSize=Director::sharedDirector()->getWinSize(); // 获取opengl视图窗口大小
Size planeSize=this->getChildByTag(AIRPLANE)->getContentSize(); // 返回的就是这个矩形的大小,只是是逻辑尺寸, 而不是像素的
if (location.x<planeSize.width/2)
{
location.x=planeSize.width/2;
}
if (location.x>winSize.width-planeSize.width/2)
{
location.x=winSize.width-planeSize.width/2;
}
if (location.y<planeSize.height/2)
{
location.y=planeSize.height/2;
}
if (location.y>winSize.height-planeSize.height/2)
{
location.y=winSize.height-planeSize.height/2;
}
this->getChildByTag(AIRPLANE)->setPosition(location);
}
这里的位置检测就是你的数学逻辑了,你可以设置下飞机的描点为0,0,看看会有什么效果,描点默认为0.5,0.5。
PlaneLayer.cpp文件
#include "PlaneLayer.h"
PlaneLayer::PlaneLayer()
{
isAlive = true;
}
PlaneLayer::~PlaneLayer()
{
}
bool PlaneLayer::init()
{
if (!Layer::init())
{
return false;
}
// 我机
Size winSize = Director::getInstance()->getWinSize();
auto sprite = Sprite::create("ui/shoot/hero1.png");
sprite->setPosition(Point(winSize.width/2,sprite->getContentSize().height/2));
sprite->setTag(AIRPLANE);
this->addChild(sprite);
// 我机触摸
auto listener = EventListenerTouchOneByOne::create();
listener->setSwallowTouches(true);
listener->onTouchBegan = [](Touch* touch, Event *event){
auto target = static_cast<Sprite*>(event->getCurrentTarget());
Point locationInNode = target->convertToNodeSpace(touch->getLocation());
Size s = target->getContentSize();
Rect rect = Rect(0,0,s.width,s.height);
if (rect.containsPoint(locationInNode))
{
return true;
}
else
{
return false;
}
};
listener->onTouchMoved =[](Touch* touch, Event *event){
auto target = static_cast<Sprite*>(event->getCurrentTarget());
target->setPosition(target->getPosition() + touch->getDelta());
};
listener->onTouchEnded = [=](Touch* touch, Event* event){
};
//将触摸监听添加到eventDispacher中去
_eventDispatcher->addEventListenerWithSceneGraphPriority(listener, sprite);
// 每帧都调用的函数
this->schedule(schedule_selector(PlaneLayer::checkBorder));
return true;
}
void PlaneLayer::checkBorder( float dt )
{
//进行边界判断,不可超出屏幕
Point location = this->getChildByTag(AIRPLANE)->getPosition();
Size winSize=Director::sharedDirector()->getWinSize(); // 获取opengl视图窗口大小
Size planeSize=this->getChildByTag(AIRPLANE)->getContentSize(); // 返回的就是这个矩形的大小,只是是逻辑尺寸, 而不是像素的
if (location.x<planeSize.width/2)
{
location.x=planeSize.width/2;
}
if (location.x>winSize.width-planeSize.width/2)
{
location.x=winSize.width-planeSize.width/2;
}
if (location.y<planeSize.height/2)
{
location.y=planeSize.height/2;
}
if (location.y>winSize.height-planeSize.height/2)
{
location.y=winSize.height-planeSize.height/2;
}
this->getChildByTag(AIRPLANE)->setPosition(location);
}
到这里,就拥有了一个无限循环的背景图 加 一个可以随意移动的飞机。