「cocos2d-x」虚拟摇杆实现(2)
2013-05-16 17:34
337 查看
上一篇文章「cocos2d-x」虚拟摇杆实现(1)讲了如何实现一个虚拟摇杆,但是这种方法实现的虚拟摇杆存在以下问题:
适用于屏幕与背景相同的情况,如果屏幕比背景小,要实现滚屏时,咱们的控制器虚拟摇杆就滑出屏幕了,之后游戏完全失去了控制。
本次采用了分层设计,将虚拟摇杆放在了HudLayer层,在屏幕滚动时,虚拟摇杆的位置会保持不变。
以下的视频演示了2种方法实现虚拟摇杆的差异:
http://www.56.com/u50/v_OTE2ODY1MzU.html
程序设计最重要的思想和设计模式,而不是掌握了多少API和函数,这一点深有体会:
初次在实现滚屏又不丢失摇杆时,我根据玩家的位置算出来摇杆的相对位置,然后实时地更改摇杆的位置,但是相对于屏幕来说,摇杆还是有滑出屏幕的可能,而且会在屏幕上浮动,位置不固定,用户体验极差;
利用分层模式,一个层为游戏背景层,专门显示背景图,再加一个HUD层,HUD层上面放游戏菜单、玩家的血条,虚拟摇杆等
在实现分层模式时,2个层之间要传递玩家的坐标,我在这里卡了很久,起初想在一个层中得到上层节点,然后再根据tag向下找,结果失败告终,几个类互相包含时,头文件错综复杂,用了#pragma once指令后,又引发了CC_SYNTHESIZE等宏定义的错误,总之,在2个CCLayer之间交换数据花了很多时间,VS中一堆莫名其妙的错误,以下为当时的代码。
2个节点之间的数据交互,建议将2个CCLayer封装为一个Scene或CCLayer类的数据成员,然后由父节点调用,这样可以很方便地完成数据交换。如下代码轻易地完成了hudlayer和gamelayer2个CCLayer之间玩家坐标的数据交换:
本次的例子进行了分模块设计,如下图所示:
GameLayer是游戏背景层,实现了游戏TMX地图的加载及地图滚动
HudLayer是玩家信息层,实现了虚拟摇杆实时控制玩家在地图中移动
GameScene是游戏场景层,把GameLayer和HudLayer作为实现变量封装进去
因为我们实现了自己的场景类,所以AppDelegate.cpp中默认的Helloword场景需要改成我们的。
GameScene类的声明代码:
GameScene类的实现代码:
GameLayer的声明:
GameLayer的实现:
HudLayer的声明:
HudLayer的实现:
适用于屏幕与背景相同的情况,如果屏幕比背景小,要实现滚屏时,咱们的控制器虚拟摇杆就滑出屏幕了,之后游戏完全失去了控制。
本次采用了分层设计,将虚拟摇杆放在了HudLayer层,在屏幕滚动时,虚拟摇杆的位置会保持不变。
以下的视频演示了2种方法实现虚拟摇杆的差异:
http://www.56.com/u50/v_OTE2ODY1MzU.html
程序设计最重要的思想和设计模式,而不是掌握了多少API和函数,这一点深有体会:
初次在实现滚屏又不丢失摇杆时,我根据玩家的位置算出来摇杆的相对位置,然后实时地更改摇杆的位置,但是相对于屏幕来说,摇杆还是有滑出屏幕的可能,而且会在屏幕上浮动,位置不固定,用户体验极差;
利用分层模式,一个层为游戏背景层,专门显示背景图,再加一个HUD层,HUD层上面放游戏菜单、玩家的血条,虚拟摇杆等
在实现分层模式时,2个层之间要传递玩家的坐标,我在这里卡了很久,起初想在一个层中得到上层节点,然后再根据tag向下找,结果失败告终,几个类互相包含时,头文件错综复杂,用了#pragma once指令后,又引发了CC_SYNTHESIZE等宏定义的错误,总之,在2个CCLayer之间交换数据花了很多时间,VS中一堆莫名其妙的错误,以下为当时的代码。
//获取GameLayer层的实例_gamelayer,这种方法行不通,还是在2个类的父类中进行数据交换吧 //GameScene * _gamescene = (GameScene *)(CCDirector::sharedDirector()->getRunningScene()); //GameLayer * _gamelayer = _gamescene->getGameLayer(); //_player->setPosition(_gamelayer->getHeroBorn());
2个节点之间的数据交互,建议将2个CCLayer封装为一个Scene或CCLayer类的数据成员,然后由父节点调用,这样可以很方便地完成数据交换。如下代码轻易地完成了hudlayer和gamelayer2个CCLayer之间玩家坐标的数据交换:
_gamelayer = GameLayer::create(); //创建GameLayer this->addChild(_gamelayer, GameLayerTag, GameLayerTag); _hudlayer = HudLayer::create(); //创建HudLayer this->addChild(_hudlayer, GameLayerTag, HudLayerTag); //设置玩家出生位置 _hudlayer->getPlayer()->setPosition(_gamelayer->getHeroBorn());
本次的例子进行了分模块设计,如下图所示:
GameLayer是游戏背景层,实现了游戏TMX地图的加载及地图滚动
HudLayer是玩家信息层,实现了虚拟摇杆实时控制玩家在地图中移动
GameScene是游戏场景层,把GameLayer和HudLayer作为实现变量封装进去
因为我们实现了自己的场景类,所以AppDelegate.cpp中默认的Helloword场景需要改成我们的。
// create a scene. it's an autorelease object //CCScene *pScene = HelloWorld::scene(); CCScene *pScene = GameScene::create(); // run scene pDirector->runWithScene(pScene);
GameScene类的声明代码:
#include "HudLayer.h" #include "GameLayer.h" #include "cocos2d.h" using namespace cocos2d; class GameScene : public cocos2d::CCScene { public: virtual bool init(); GameScene(); ~GameScene(); void update(float dt); CREATE_FUNC(GameScene); CC_SYNTHESIZE(GameLayer *, _gamelayer, GameLayer); CC_SYNTHESIZE(HudLayer *, _hudlayer, HudLayer); };
GameScene类的实现代码:
#include "GameScene.h" GameScene::GameScene() { _gamelayer = NULL; _hudlayer = NULL; } GameScene::~GameScene() { } void GameScene::update(float dt) { _gamelayer->setViewpointCenter(_hudlayer->getPlayer()->getPosition()); } bool GameScene::init() { bool bRet = false; do { //先初始化父类,如果失败则break循环 CC_BREAK_IF(! CCScene::init()); _gamelayer = GameLayer::create(); //创建GameLayer this->addChild(_gamelayer, GameLayerTag, GameLayerTag); _hudlayer = HudLayer::create(); //创建HudLayer this->addChild(_hudlayer, GameLayerTag, HudLayerTag); //设置玩家出生位置 _hudlayer->getPlayer()->setPosition(_gamelayer->getHeroBorn()); this->scheduleUpdate(); bRet = true; }while(0); return bRet; }
GameLayer的声明:
#include "cocos2d.h" using namespace cocos2d; class GameLayer : public cocos2d::CCLayer { public: GameLayer(); //构造函数声明 ~GameLayer(); //析构函数声明 CREATE_FUNC(GameLayer); //类似于cocos2d中的node方法,返回一个autorelease对象 virtual bool init(); //本类对象初始化 bool InitTmxMap(); //初始化TMX地图 //设置视图位置 void setViewpointCenter(cocos2d::CCPoint position); //cocos2d::CCTMXTiledMap *_TiledMap; CC_SYNTHESIZE(cocos2d::CCPoint , _heroborn, HeroBorn); CC_SYNTHESIZE(cocos2d::CCTMXTiledMap *, _tiledmap, TiledMap); CC_SYNTHESIZE(cocos2d::CCTMXLayer*, _background, Background); };
GameLayer的实现:
#include "GameLayer.h" GameLayer::GameLayer() { _tiledmap = NULL; _background = NULL; } GameLayer::~GameLayer() { } bool GameLayer::init() { bool bRet = false; do { //先初始化父类,如果失败则break循环 CC_BREAK_IF(! CCLayer::init()); InitTmxMap(); bRet = true; }while(0); return bRet; } bool GameLayer::InitTmxMap() { bool bRet = false; do { //创建瓦片地图 this->setTiledMap(CCTMXTiledMap::create("pd_tilemap1.tmx")); _tiledmap->setAnchorPoint(ccp(0.0f, 0.0f)); //设置瓦片地图的锚点为屏幕正中间 _tiledmap->setPosition(ccp(0, 0)); //设置瓦片地图的位置为屏幕左下角 this->setBackground(_tiledmap->layerNamed("Layer1")); this->addChild(_tiledmap, 2); //加入场景层 //获取Objects对象层 CCLOG("_tileMap %d", _tiledmap->retainCount()); CCTMXObjectGroup *objects = _tiledmap->objectGroupNamed("Objects"); CCLOG("_tileMap %d", _tiledmap->retainCount()); CC_BREAK_IF(!objects); //如果失败,退出 //获取SpawnPoint的字典(包含坐标,高和宽等信息) CCDictionary *spawnPoint = objects->objectNamed("SpawnPoint"); CC_BREAK_IF(!spawnPoint); //如果失败,退出 //分别取x和y坐标的值 int x = spawnPoint->valueForKey("x")->intValue(); int y = spawnPoint->valueForKey("y")->intValue(); this->setHeroBorn(ccp(x, y)); //设置玩家出生坐标 bRet = true; }while(0); return bRet; } void GameLayer::setViewpointCenter(cocos2d::CCPoint position) { CCSize winSize = CCDirector::sharedDirector()->getWinSize(); int x = MAX(position.x, winSize.width / 2); int y = MAX(position.y, winSize.height / 2); x = MIN(x, (_tiledmap->getMapSize().width * _tiledmap->getTileSize().width) - winSize.width / 2); y = MIN(y, (_tiledmap->getMapSize().height * _tiledmap->getTileSize().height) - winSize.height / 2); CCPoint actualPosition = ccp(x, y); CCPoint centerOfView = ccp(winSize.width / 2, winSize.height / 2); CCPoint viewPoint = ccpSub(centerOfView, actualPosition); this->setPosition(viewPoint); }
HudLayer的声明:
#include "cocos2d.h" typedef enum { HudLayerTag = 2, GameLayerTag =1, }GameLayersTags; using namespace cocos2d; class HudLayer : public cocos2d::CCLayer { public: HudLayer(); //构造函数声明 ~HudLayer(); //析构函数声明 CREATE_FUNC(HudLayer); //类似于cocos2d中的node方法,返回一个autorelease对象 virtual bool init(); //本类对象初始化 void InitJoystick(); //初始化摇杆 void InitHero(); //初始化主角精灵 void menuCloseCallback(CCObject* pSender); //触摸事件处理 virtual void ccTouchesBegan(CCSet *pTouches, CCEvent *pEvent); virtual void ccTouchesMoved(CCSet *pTouches, CCEvent *pEvent); virtual void ccTouchesEnded(CCSet *pTouches, CCEvent *pEvent); float heronsformula(float x1, float y1, float x2, float y2, float x3, float y3); bool triangleContainPoint(float x1, float y1, float x2, float y2, float x3, float y3, float px, float py); void setPlayerPosition(cocos2d::CCPoint position); //设置玩家位置 CC_SYNTHESIZE(cocos2d::CCSprite*, _player, Player); //CC_SYNTHESIZE(); private: void Moving(float dt); cocos2d::CCSprite *joystick; cocos2d::CCSprite *joystick1; cocos2d::CCSize size; cocos2d::CCPoint O; //中心点O float R; //大圆半径 //精灵移动的速度分量值 float speedX; float speedY; //是否移动的标志 bool IsMoving; };
HudLayer的实现:
#include "Hudlayer.h"
HudLayer::HudLayer()
{
//初始化需要的变量
IsMoving = false;
speedX = speedY = 0;
}
HudLayer::~HudLayer()
{
}
bool HudLayer::init()
{
bool bRet = false;
do
{
//先初始化父类,如果失败则break循环
CC_BREAK_IF(! CCLayer::init());
//创建一个关闭按钮菜单置于屏幕右下角
CCMenuItemLabel *txtmenItem = CCMenuItemLabel::create(CCLabelTTF::create("Exit", "Arial", 24), this, menu_selector(HudLayer::menuCloseCallback));
CC_BREAK_IF(! txtmenItem);
txtmenItem->setPosition(ccp(CCDirector::sharedDirector()->getWinSize().width - txtmenItem->getContentSize().width/2, 20));
CCMenu* pMenu = CCMenu::create(txtmenItem, NULL);
pMenu->setPosition(CCPointZero);
CC_BREAK_IF(! pMenu);
// Add the menu to HelloWorld layer as a child layer.
this->addChild(pMenu, 10);
this->InitHero(); //初始化精灵
this->setTouchEnabled(TRUE); //打开触摸事件处理
this->InitJoystick(); //初始化摇杆
//每帧要执行的函数
this->schedule(schedule_selector(HudLayer::Moving));
bRet = true;
}while(0);
return bRet;
}
void HudLayer::menuCloseCallback(CCObject* pSender)
{
CCDirector::sharedDirector()->end();
}
void HudLayer::InitJoystick()
{
//创建摇杆下面部分
joystick1 = CCSprite::create("joystick1.png");
//设置透明度,锚点,位置
joystick1->setOpacity(150);
joystick1->setAnchorPoint(ccp(0, 0));
joystick1->setPosition(ccp(0, 0));
joystick1->setColor(ccYELLOW);
//大圆半径
R=joystick1->getContentSize().width/2;
//中心点
O = ccp(R, R);
//添加进布景
this->addChild(joystick1, 3);
//创建摇杆上面圆圈部分
joystick = CCSprite::create("joystick2.png");
//设置位置为摇杆中心点并添加进布景
joystick->setPosition(ccp(O.x, O.y));
joystick->setColor(ccRED);
joystick->setScale(0.8f);
this->addChild(joystick, 4);
}
void HudLayer::InitHero()
{
CCSize size = CCDirector::sharedDirector()->getWinSize();
this->setPlayer(CCSprite::create("hartnett.jpg"));
_player->setPosition(ccp(size.width/2, size.height/2));
//获取GameLayer层的实例_gamelayer,这种方法行不通,还是在2个类的父类中进行数据交换吧 //GameScene * _gamescene = (GameScene *)(CCDirector::sharedDirector()->getRunningScene()); //GameLayer * _gamelayer = _gamescene->getGameLayer(); //_player->setPosition(_gamelayer->getHeroBorn());
_player->setScale(0.3f);
this->addChild(_player);
}
void HudLayer::Moving(float dt)
{
if (IsMoving && (speedX != 0 || speedY != 0))
{
//精灵移动
CCPoint position=ccp(_player->getPosition().x + speedX, _player->getPosition().y + speedY);
CCSize size=CCDirector::sharedDirector()->getWinSize();
CCRect rect=CCRectMake(0, 0, size.width, size.height);
//_player->setPosition(position);
//判断触摸点是否在屏幕内
if(rect.containsPoint(position))
{
_player->setPosition(position);
}
}
}
float HudLayer::heronsformula(float x1,float y1,float x2,float y2,float x3,float y3)
{
//求边长a
float a = sqrt(pow(x1 - x2, 2) + pow(y1 - y2, 2));
//求边长b
float b = sqrt(pow(x2 - x3, 2) + pow(y2 - y3, 2));
//求边长c
float c = sqrt(pow(x3 - x1, 2) + pow(y3 - y1, 2));
//求半周长s
float s = (a + b + c) / 2;
//根据海伦公式返回三角形面积
return sqrt(s * (s - a) * (s - b) * (s - c));
}
//判断三个新三角形面积和是否等于原先三角形面积的函数的实现
bool HudLayer::triangleContainPoint(float x1,float y1,float x2,float y2,float x3,float y3,float px,float py)
{
//求S1的面积
float s1=heronsformula(x1,y1,x2,y2,px,py);
//求S2的面积
float s2=heronsformula(x2,y2,x3,y3,px,py);
//求S3的面积
float s3=heronsformula(x3,y3,x1,y1,px,py);
//求S的面积
float s=heronsformula(x1,y1,x2,y2,x3,y3);
//返回S是否等于S1,S2,S3的和
return abs(s-(s1+s2+s3))<0.001f;
}
void HudLayer::ccTouchesBegan(CCSet *pTouches, CCEvent *pEvent)
{
CCTouch *touch = (CCTouch*)pTouches->anyObject();
CCPoint location = touch->getLocation();
CCRect rect = joystick->boundingBox();
if (rect.containsPoint(location))
{
IsMoving = true;
}
}
void HudLayer::ccTouchesMoved(CCSet *pTouches, CCEvent *pEvent)
{
CCTouch *touch = (CCTouch*)pTouches->anyObject();
CCPoint location = touch->getLocation();
//判断触摸滑动点是否在摇杆范围内
bool inRange = pow(O.x - location.x, 2) + pow(O.y - location.y, 2) < pow(R, 2);
if (IsMoving && inRange)
{
CCPoint position=_player->getPosition();
joystick->setPosition(location);
float r = R * 2 / 6;
float d = R * 2 / 3;
//上,区域2或5
if(triangleContainPoint(O.x, O.y, O.x - r, O.y + r, O.x +r, O.y + r, location.x, location.y)
|| CCRectMake(O.x - r, O.y + r, d, d).containsPoint(location))
{
speedX = 0;
speedY = 1;
}
//下,区域6或11
else if (triangleContainPoint(O.x, O.y, O.x - r, O.y - r, O.x + r, O.y - r, location.x, location.y)
|| CCRectMake(O.x - r, O.y - r - d, d, d).containsPoint(location))
{
speedX = 0;
speedY = -1;
}
//左,区域4或7
else if (triangleContainPoint(O.x, O.y, O.x - r, O.y + r, O.x - r, O.y - r, location.x, location.y)
|| CCRectMake(O.x - r - d, O.y - r, d, d).containsPoint(location))
{
speedX = -1;
speedY = 0;
}
//右,区域9或8
else if (triangleContainPoint(O.x, O.y, O.x + r, O.y + r, O.x + r, O.y - r, location.x, location.y)
|| CCRectMake(O.x + r, O.y - r, d, d).containsPoint(location))
{
speedX = 1;
speedY = 0;
}
//右上,区域3
else if (location.x - (O.x + r) > 0 && location.y - (O.y + r) > 0)
{
speedX = 0.7f;
speedY = 0.7f;
}
//左上,区域1
else if (location.x - (O.x - r) < 0 && location.y - (O.y + r) > 0)
{
speedX =- 0.7f;
speedY = 0.7f;
}
//左下,区域10
else if (location.x - (O.x - r) < 0 && location.y - (O.y - r) < 0)
{
speedX = -0.7f;
speedY = -0.7f;
}
//右下,区域12
else if (location.x - (O.x + r) > 0 && location.y - (O.y - r) < 0)
{
speedX = 0.7f;
speedY = -0.7f;
}
}
}
void HudLayer::ccTouchesEnded(CCSet *pTouches, CCEvent *pEvent)
{
IsMoving = false;
joystick->setPosition(O);
speedX = speedY = 0;
}
相关文章推荐
- Cocos2d-x3.2实现虚拟摇杆多点触摸
- cocos2D 虚拟摇杆Joystick功能实现
- Cocos2d-x3.2实现虚拟摇杆多点触摸
- 基于Cocos2d-x3.2的虚拟摇杆实现及操控角色移动
- Cocos2d-js3.3 虚拟摇杆的实现
- cocos2d-x 之虚拟摇杆实现
- Cocos2d-js3.3虚拟摇杆的实现
- Cocos2d-x3.2实现虚拟摇杆多点触摸-----沈大海
- 「cocos2d-x」虚拟摇杆实现(1)
- Cocos2d-x3.2实现虚拟摇杆多点触摸
- ios joystick 虚拟摇杆实现 ( 非 Cocos2d )
- Unity2D - 4. 实现android虚拟摇杆控制人物移动
- 关于cocos2dx虚拟摇杆的lua实现
- cocos2d-x 街机摇杆 实现
- UGUI实现Unity虚拟摇杆
- Unity实现虚拟摇杆
- Unity手游之路<五>虚拟摇杆之Unity内置插件实现
- Unity3D 虚拟摇杆 NGUI实现方法
- Unity手游之路<五>虚拟摇杆之Unity内置插件实现