Cocos2d-x 之 八方向小摇杆

前端之家收集整理的这篇文章主要介绍了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好很多:

  1. //用于标识摇杆方向
  2. typedef enum{
  3. 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就不用了,用其他的替代:

  1. // JoyRocker.h
  2. static JoyRocker* create(Vec2 pos);
  3. bool initRocker(Vec2 pos);
  4.  
  5.  
  6. // JoyRocker.cpp
  7. JoyRocker* JoyRocker::create(Vec2 pos)
  8. {
  9. JoyRocker* layer = JoyRocker::create();
  10. if ( layer )
  11. {
  12. layer->initRocker(pos);
  13. return layer;
  14. }
  15. CC_SAFE_DELETE(layer);
  16. return NULL;
  17. }
  18.  
  19. bool JoyRocker::initRocker(Vec2 pos)
  20. {
  21. // 摇杆背景 图片
  22. Sprite* spRockerBG = Sprite::create("spi_joystickBG.png");
  23. spRockerBG->setPosition(pos);
  24. spRockerBG->setTag(1);
  25. this->addChild(spRockerBG,0);
  26. // 摇杆中心 图片
  27. Sprite* spRockerCenter = Sprite::create("spi_joystickCenter.png");
  28. spRockerCenter->setPosition(pos);
  29. spRockerCenter->setTag(2);
  30. this->addChild(spRockerCenter,1);
  31. // 设置 摇杆中心 位置
  32. rockerCenterPos = pos;
  33. // 获取 摇杆背景 半径
  34. rockerBGR = spRockerBG->getContentSize().width*0.5;
  35. // 设置 摇杆 初始方向
  36. rocketDirection = 0;
  37.  
  38.  
  39. // 事件监听部分
  40. listener = EventListenerTouchOneByOne::create();
  41. // 吞掉这个触摸
  42. listener->setSwallowTouches(true);
  43.  
  44. listener->onTouchBegan = CC_CALLBACK_2(JoyRocker::TouchBegan,this);
  45. listener->onTouchMoved = CC_CALLBACK_2(JoyRocker::TouchMoved,this);
  46. listener->onTouchEnded = CC_CALLBACK_2(JoyRocker::TouchEnded,this);
  47.  
  48. // 注册事件监听机制
  49. eventDispatcher = Director::getInstance()->getEventDispatcher();
  50.  
  51. return true;
  52. }


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

TouchBegan:

  1. bool JoyRocker::TouchBegan(Touch* touch,Event* event)
  2. {
  3. Sprite* sp = (Sprite*)this->getChildByTag(2);
  4. //得到触屏点坐标
  5. Vec2 point = touch->getLocation();
  6. //判断是否点击到sp这个精灵:boundingBox()精灵大小之内的所有坐标
  7. if(sp->boundingBox().containsPoint(point))
  8. {
  9. // 可以移动了
  10. isCanMove = true;
  11. }
  12.  
  13. return true;
  14. }


TouchMoved:

  1. void JoyRocker::TouchMoved(Touch* touch,Event* event)
  2. {
  3. // 如果不能移动,直接返回
  4. if(!isCanMove)
  5. {
  6. return;
  7. }
  8. Sprite* sp = (Sprite*)getChildByTag(2);
  9. Vec2 point = touch->getLocation();
  10. //得到摇杆与触屏点所形成的角度
  11. float angle = getRad(rockerCenterPos,point);
  12.  
  13. //判断两个圆的圆心距是否大于摇杆背景的半径
  14. if (sqrt(pow((rockerCenterPos.x - point.x),2) + pow((rockerCenterPos.y - point.y),2)) >= rockerBGR)
  15. {
  16. //保证内部小圆运动的长度限制
  17. sp->setPosition(ccpAdd(getAnglePosition(rockerBGR,angle),Vec2(rockerCenterPos.x,rockerCenterPos.y)));
  18. }
  19. else
  20. {
  21. //当没有超过,让摇杆跟随用户触屏点移动即可
  22. sp->setPosition(point);
  23. }
  24. //判断方向
  25.  
  26. // 右方
  27. if( angle>=-PI/8 && angle<PI/8 ) {
  28. rocketDirection = rocker_right;
  29. isLeft = false;
  30. }
  31. // 右上方
  32. else if( angle>=PI/8 && angle<3*PI/8 ) {
  33. rocketDirection = rocker_rightUp;
  34. isLeft = false;
  35. }
  36. // 上方
  37. else if( angle>=3*PI/8 && angle<5*PI/8 ) {
  38. rocketDirection = rocker_up;
  39. }
  40. // 左上方
  41. else if( angle>=5*PI/8 && angle<7*PI/8 ) {
  42. rocketDirection = rocker_leftUp;
  43. isLeft = true;
  44. }
  45. // 左方
  46. else if( (angle>=7*PI/8&&angle<=PI) || (angle>=-PI&&angle<-7*PI/8) ) {
  47. rocketDirection = rocker_left;
  48. isLeft = true;
  49. }
  50. // 左下方
  51. else if( angle>=-7*PI/8 && angle<-5*PI/8 ) {
  52. rocketDirection = rocker_leftDown;
  53. isLeft = true;
  54. }
  55. // 下方
  56. else if( angle>=-5*PI/8 && angle<-3*PI/8 ) {
  57. rocketDirection = rocker_down;
  58. }
  59. // 右下方
  60. else if( angle>=-3*PI/8 && angle<-PI/8 ) {
  61. rocketDirection = rocker_rightDown;
  62. isLeft = false;
  63. }
  64. }


要解释的有三点:

① 两个工具函数

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

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

  1. float JoyRocker::getRad(Vec2 pos1,Vec2 pos2)
  2. {
  3. float px1 = pos1.x;
  4. float py1 = pos1.y;
  5. float px2 = pos2.x;
  6. float py2 = pos2.y;
  7. //得到两点x的距离
  8. float x = px2 - px1;
  9. //得到两点y的距离
  10. float y = py1 - py2;
  11. //算出斜边长度
  12. float xie = sqrt(pow(x,2) + pow(y,2));
  13. //得到这个角度的余弦值(通过三角函数中的点里:角度余弦值=斜边/斜边)
  14. float cosAngle = x / xie;
  15. //通过反余弦定理获取到期角度的弧度
  16. float rad = acos(cosAngle);
  17. //注意:当触屏的位置Y坐标<摇杆的Y坐标,我们要去反值-0~-180
  18. if (py2 < py1)
  19. {
  20. rad = -rad;
  21. }
  22. return rad;
  23. }

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

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

· 第二个工具函数

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

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

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


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

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

  1. ccpAdd(const Vec2& v1,const Vec2& v2)
  2. {
  3. return v1 + v2;
  4. }

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

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

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

③ 关于 isLeft

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

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

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

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

这时候,isLeft 就用到了~



> TouchEnd

  1. void JoyRocker::TouchEnded(Touch* touch,Event* event)
  2. {
  3. if(!isCanMove)
  4. {
  5. return;
  6. }
  7. // 获取 摇杆背景 与 摇杆中心
  8. Sprite* rocker = (Sprite*)getChildByTag(2);
  9. Sprite* rockerBG = (Sprite*)getChildByTag(1);
  10. // 让 摇杆中心 停止之前所有动作,然后开始 执行归位
  11. rocker->stopAllActions();
  12. rocker->runAction(MoveTo::create(0.08,rockerBG->getPosition()));
  13. // 设置 方向为 stay,并且 在下次触摸开始前 不可移动
  14. rocketDirection=rocker_stay;
  15. isCanMove = false;
  16. }

摇杆已经OK啦


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

  1. // 场景的 init 函数
  2. // 获取 屏幕大小
  3. visibleSize = Director::getInstance()->getVisibleSize();
  4.  
  5. // 添加绿色小方块
  6. bg = Sprite::create("green.png");
  7. bg->setPosition(visibleSize.width/2,visibleSize.height/2);
  8. this->addChild(bg);
  9.  
  10. // 添加 摇杆
  11. jr = JoyRocker::create(Vec2(visibleSize.width-100,100));
  12. this->addChild(jr);
  13.  
  14. // 时时更新函数
  15. this->scheduleUpdate();

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

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

  1. void DemoScene::update(float ft)
  2. {
  3. //判断是否按下摇杆及其类型
  4. switch( jr->getDirection() )
  5. {
  6. case 1:
  7. bg->setPosition(Vec2(bg->getPosition().x+2,bg->getPosition().y)); //向右走
  8. break;
  9. case 2:
  10. bg->setPosition(Vec2(bg->getPosition().x,bg->getPosition().y+2)); //向上走
  11. break;
  12. case 3:
  13. bg->setPosition(Vec2(bg->getPosition().x-2,bg->getPosition().y)); //向左走
  14. break;
  15. case 4:
  16. bg->setPosition(Vec2(bg->getPosition().x,bg->getPosition().y-2)); //向下走
  17. break;
  18. case 5:
  19. bg->setPosition(Vec2(bg->getPosition().x-1,bg->getPosition().y+1)); //向左上走
  20. break;
  21. case 6:
  22. bg->setPosition(Vec2(bg->getPosition().x+1,bg->getPosition().y+1)); //向右上走
  23. break;
  24. case 7:
  25. bg->setPosition(Vec2(bg->getPosition().x-1,bg->getPosition().y-1)); //向左下走
  26. break;
  27. case 8:
  28. bg->setPosition(Vec2(bg->getPosition().x+1,bg->getPosition().y-1)); //向右下走
  29. break;
  30. default:
  31. break;
  32. }
  33. }


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

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

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

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


Ok,方向盘就到这里啦,

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



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

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

百度云: >点这里 <





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

猜你在找的Cocos2d-x相关文章