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

「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中一堆莫名其妙的错误,以下为当时的代码。

//获取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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: