Cocos2d-x 之 八方向小摇杆

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



前文:

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

网上有四方向的,

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

嘿嘿~ let it go~


****************************************************************转载请注明出处:********************************************************************


正文:

1.先准备好图片:

需要移动的小方块:


摇杆背景 和 摇杆中心:




********************************************************************


2.原理

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

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

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


然后,摇杆 如何瓜分?


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

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


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

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

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


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


****************************************************************转载请注明出处:********************************************************************


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类 和 小绿块的场景类,

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

百度云: >点这里 <





*******************************************

相关文章

    本文实践自 RayWenderlich、Ali Hafizji 的文章《...
Cocos-code-ide使用入门学习地点:杭州滨江邮箱:appdevzw@1...
第一次開始用手游引擎挺激动!!!进入正题。下载资源1:从C...
    Cocos2d-x是一款强大的基于OpenGLES的跨平台游戏开发...
1.  来源 QuickV3sample项目中的2048样例游戏,以及最近《...
   Cocos2d-x3.x已经支持使用CMake来进行构建了,这里尝试...