您的位置:首页 > 移动开发 > Cocos引擎

【cocos2d-x-3.2】【高仿微信打飞机系列一】【开始界面 子弹生成 背景滑动 主飞机】

2015-03-04 11:13 573 查看
看了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,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, y = %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.子弹管理器

首先,我们写一个基类,用来绑定一个精灵,所有的飞机,子弹都会继承他。

#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;
}
接着,写一个子弹的基类,继承自Entity,他包含子弹的共有属性。不同的子弹类型都会继承自这个子弹基类。方便扩展。

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

以上全部写完,基本的画面就出来了,画面如下



好了 第一篇就先写到这里,后续会写第二篇。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐