【cocos2d-x游戏开发】 从零单排之(二)

这一章,我们要实现游戏开始菜单(其实只有一个按钮O(∩_∩)O~),然后给游戏场景添加人物和地图背景。先剧透一下今天要实现的效果



首先,介绍一下要实现的功能,进入游戏之后,有一个开始界面,点击里面的开始游戏按钮,进入游戏界面,游戏里有向后滚动的背景,和跑动的主角。 我们先根据需求,把需要的类定义出来,然后一个一个去实现。

SceneStart 游戏开始界面
SceneGame 游戏场景,人物地图等等都显示在这里面
|
|------------GameCamera 游戏视角(就是我们在屏幕上看见的区域)
|
|------------Player  玩家
|
|------------MapBg 地图背景


一 实现开始界面:SceneStart
我们先定义出需要实现的方法
class SceneStart:public cocos2d::Scene
{
public:
	SceneStart();
	~SceneStart();
	//初始化方法
	bool init();
	//按钮点击回调
	void menuItemClicked(cocos2d::Ref*);
	//获取实例方法
	static SceneStart* create();
private:
};
下面是具体的实现
#include "SceneStart.h"
#include "Utils.h"
#include "SceneGame.h"
USING_NS_CC;
SceneStart::SceneStart()
{
	
}

SceneStart::~SceneStart()
{
}

SceneStart*  SceneStart::create()
{
	SceneStart* tscene = new SceneStart();
	if(tscene && tscene->init())
	{
		tscene->autorelease();
		return tscene;
	}
	return nullptr;
}

bool SceneStart::init()
{
	if(Scene::init())
	{
		//添加开始按钮
		MenuItemFont* titem = MenuItemFont::create(Utils::getInstance()->G2U("开始游戏"),CC_CALLBACK_1(SceneStart::menuItemClicked,this));
		Menu* tmenu = Menu::createWithItem(titem);
		addChild(tmenu);
		
		return true;
	}
	return false;
}

//开始按钮点击回调
void SceneStart::menuItemClicked(cocos2d::Ref*)
{
	SceneGame* tgame = SceneGame::create();
	Director::getInstance()->replaceScene(tgame);
}
这里要注意一点,Cocos2d-x里支持的语言是UTF-8格式,但windows里的汉语文字gb2312,我们需要手动用转换成unicode格式。我这里直接用了一个网上找的方法char* Utils::G2U(const char* gb2312)  ,定义在了Utils.h中,下面贴出实现方法
char* Utils::G2U(const char* gb2312)  
{
	 int len = MultiByteToWideChar(CP_ACP, 0, gb2312, -1, NULL, 0);    
	 wchar_t* wstr = new wchar_t[len+1];    
	 memset(wstr, 0, len+1);    
	 MultiByteToWideChar(CP_ACP, 0, gb2312, -1, wstr, len);    
	 len = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL);    
	 char* str = new char[len+1];    
	 memset(str, 0, len+1);    
	 WideCharToMultiByte(CP_UTF8, 0, wstr, -1, str, len, NULL, NULL);    
	 if(wstr) delete[] wstr;    
	 return str;    
}

二 游戏视口 GameCamera,因为地图可能很大,玩家角色也可能在地图上的任何位置出现,但我们的屏幕大小是固定的,只能显示出游戏世界的一部分,所以,我们抽象出这个游戏视角类,来走位玩家看游戏世界的窗口。
#include "cocos2d.h"
class GameCamera:public cocos2d::Ref
{
public:
	GameCamera();
	~GameCamera();
	static GameCamera* getInstance();
	//视口的位置和大小
	const cocos2d::Rect getRect();
	//设置水平移动速度(暂时只实现水平移动)
	void setSpeed(float speed);
	//充值视口位置
	void reset();
	//随时间刷新
	void refresh(float dt);
private:
	static GameCamera* s_instance;
	float m_time;
	float m_speed;
	cocos2d::Rect m_rect;
};
#include "GameCamera.h"
USING_NS_CC;
GameCamera* GameCamera::s_instance = nullptr;
GameCamera::GameCamera():m_time(0),m_rect(Rect(0,0,960,570)),m_speed(0)
{
}
GameCamera::~GameCamera()
{
}
GameCamera* GameCamera::getInstance()
{
	if(!s_instance)
	{
		s_instance = new GameCamera();
	}
	return s_instance;
}
void GameCamera::setSpeed(float speed)
{
	m_speed = speed;
}
void GameCamera::reset()
{
	m_rect.origin = m_rect.origin.ZERO;
}
const Rect GameCamera::getRect()
{
	return m_rect;
}
void GameCamera::refresh(float dt)
{
	m_time += dt;
	m_rect.origin.x = m_time * m_speed;
}

三 循环滚动的地图 MapBg , 因为主角要在X方向上一直跑动,所以地图要在横向上能循环接上的,我们这里用2张同样的图片,前后相接,实现地图的循环滚动。
class MapBg:public cocos2d::Sprite
{
public:
	MapBg();
	~MapBg();
	bool init();
	static MapBg* create();
	//根据视口的位置更新地图显示
	void updateByCameraPos(float posx,float posy);
private:
	cocos2d::Sprite* m_bg1;
	cocos2d::Sprite* m_bg2;
};
MapBg* MapBg::create()
{
	MapBg* tbg = new MapBg();
	if(tbg && tbg->init())
	{
		tbg->autorelease();
		return tbg;
	}else 
	{
		CC_SAFE_DELETE(tbg);
	}
	return nullptr;
}

bool MapBg::init()
{
	if(Sprite::init())
	{
		Texture2D* tex = TextureCache::getInstance()->addImage("map.jpg");
		
		//因为2张地图背景是来自同一个texture,所以可以使用SpriteBatchNode同时渲染,
		//这样opengl在渲染时可以只draw一次,提高渲染效率
		SpriteBatchNode* batch = SpriteBatchNode::createWithTexture(tex);
		addChild(batch);
		
		m_bg1 = Sprite::createWithTexture(tex);
		m_bg1->setAnchorPoint(Point(0,0));
		m_bg2 = Sprite::createWithTexture(tex);
		m_bg2->setAnchorPoint(Point(0,0));

		batch->addChild(m_bg1);
		batch->addChild(m_bg2);

		return true;
	}
	return false;
}

void MapBg::updateByCameraPos(float posx,float posy)
{
	float twidth = m_bg1->getContentSize().width;
	int dis = static_cast
      
      
       
       (posx)%static_cast
       
       
        
        (twidth);
	m_bg1->setPositionX(-dis);
	m_bg2->setPositionX(m_bg1->getPosition().x + twidth);
}

       
       
      
      

四 跑动的主角 Player
主角的跑动动作是用一个3帧的序列帧循环播放实现的,(这里为了节省时间没有实现站立、跑动、跳跃等动作的切换,统一使用了一个跑动动作)。动画的实现有很多种办法,这里用的是每隔一定的时间间隔改变Sprite的Frame。也可以用Sprite自带的runAction,通过传给action系统一个定义好的animate来实现(其实是因为我开始没有想到用这个o(╯□╰)o)。
主角在游戏世界里,就要有世界坐标,我们用m_x和m_y这2个世界坐标,来区别主角在opengl坐标系上的坐标;然后为主角的移动,增加speedx和speedy 2个属性,这样,我们就可以在update方法里,刷新主角的坐标了(世界坐标+速度*时间 计算出世界坐标后,在根据视口的位置,更新主角在屏幕上的显示位置);
class Player:public cocos2d::Sprite
{
public:
	Player(float posx,float posy);
	virtual ~Player();
	virtual bool init();
	void update(float dt);
	static Player* create(float posx,float posy);
private:
	float timelast;
	int m_currentFrame;
	float m_x;
	float m_y;
	float m_speedY;
	float m_speedX;
	void updateViewByCurrentFrame();
};
bool Player::init()
{
	Texture2D* tex = Director::getInstance()->getTextureCache()->addImage("role.png");
	SpriteFrame* tframe;
	char* tname = new char[10];
	for(int i = 0 ;i < 3 ;i++)
	{
		tname = new char[10];
		sprintf(tname,"body%d",i+1);
		tframe = SpriteFrame::createWithTexture(tex,Rect(82*i,0,82,103));
		SpriteFrameCache::getInstance()->addSpriteFrame(tframe,tname);
	}
	
	if(Sprite::initWithSpriteFrameName("body1"))
	{
		this->scheduleUpdate();
		return true;
	}
	return false;
}

Player* Player::create(float posx,float posy)
{
	Player* tp = new Player(posx,posy);
	if(tp && tp->init())
	{
		tp->autorelease();
		return tp;
	}else 
	{
		CC_SAFE_DELETE(tp);
	}
	return nullptr;
}

void Player::update(float dt)
{
	Sprite::update(dt);

	timelast += dt;
	//play movie
	if(timelast > 0.2f)
	{
		timelast -= 0.2f;

		++m_currentFrame;
		if(m_currentFrame > 3)
		{
			m_currentFrame = 1;
		}

		updateViewByCurrentFrame();
	}
	//position change
	float targetX = m_x + m_speedX*dt;
	float targetY = m_y + m_speedY*dt;
	GameCamera* camera = GameCamera::getInstance();
	const Rect trect = camera->getRect();
	this->setPosition(targetX - trect.origin.x,targetY - trect.origin.y);
}

void Player::updateViewByCurrentFrame()
{
	char* texName = new char[10];
	sprintf(texName,"body%d",m_currentFrame);
	this->setSpriteFrame(SpriteFrameCache::getInstance()->getSpriteFrameByName(texName));
}

五 都到游戏场景里来 GameScene ,把之前写好得游戏元素都添加到游戏场景中。
#include "Player.h"
class SceneGame:public cocos2d::Scene
{
public:
	SceneGame();
	~SceneGame();
	
	static SceneGame* create();
	bool init();
	void update(float dt);
private:
	MapBg* m_map;
	Player* m_player;
};
bool SceneGame::init()
{
	if(Scene::init())
	{
		//启动刷新
		this->scheduleUpdate();

		//初始化游戏视角
		GameCamera::getInstance()->setSpeed(50);

		//添加地图
		m_map = MapBg::create();
		addChild(m_map);

		//添加人物
		m_player = Player::create(600,110);
		addChild(m_player);

		return true;
	}
	return false;
}

void SceneGame::update(float dt)
{
	Scene::update(dt);

    //刷新游戏视口
	GameCamera* camera = GameCamera::getInstance();
	camera->refresh(dt);
	
	//更新地图
	const Rect trect = camera->getRect();
	m_map->updateByCameraPos(trect.origin.x,trect.origin.y);
}



本章内容到这里就结束了,下一章,我们将会给主角加上奔跑跳跃的能力。


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值