看了cocos2d有一段时间了,简单的小游戏还是可以写出来。这个过程主要还是看,而很少动手去写,这样本身水平很难提高。就好比看完设计模式,现在依旧不会用,时间一久,也忘记的差不多了。俗话说,纸上得来终觉浅,绝知此事要躬行。因此,决定写一个高仿发飞机的小游戏,虽然简单,但还是要坚持写完。
这个系列主要还是基于我想到哪就写到哪,不定期更新。
1.开始界面
首先,我们得找到微信打飞机的图片资源,直接到下面链接下载就可以了:
http://download.csdn.net/detail/q100036q/7524993
解压后:
资源图片准备就绪,我们就可以开始编码工作了,依旧是看图写代码,一步一步搭建。
开始画面很简单,一张背景图片再加一个animation,便构成了开始界面。
代码如下:
- #ifndef _START_GAME_SCENE_H_
- #define _START_GAME_SCENE_H_
- #include "cocos2d.h"
- USING_NS_CC;
- class StartGameScene : public Layer
- {
- public:
- static cocos2d::Scene* createScene();
- virtual bool init();
- CREATE_FUNC(StartGameScene);
- void loadingHandler();
- void loadingDone();
- };
- #endif
- #include "StartGameScene.h"
- #include "PlayGameScene.h"
- Scene* StartGameScene::createScene()
- {
- // 'scene' is an autorelease object
- auto scene = Scene::create();
- // 'layer' is an autorelease object
- auto layer = StartGameScene::create();
- // add layer as a child to scene
- scene->addChild(layer);
- // return the scene
- return scene;
- }
- // on "init" you need to initialize your instance
- bool StartGameScene::init()
- {
- //////////////////////////////
- // 1. super init first
- if ( !Layer::init() )
- {
- return false;
- }
- Size visibleSize = Director::getInstance()->getVisibleSize();
- Vec2 origin = Director::getInstance()->getVisibleOrigin();
- // add background image
- auto sprite = Sprite::create("shoot_background/background.png");
- // position the sprite on the center of the screen
- sprite->setPosition(Vec2(visibleSize.width/2 + origin.x,visibleSize.height/2 + origin.y));
- // add the sprite as a child to this layer
- this->addChild(sprite,0);
- //加载页面
- loadingHandler();
- return true;
- }
- //loading handler
- void StartGameScene::loadingHandler()
- {
- Size visibleSize = Director::getInstance()->getVisibleSize();
- //start:开始界面的animation
- auto loadingSp = Sprite::create();
- loadingSp->setPosition(Point(visibleSize.width / 2,visibleSize.height / 2));
- this->addChild(loadingSp,1);
- Vector<SpriteFrame*> framelist;
- for(int i = 1; i <=4; i++)
- {
- SpriteFrame* sf = SpriteFrame::create(String::createWithFormat("shoot_background/game_loading%d.png",i)->_string,Rect(0,186,38));
- framelist.pushBack(sf);
- }
- Animation* an = Animation::createWithSpriteFrames(framelist,0.5f);
- auto animate = Animate::create(an);
- auto repeat = Repeat::create(animate,3);//播放三次
- //回调函数
- auto callback = CallFunc::create(CC_CALLBACK_0(StartGameScene::loadingDone,this));
- auto sequence = Sequence::create(repeat,callback,NULL);
- loadingSp->runAction(sequence);
- //end:开始界面的animation
- }
- //开始界面播放完,直接进入游戏场景
- void StartGameScene::loadingDone()
- {
- Director::getInstance()->replaceScene(PlayGameScene::createScene());
- }
2.背景移动
进入游戏后,要让背景图片动起来,并且无缝衔接。实际上,用两张背景图片就可以达到这种效果。当第一张移出屏幕的时候,立即放置到最上面,衔接第二张,达到无缝衔接的效果。主要代码如下:
- //run the background
- auto bg_1 = Sprite::create("shoot_background/background.png");
- bg_1->setAnchorPoint(Point(0,0 ));
- bg_1->setPosition(Point(0,0));
- bg_1->setTag(BG_1);
- this->addChild(bg_1,0);
- auto bg_2 = Sprite::create("shoot_background/background.png");
- bg_2->setAnchorPoint(Point(0,0 ));
- bg_2->setPosition(Point(0,bg_1->getContentSize().height));
- bg_2->setTag(BG_2);
- this->addChild(bg_2,0);
- //run background image
- this->schedule(schedule_selector(PlayGameScene::runBackground),0.01f);
- void PlayGameScene::runBackground(float dt)
- {
- auto visibleSize = Director::getInstance()->getVisibleSize();
- auto bg_1 = this->getChildByTag(BG_1);
- auto bg_2 = this->getChildByTag(BG_2);
- int bg_height = bg_1->getContentSize().height;
- bg_1->setPositionY(bg_1->getPositionY() - bg_speed);
- bg_2->setPositionY(bg_2->getPositionY() - bg_speed);
- //bg2移出屏幕后衔接在bg1后面
- if(bg_2->getPositionY() <= 0)
- {
- bg_1->setPositionY(bg_2->getPositionY() + bg_height);
- }
- //bg1移出屏幕后衔接在bg2后面
- if(bg_1->getPositionY() <= 0)
- {
- bg_2->setPositionY(bg_1->getPositionY() + bg_height);
- }
- }
3.加载主飞机
经过步骤2,背景也可以无缝移动了,把主飞机(hero_plane)放上去,并且注册他的触碰事件检测,也就是我们手指触碰主飞机的时候,飞机能够跟着我们手指移动而移动。这样的效果也很简单,在层中去注册touchevent事件处理。点击时如果是touch飞机,设置一个标志位,touchmove事件的时候,不断去更新飞机的位置,这样就可以达到效果了。
主要代码如下:
- //add plane hero
- Sprite* hero = Sprite::create("hero1.png");
- PlaneHero* hero_plane = PlaneHero::create(hero);
- hero_plane->setPosition(Point(visibleSize.width / 2,visibleSize.height / 2));
- hero_plane->setTag(HERO_PLANE);
- hero_plane->setAnchorPoint(Point(0,0));
- this->addChild(hero_plane,1);
- //touch event handler
- auto touchListener = EventListenerTouchOneByOne::create();
- touchListener->setSwallowTouches(true);
- touchListener->onTouchBegan = CC_CALLBACK_2(PlayGameScene::onTouchBegan,this);
- touchListener->onTouchMoved = CC_CALLBACK_2(PlayGameScene::onTouchMoved,this);
- touchListener->onTouchCancelled = CC_CALLBACK_2(PlayGameScene::onTouchEnded,this);
- _eventDispatcher->addEventListenerWithSceneGraPHPriority(touchListener,this);
- //Touch event handler
- bool PlayGameScene::onTouchBegan(Touch *touch,Event* event)
- {
- Point touchPoint = touch->getLocation();
- bTouchPlane = false;
- auto hero_plane = this->getChildByTag(HERO_PLANE);
- Point hero_pos = hero_plane->getPosition();
- Size hero_size = hero_plane->getContentSize();
- log("hero_pos x = %f,y = %f",hero_pos.x,hero_pos.y);
- log("touchPoint x = %f,touchPoint.x,touchPoint.y);
- log("hero_size width = %f,height = %f",hero_size.width,hero_size.height);
- if(touchPoint.x >= hero_pos.x && touchPoint.x <= (hero_pos.x + hero_size.width) && touchPoint.y >= hero_pos.y && touchPoint.y <= (touchPoint.y + hero_size.height))
- {
- //touch the plane,mark this flag for use
- bTouchPlane = true;
- }
- return true;
- }
- void PlayGameScene::onTouchMoved(Touch *touch,Event* event)
- {
- if(bTouchPlane == true)
- {
- //move the hero_pane with out touch on screen
- auto hero_plane = this->getChildByTag(HERO_PLANE);
- Point pos = touch->getLocation();
- hero_plane->setPosition(pos);
- }
- }
- void PlayGameScene::onTouchEnded(Touch *touch,Event* event)
- {
- bTouchPlane = false;
- }
4.子弹管理器
首先,我们写一个基类,用来绑定一个精灵,所有的飞机,子弹都会继承他。
接着,写一个子弹的基类,继承自Entity,他包含子弹的共有属性。不同的子弹类型都会继承自这个子弹基类。方便扩展。
- #ifndef _ENTITY_H_
- #define _ENTITY_H_
- #include "cocos2d.h"
- USING_NS_CC;
- class Entity : public Node
- {
- public:
- Entity();
- ~Entity();
- void bindSprite(Sprite* sprite);
- Sprite* getSprite();
- private:
- Sprite* m_sprite;
- };
- #endif
- #include "Entity.h"
- Entity::Entity()
- {
- m_sprite = NULL;
- }
- Entity::~Entity()
- {}
- void Entity::bindSprite(Sprite* sprite)
- {
- if(this->m_sprite != NULL)
- {
- m_sprite->removeFromParentAndCleanup(true);
- }
- this->m_sprite = sprite;
- this->addChild(m_sprite);
- Size size = m_sprite->getContentSize();
- this->setContentSize(size);
- }
- Sprite* Entity::getSprite()
- {
- return this->m_sprite;
- }
紧接着,写一个普通类型的子弹,在这里其实主要是写入子弹的速度。不同类型的子弹拥有不同的属性值,我们先简单的设置子弹的速度就可以。以后需要扩展直接对这个类进行。
- #ifndef _BULLETBASE_BASE_H_
- #define _BULLETBASE_BASE_H_
- #include "Entity.h"
- #define SPEED_DEFAULT 10
- #define SPEED_NORMAL 5
- class BulletBase : public Entity
- {
- public:
- BulletBase();
- ~BulletBase();
- //子弹是否在使用中
- void setUsed(bool isUsed);
- bool isUsed();
- bool isArrive();
- //设置子弹速度
- void setSpeed(int speed);
- int getSpeed();
- //是否飞出屏幕
- bool isMoveOutScreen();
- void setIsMoveOutScreen(bool outScreen);
- //是否加进层里面
- //bool isAddInLayer();
- //void setIsAddInLayer(bool inLayer);
- protected:
- bool m_isArrive;
- int m_speed;
- private:
- bool m_isUsed;
- bool m_isOutScreen;
- //bool m_isAddInLayer;
- };
- #endif
- #include "BulletBase.h"
- BulletBase::BulletBase()
- {
- m_isUsed = false;
- m_isArrive = false;
- m_speed = SPEED_NORMAL;
- m_isOutScreen = false;
- //m_isAddInLayer = false;
- }
- BulletBase::~BulletBase()
- {
- }
- void BulletBase::setUsed(bool isUsed)
- {
- this->m_isUsed = isUsed;
- }
- bool BulletBase::isUsed()
- {
- return this->m_isUsed;
- }
- bool BulletBase::isArrive()
- {
- return this->m_isArrive;
- }
- //设置子弹速度
- void BulletBase::setSpeed(int speed)
- {
- this->m_speed = speed;
- }
- int BulletBase::getSpeed()
- {
- return this->m_speed;
- }
- //是否已经飞出屏幕
- bool BulletBase::isMoveOutScreen()
- {
- return this->m_isOutScreen;
- }
- void BulletBase::setIsMoveOutScreen(bool outScreen)
- {
- this->m_isOutScreen = outScreen;
- }
- #if 0
- //是否加进层里面
- bool BulletBase::isAddInLayer()
- {
- return this->m_isAddInLayer;
- }
- void BulletBase::setIsAddInLayer(bool inLayer)
- {
- this->m_isAddInLayer = inLayer;
- }
- #endif
- #ifndef _BULLET_NORMAL_H_
- #define _BULLET_NORMAL_H_
- #include "BulletBase.h"
- class BulletNormal : public BulletBase
- {
- public:
- BulletNormal();
- ~BulletNormal();
- CREATE_FUNC(BulletNormal);
- virtual bool init();
- static BulletNormal* create(Sprite* sprite);
- bool init(Sprite* sprite);
- private:
- void moveEnd();
- };
- #endif
最后,创建子弹管理器,生成子弹,保存到一个列表里面,需要的时候直接取出。同时,子弹的飞行也在管理器中做处理,不断检测子弹并且设置子弹的运行。
- #include "BulletNormal.h"
- BulletNormal::BulletNormal()
- {
- m_speed = SPEED_NORMAL;
- }
- BulletNormal::~BulletNormal()
- {}
- bool BulletNormal::init()
- {
- return true;
- }
- BulletNormal* BulletNormal::create(Sprite* sprite)
- {
- BulletNormal* bNor = new BulletNormal();
- if(bNor && bNor->init(sprite))
- {
- bNor->autorelease();
- }
- else
- {
- CC_SAFE_DELETE(bNor);
- }
- return bNor;
- }
- bool BulletNormal::init(Sprite* sprite)
- {
- bool ret = false;
- bindSprite(sprite);
- ret = true;
- return ret;
- }
- void BulletNormal::moveEnd()
- {
- m_isArrive = true;
- }
- #ifndef _BULLET_MANAGER_H_
- #define _BULLET_MANAGER_H_
- #include "cocos2d.h"
- USING_NS_CC;
- #define BULLET_MAX_CACHE_NUM 20 //子弹缓存数量
- class BulletBase;
- class BulletManager : public Node
- {
- public:
- BulletManager();
- ~BulletManager();
- static BulletManager* create();
- bool init();
- //获取未用的子弹
- BulletBase* getUnusedBullet();
- private:
- Vector<BulletBase*> m_bulletList;//子弹列表,保存子弹
- void createBullets();//创建缓存子弹
- void bulletLogicCheck(float dt);//子弹逻辑
- };
- #endif
- #include "BulletManager.h"
- #include "BulletBase.h"
- #include "BulletNormal.h"
- BulletManager::BulletManager()
- {}
- BulletManager::~BulletManager()
- {}
- BulletManager* BulletManager::create()
- {
- BulletManager* bulletMgr = new BulletManager();
- if(bulletMgr && bulletMgr->init())
- {
- bulletMgr->autorelease();
- }
- else
- {
- CC_SAFE_DELETE(bulletMgr);
- }
- return bulletMgr;
- }
- bool BulletManager::init()
- {
- //创建子弹列表
- createBullets();
- log("m_bulletList size() = %d",m_bulletList.size());
- //循环检测子弹列表
- this->schedule(schedule_selector(BulletManager::bulletLogicCheck));
- return true;
- }
- //获取未用的子弹
- BulletBase* BulletManager::getUnusedBullet()
- {
- log("m_bulletList size() = %d",m_bulletList.size());
- for(int i = 0; i < m_bulletList.size(); i++)
- {
- BulletBase* bullet = m_bulletList.at(i);
- if(bullet->isUsed() == false)
- {
- bullet->setUsed(true);
- bullet->setIsMoveOutScreen(false);
- return bullet;
- }
- }
- #if 0 //i don't know why
- for(auto bullet : m_bulletList)
- {
- if(bullet->isUsed() == false)
- {
- bullet->setUsed(true);
- bullet->setIsMoveOutScreen(false);
- return bullet;
- }
- }
- #endif
- log("no bullet unused");
- return NULL;
- }
- //创建子弹缓存
- void BulletManager::createBullets()
- {
- BulletBase* bullet = NULL;
- for(int i = 0; i < BULLET_MAX_CACHE_NUM; i++)
- {
- auto bullet_sprite = Sprite::create("bullet1.png");
- bullet = BulletNormal::create(bullet_sprite);
- bullet->setUsed(false);
- bullet->setIsMoveOutScreen(false);
- m_bulletList.pushBack(bullet);
- this->addChild(bullet);
- }
- log("m_bulletList size() = %d",m_bulletList.size());
- }
- //检测子弹
- void BulletManager::bulletLogicCheck(float dt)
- {
- auto visibleSize = Director::getInstance()->getVisibleSize();
- for(auto bullet : m_bulletList)
- {
- if(bullet->isUsed() == true)
- {
- //子弹运行
- int posY = bullet->getPositionY();
- posY += SPEED_DEFAULT;
- bullet->setPositionY(posY);
- //out of screen
- if(posY >= visibleSize.height)
- {
- bullet->setIsMoveOutScreen(true);
- bullet->setUsed(false);
- }
- }
- }
- }
以上全部写完,基本的画面就出来了,画面如下
好了 第一篇就先写到这里,后续会写第二篇。