上一篇:【一】仿微信飞机大战的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); }到这里,就拥有了一个无限循环的背景图 加 一个可以随意移动的飞机。