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

Cocos2d-x 之 八方向小摇杆

2015-08-20 20:50 656 查看
***************************************转载请注明出处:http://blog.csdn.net/lttree*******************************************

前文:

之前做   >>>CatchingJoy <<<   的时候,用到了摇杆,

网上有四方向的,

我用到的是八个方向,看了下它的思路,改动了一些,就成了8方向拉~

嘿嘿~  let it go~

****************************************************************转载请注明出处:http://blog.csdn.net/lttree********************************************************************

正文:

1.先准备好图片:

需要移动的小方块:


    

摇杆背景 和 摇杆中心:


                 


****************************************************************转载请注明出处:http://blog.csdn.net/lttree********************************************************************

2.原理

首先,放摇杆的需要是一个层,放着摇杆背景 和 中心(显然中心要在背景上层),

然后我们再把这个层放在场景中。

这个层需要接受触摸(单点),并屏蔽下层触摸(一般来说)。

然后,摇杆 如何瓜分?



(Windows画图绘制,很渣,凑合看吧= =。)

每个方向  都占45°    45*8 = 360

其次,TouchBegan,接受到触摸点,判断是否在 摇杆中心 那个范围,用contiansPoint实现。

然后,TouchMoved,看往哪个方向移动,移动的距离,如果拉扯的距离大于半径,摇杆中心是不能出摇杆背景的,如果拉扯距离小于等于半径,中心该在哪就在哪。

最后,TouchEnd,要让摇杆中心从当前位置移动回初始位置。

所以,比较麻烦的地方仅仅在于 TouchMoved部分,要判断距离,判断摇杆中心该放在哪里等...

****************************************************************转载请注明出处:http://blog.csdn.net/lttree********************************************************************

3.实现

> 准备工作

(1) 先做个 enum(枚举),把8个方向用英语比1,2,3...8好很多:

//用于标识摇杆方向
typedef enum{
rocker_stay = 0,
rocker_right,
rocker_up,
rocker_left,
rocker_down,
rocker_leftUp,
rocker_rightUp,
rocker_leftDown,
rocker_rightDown,
}rockerDirecton;

可以看到,除了8个方向,还有一个stay(原地不动)

(2) 这里,设置摇杆位置,可以自定义也可以固定,我用的自定义(好处不多说),

所以原来那种 create,init就不用了,用其他的替代:

// JoyRocker.h
static JoyRocker* create(Vec2 pos);
bool initRocker(Vec2 pos);

// JoyRocker.cpp
JoyRocker* JoyRocker::create(Vec2 pos)
{
JoyRocker* layer = JoyRocker::create();
if ( layer )
{
layer->initRocker(pos);
return layer;
}
CC_SAFE_DELETE(layer);
return NULL;
}

bool JoyRocker::initRocker(Vec2 pos)
{
// 摇杆背景 图片
Sprite* spRockerBG = Sprite::create("spi_joystickBG.png");
spRockerBG->setPosition(pos);
spRockerBG->setTag(1);
this->addChild(spRockerBG,0);

// 摇杆中心 图片
Sprite* spRockerCenter = Sprite::create("spi_joystickCenter.png");
spRockerCenter->setPosition(pos);
spRockerCenter->setTag(2);
this->addChild(spRockerCenter,1);

// 设置 摇杆中心 位置
rockerCenterPos = pos;
// 获取 摇杆背景 半径
rockerBGR = spRockerBG->getContentSize().width*0.5;
// 设置 摇杆 初始方向
rocketDirection = 0;

// 事件监听部分
listener = EventListenerTouchOneByOne::create();
// 吞掉这个触摸
listener->setSwallowTouches(true);

listener->onTouchBegan = CC_CALLBACK_2(JoyRocker::TouchBegan,this);
listener->onTouchMoved = CC_CALLBACK_2(JoyRocker::TouchMoved,this);
listener->onTouchEnded = CC_CALLBACK_2(JoyRocker::TouchEnded,this);

// 注册事件监听机制
eventDispatcher = Director::getInstance()->getEventDispatcher();

return true;
}


> 然后就是 Touch 三兄弟,这里我用的传统的方式,没有用lamda表达式= =。

TouchBegan:

bool JoyRocker::TouchBegan(Touch* touch, Event* event)
{
Sprite* sp = (Sprite*)this->getChildByTag(2);

//得到触屏点坐标
Vec2 point = touch->getLocation();

//判断是否点击到sp这个精灵:boundingBox()精灵大小之内的所有坐标
if(sp->boundingBox().containsPoint(point))
{
// 可以移动了
isCanMove = true;
}

return true;
}


TouchMoved:

void JoyRocker::TouchMoved(Touch* touch, Event* event)
{
// 如果不能移动,直接返回
if(!isCanMove)
{
return;
}

Sprite* sp = (Sprite*)getChildByTag(2);
Vec2 point = touch->getLocation();

//得到摇杆与触屏点所形成的角度
float angle = getRad(rockerCenterPos,point);

//判断两个圆的圆心距是否大于摇杆背景的半径
if (sqrt(pow((rockerCenterPos.x - point.x),2) + pow((rockerCenterPos.y - point.y),2)) >= rockerBGR)
{
//保证内部小圆运动的长度限制
sp->setPosition(ccpAdd(getAnglePosition(rockerBGR,angle),Vec2(rockerCenterPos.x,rockerCenterPos.y)));
}
else
{
//当没有超过,让摇杆跟随用户触屏点移动即可
sp->setPosition(point);
}

//判断方向

// 右方
if( angle>=-PI/8 && angle<PI/8 )	{
rocketDirection = rocker_right;
isLeft = false;
}
// 右上方
else if( angle>=PI/8 && angle<3*PI/8 )	{
rocketDirection = rocker_rightUp;
isLeft = false;
}
// 上方
else if( angle>=3*PI/8 && angle<5*PI/8 )	{
rocketDirection = rocker_up;
}
// 左上方
else if( angle>=5*PI/8 && angle<7*PI/8 )	{
rocketDirection = rocker_leftUp;
isLeft = true;
}
// 左方
else if( (angle>=7*PI/8&&angle<=PI) || (angle>=-PI&&angle<-7*PI/8) )	{
rocketDirection = rocker_left;
isLeft = true;
}
// 左下方
else if( angle>=-7*PI/8 && angle<-5*PI/8 )	{
rocketDirection = rocker_leftDown;
isLeft = true;
}
// 下方
else if( angle>=-5*PI/8 && angle<-3*PI/8 )	{
rocketDirection = rocker_down;
}
// 右下方
else if( angle>=-3*PI/8 && angle<-PI/8 )	{
rocketDirection = rocker_rightDown;
isLeft = false;
}

}


要解释的有三点:

① 两个工具函数

· 用户 触摸 一个点后,这个点与 摇杆中心 相连,这条线段与水平方向 所构成的角度,

就是——函数 getRad(返回的是 弧度值)

float JoyRocker::getRad(Vec2 pos1,Vec2 pos2)
{
float px1 = pos1.x;
float py1 = pos1.y;
float px2 = pos2.x;
float py2 = pos2.y;

//得到两点x的距离
float x = px2 - px1;
//得到两点y的距离
float y = py1 - py2;
//算出斜边长度
float xie = sqrt(pow(x,2) + pow(y,2));
//得到这个角度的余弦值(通过三角函数中的点里:角度余弦值=斜边/斜边)
float cosAngle = x / xie;
//通过反余弦定理获取到期角度的弧度
float rad = acos(cosAngle);
//注意:当触屏的位置Y坐标<摇杆的Y坐标,我们要去反值-0~-180
if (py2 < py1)
{
rad = -rad;
}
return rad;
}

用简单的高数知识推一推,在纸上画一画,就出来了,这里不解释了就(画图太麻烦啊)...o(╯□╰)o...

如果实在不懂,回复我,我再更新上去

· 第二个工具函数

根据 与水平方向形成的角度的弧度值,返回对应点的位置,就是第一个工具函数的 反 

// 根据角度,返回点坐标
Vec2 JoyRocker::getAnglePosition(float r,float angle)
{
return Vec2(r*cos(angle),r*sin(angle));
}

三角形的那一串,应该不用解释了= =。。。

② 关于 判断圆心距那块的 函数 cppAdd

其实,并不神秘的东西,跳转到定义就发现:

ccpAdd(const Vec2& v1, const Vec2& v2)
{
return v1 + v2;
}

就是这么简单,当然那一系列还有:

加减乘除,取反,取中点等等等等,

具体的看 头文件--> CCDeprecated.h

③ 关于 isLeft

每次判断完方向,都会对 isLeft赋值,

这个的作用是  标记  正面朝向,

假设,我们做的图是人物走动的,肯定只是做一个方向的(当然,也可以做两个方向,但。。)

然后,什么时候用向右走,什么时候用向左走,(如果向没做图的那个方向,我们可以用 setFlipedX(true),让Sprite 180° 大翻转)

这时候,isLeft 就用到了~

> TouchEnd

void JoyRocker::TouchEnded(Touch* touch, Event* event)
{
if(!isCanMove)
{
return;
}

// 获取 摇杆背景 与 摇杆中心
Sprite* rocker = (Sprite*)getChildByTag(2);
Sprite* rockerBG = (Sprite*)getChildByTag(1);

// 让 摇杆中心 停止之前所有动作,然后开始 执行归位
rocker->stopAllActions();
rocker->runAction(MoveTo::create(0.08, rockerBG->getPosition()));

// 设置 方向为 stay,并且 在下次触摸开始前 不可移动
rocketDirection=rocker_stay;
isCanMove = false;
}

摇杆已经OK啦

>现在 把小方块加进来,动起来

//  场景的 init 函数
// 获取 屏幕大小
visibleSize = Director::getInstance()->getVisibleSize();

// 添加绿色小方块
bg = Sprite::create("green.png");
bg->setPosition(visibleSize.width/2,visibleSize.height/2);
this->addChild(bg);

// 添加 摇杆
jr = JoyRocker::create(Vec2(visibleSize.width-100,100));
this->addChild(jr);

// 时时更新函数
this->scheduleUpdate();

这里,我们调用 scheduleUpdate,就是让程序 每一帧 都自动调用 void update(float ft) 函数,

然后,我们重写下 update 函数:

void DemoScene::update(float ft)
{
//判断是否按下摇杆及其类型
switch( jr->getDirection() )
{
case 1:
bg->setPosition(Vec2(bg->getPosition().x+2,bg->getPosition().y));		//向右走
break;
case 2:
bg->setPosition(Vec2(bg->getPosition().x, bg->getPosition().y+2));		//向上走
break;
case 3:
bg->setPosition(Vec2(bg->getPosition().x-2,bg->getPosition().y));		//向左走
break;
case 4:
bg->setPosition(Vec2(bg->getPosition().x,bg->getPosition().y-2));		//向下走
break;
case 5:
bg->setPosition(Vec2(bg->getPosition().x-1,bg->getPosition().y+1));             //向左上走
break;
case 6:
bg->setPosition(Vec2(bg->getPosition().x+1,bg->getPosition().y+1));             //向右上走
break;
case 7:
bg->setPosition(Vec2(bg->getPosition().x-1,bg->getPosition().y-1));             //向左下走
break;
case 8:
bg->setPosition(Vec2(bg->getPosition().x+1,bg->getPosition().y-1));             //向右下走
break;
default:
break;
}
}


其实就是每帧都获取 方向盘的方向,

然后,根据方向,执行对应动作,

这里,我设置的,如果单纯往某个方向走,移动速度为 每帧移动2,

如果 向复合方向走(左上、左下、右上、右下),每个相应方向只是 每帧移动1。

Ok,方向盘就到这里啦,

可以再优化一下,比如 不让 小绿块 飞出界面(就在移动前加个判断就行)...

上面代码不全,上传一个JoyRocker类 和 小绿块的场景类,

里面可能有些小改动,但大体都是一样的,

百度云: >点这里 <

***************************************转载请注明出处:http://blog.csdn.net/lttree*******************************************
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息