cocos2d-x 关于旋转和移动的一点小技巧

shun_fzll
Wed, 25 Jun 2014 09:48:24 +0000

你犯困吗，恩，先给你讲个笑话提提神～

一對情侶去從林遊玩，被食人族捉住。食人族首領心情很好，說你們如果想活命，就吃掉對方的大便。在他們回來的路上，女人終於忍不住停下，坐到石頭上哭起來。男人摟住她的肩膀。女人別過臉去，幽幽的說：你不愛我，要不然剛才你不會拉這麼多。

（能呵呵吗？）

================================================================================

一般在游戏中我们避免不了处理旋转或者子弹发射什么的，
就比如塔防游戏来说吧，我们需要判断敌人往哪里走，炮塔就往哪里转，转完然后朝着一个方向发射子弹（
是一个方向而不是朝一个点，就比如保卫萝卜，子弹穿过怪物继续朝那个方向飞行，直到飞往屏幕外才移除），下面来简单分析一下实现的过程，需要涉及到一点点平面向量的数学知识。

（注意匀速）

1. 旋转 : 朝着某个点的方向匀速旋转

2.发射：让子弹朝着某个点的方向匀速移动

我们来分步骤实现，先实现旋转功能：

嗯，现在假设平面中有点A和点B，A是炮塔，B是敌人，现在我们需要让炮塔A的方向朝着敌人B旋转，因为炮塔放置的时候方向向上，所以
我们要旋转的角度为α，如图

现在首先，我们创建敌人和塔

//敌人
auto enemy = Sprite::create("enemy.png");
enemy->setPostion(Point(100,200));
this->addChild(enemy);

//塔
auto tower = Sprite::create("tower.png");
tower->setPostion(Point(200,100));
this->addChild(tower);



```




然后我们让塔旋转瞄准敌人，
只是为了能射它一脸（呵呵？）



//让塔的方向旋转对准敌人

//射击方向向量
Point shootVector = enemy->getPosition() - tower->getPosition();
//向量标准化(即向量长度为1)
Point normalizedVector = ccpNormalize(shootVector) ;
//算出旋转的弧度
float radians = atan2(normalizedVector.y, - normalizedVector.x);
//将弧度转换成角度
float degree = CC_RADIANS_TO_DEGREES(radians);


//匀速旋转需要我们设置一下速度，这里假设旋转速度为 2π (rad/s)
float rotateSpeed = 2 * M_PI;
//那么旋转1弧度所用时间为
float rotate_1rad_time = 1 / rotateSpeed;
//所以旋转的时长为
float rotateDuration = fabs(radians * rotate_1rad_time);


//最后执行旋转
_sprite->runAction(RotateTo::create(rotateDuration,degree- 90));


```





需要注意一下
（1）假设点A为塔，B为敌。则 向量  shootVector = OB -OA = AB
（2）atan2（y,x）是就是反正切函数， 算出的是  点（x,y）与x轴正方向的夹角，返回的是角的弧度值

（3）所以degree算出的角度其实是与x轴正方向的夹角
（4）由于炮塔方向向上，所以  【旋转的角度  α 】= degree - 90



如果你想不起来什么是反正切，那没关系，看下面假设和图（再想不起来我只能呵呵了）
假设 tan（α） = y / x ， 则有 α = arctan（y / x）


  QQ截图20140625160225.png693×333






旋转完后接下来我们再实现射击功能：



假设有塔，子弹和敌人，位置如图，我们需要
把子弹由位置A沿着AB方向 匀速射到C（C点在屏幕外）




我们先创建敌人，塔和子弹


//敌人
auto enemy = Sprite::create("enemy.png");
enemy->setPostion(Point(100,200));
this->addChild(enemy);

//塔
auto tower = Sprite::create("tower.png");
tower->setPostion(Point(200,100));
this->addChild(tower);

//子弹，和塔在一个位置
auto tower = Sprite::create("bullet.png");
tower->setPostion(Point(200,100));
this->addChild(tower);

```



然后这次我们真的射它一脸
（再次呵呵）





//射击方向向量
Point shootVector = enemy->getPosition() - bullet->getPosition();
//向量标准化(即向量长度为1)
Point normalizedVector = ccpNormalize(shootVector);
//移动长度向量
Point overShootVector = normalizedVector * 900;
//超出屏幕的点
Point offScreenPoint = bullet->getPosition() + overShootVector;

//假设速度为500（pix/s）
float moveSpeed = 500;
//移动时间
float moveDuration = overShootVector / moveSpeed;

//执行设计
auto move = MoveTo::create(moveDuration,offScreenPoint);
CallFunc* moveDone = CallFunc::create(CC_CALLBACK_0(shootFinish,this,bullet));
bullet->runAction(Sequence::create(move,moveDone,NULL));

```


射击结束后移除子弹

//射击结束后移除
void HelloWorld::shootFinish(Node* pNode){
    Sprite* bullet = (Sprite*)pNode;
    if(bullet != NULL)
        bullet->stopAllActions();
        this->removeChild(bullet);
}


```



稍稍解释一下 ：
（1）shootVector就是向量AB。
（2）overShootVector = （AB向量标准化）× 900 即得到  AC。比如说你设置的分辨率为 800 x 400 ，那么你可以用
标准化向量 × 你最大分辨率再大一点，这样子向量就会超出屏幕之外而且长度又固定。

（3）然后根据向量OC = OA + AC ，算出要移动到的点offScreenPoint（即点C）。
（4）设置一下速度，长度一定了，所以时间 = 长度 / 速度 。


原文：http://blog.csdn.net/shun_fzll/article/details/34430045

阅读整个主题