Cocos2d-x 精灵移动

来源:互联网 发布:淘宝图片怎么发链接 编辑:程序博客网 时间:2024/04/27 23:31


 


 

红哥里面使用的ccTouchesMoved,回忆下我之前的教程,使用的是ccTouchesBegan,大家可以体验一下这两种方式的不同。

当我们的手指触碰到屏幕的一瞬间,调用的是ccTouchesBegan,之后手指如果在屏幕上滑动,手指会一直调用ccTouchesMoved,所以大家可以想象一下这两种方式不同。我的方式会无视之后手指的位置,精灵只会向第一个点击的地方移动。而红哥的方式则是无视第一点触碰的坐标,一直会跟着之后的手指位置移动,这就是 began(起始)和moved(滑动)的区别。

     但实际上,我们在正常实现触碰+移动的时候,很少使用ccMove家族的成员,最重要的原因是他们非常死板,很不灵活,除了贝塞尔(CCBezier)同学比较受欢迎,其他的同学基本不能和ccTouch家族结合使用。所以接下来,我们会模拟两种比较实用的方式来实现飞机的移动。

1. 精灵以匀速向当前触摸点行动,当触摸取消时,精灵停止。

这种方式相对而言比较常见,让我们一起实现之。

第一步:让我们在ccTouchesMoved函数中添加一下代码:

    CCSetIteratorit=pTouches->begin();

    CCTouch* touch=(CCTouch*)(*it);   

    CCPointm_tBeginPos=touch->locationInView();

   m_tBeginPos=CCDirector::sharedDirector()->convertToGL(m_tBeginPos);

 

等!!!

这里插播个话题,这段代码反复出现(ccTouch家族都用他),功能完全一样!如果大家发现这种倒霉代码,最好考虑把他变成一个更可爱的子函数。

我们在helloworld头文件中加入函数声明:

    // 触摸相关

CCPoint convertToGL(CCSet *pTouches);

并且一定要保证这位同学跟ccTouches那三兄弟在一起,这才是良好的编程习惯!!不然等代码量上去了,有你哭的那天。

   之后的实现如下

CCPoint HelloWorld::convertToGL(cocos2d::CCSet *pTouches)

{

    if (!pTouches) {

        CCSetIteratorit=pTouches->begin();

        CCTouch* touch=(CCTouch*)(*it);

        CCPointm_tBeginPos=touch->locationInView();

        m_tBeginPos=CCDirector::sharedDirector()->convertToGL(m_tBeginPos);

        return m_tBeginPos;

       

    }

    assert(!"pTouches is Null!!!!!");

}

部分同学对这里面的if (!pTouches)和assert(!"pTouches is Null!!!!!"),这是本人编写子函数的一个习惯,这样如果传进来的pTouches参数为空,编译时会直接捕捉到这个错误,而不会继续下传。当然以我们现在这个规模的程序来说,确实是有点杀鸡牛刀了,但我还是推荐大家以后编写子函数的时候使用这种方式

   这样这段代码就封装好了,以后我们编写哪个ccTouch的时候,可以直接使用我们convertToGL了!(这个函数的中文意思是“转换成GL”)

 

于是我们的ccTouchesMoved函数变成了这样

void HelloWorld::ccTouchesMoved(CCSet *pTouches,CCEvent *pEvent)

{

    CCPoint m_CurrentPos=convertToGL(pTouches);

 

}

我们现在获取了当前坐标,该让player向目标移动了!

第一:先定义飞机向那儿移动的速度。

在头文件中增加

private:

    float playerSpd=1;

定义playerSpd为速度,初始值为1。

第二:找出当前帧移动的单位向量

这里普及一下基础的几何知识,一个从点A和点B的向量AB,它是B点的坐标对应减掉A点的坐标。例如A点坐标是(a1,a2),B点坐标是(b1,b2),那么AB向量为(b1-a1,b2-a2).

但是我们单求“向量”是不对的,我们应该求的是“单位向量”。这个单位向量非常神奇,A点坐标加上(单位向量乘以两点间距离)之后,得出的结果是B点坐标。而速度则是每帧移动的距离,则(单位向量*速度)就可以计算出每帧的点坐标值啦!大家可以理解这个“单位向量”为“方向”。

为了实现这一步,我们可以定义一个子函数来实现上述过程,问题就在于这个子函数应该定义在什么地方。

其实可以思考一下,我们什么时候会用到这个子函数?所有存在CCPoint的情况下!

所以这个子函数最好定义在CCPoint类的类内。

另外一方面,为了能够让过程和数学过程更像,我们需要重载CCPoint的减号“-”,让这个函数支持两个CCPoint类做减法。

So,在CCPoint类的实现中中添加如下两个函数体(声明我就不写了,懒,大家自己加到头文件里吧)

CCPoint& CCPoint::operator- (constCCPoint& other)

{

    this->x=this->x-other.x;

    this->y=this->y-other.y;

    return *this;

}

另外增加计算单位向量的函数

CCPoint CCPoint::computeUnitVector()

{

    CCPoint p1=*this;

    assert(sqrt((p1.x)*(p1.x)+(p1.y)*(p1.y)));

    p1.x=p1.x/sqrt((p1.x)*(p1.x)+(p1.y)*(p1.y));

    p1.y=p1.y/sqrt((p1.x)*(p1.x)+(p1.y)*(p1.y));

    return p1;

}上面这个函数直接计算出某向量的单位向量了.

现在CCPoint类已经不单纯是一个“点”了,它现在还可以表示一个“向量”和“单位向量”,所以我们还需要用到让CCPoint类能够乘以一个speed。

在CCPoint头文件中加入定义,并在在CCPoint实现文件中,加入如下代码:

 

CCPoint& CCPoint::operator*(constfloat& speed)

{

    this->x=this->x*speed;

    this->y=this->y*speed;

    return *this;   

}

 

另一方面,还需要重载CCPoint的+号函数,能让两个CCPoint相加,得出一个新坐标,具体实现如下

CCPoint& CCPoint::operator+(constCCPoint &other)

{

    this->x=this->x+other.x;

    this->y=this->y+other.y;

    return *this;

}

OK,让我们回到CCTouchesMoved中;加入如下代码

void HelloWorld::ccTouchesMoved(CCSet *pTouches,CCEvent *pEvent)

{

    CCPoint m_CurrentPos=convertToGL(pTouches);

    //获得当前坐标

    CCPoint playerCurPos=player->getPosition();

    //如果距离不为0,则向那个方位移动一个速度

    if (ccpDistance(playerCurPos,m_CurrentPos)!=0)

    {

   

        CCPointm_vector=m_CurrentPos-playerCurPos;

        //m_vector转换为单位向量

        m_vector=m_vector.computeUnitVector();

        //每次调用ccTouchesMoved的时候,都向单位方向移动一个速度值

        player->setPosition(playerCurPos+m_vector*playerSpd);

 

    }

 

}

现在,当玩家在屏幕上滑动时,飞机就会向触摸的地方移动了。但有一个非常残酷的事实… 现在只有滑动的时候,才会调用CCTouchesMoved - -,CCTouchesMoved对对触摸进行了判断,所以现在这架飞机变成了非常奇异的家伙,你滑动手指,才会向你移动,你不滑动手指,他坚决不动.

很遗憾~苹果在底层做了判断,毕竟,如果手一直按住都响应的话,那设备将及其费电- -,所以我们只能换另外一种方式了~

可以仔细思考一下我们需求的流程,如下:

点击屏幕,则屏幕向该点以一定速度移动。

如果中途发生滑动,则修改该点坐标

如果触摸停止,则此动作停止

所以我们可以采用另外一种方式来实现此功能

ccTouchesBegan用来初始化点坐标,并激活飞机的一个动作,此动作不停得向触摸点移动

ccTouchesMoved用来改变此点的坐标

ccTouchesEnded终结此动作。

我们如果需要实现此功能,则需要以上三个函数协同工作,才能共同达成目标,下一讲将会讲述如何实现此功能。


OK,承接上讲,我们先插播个上节遗漏的问题。

我们上节重载了CCPoint的*号,使之能乘以一个float型后计算每次调用的偏移量。后来想了下,如果这个*号后来和int相乘要肿么办呢?我稍微骚了一下,把这个重载函数改装成了一个模板重载函数,代码如下

         //

   //定义模板,因为未来的speed有可能是不同类型的~

    template<typename item>

    CCPoint& operator* (const item& speed)

    {

        this->x=this->x*speed;

        this->y=this->y*speed;

        return *this;

    }

 

一般来说,这里是用不着这么高级的功能的,这里用模板来实现泛型,是有点骚气横流的感觉,但下面这个例子就稍微实用一些了。

 

现在我想实现个函数,通过调用这个函数,我可以把任何作为参数输入进去的CCNode家族成员设置到原点this->setPositon(CCPointZero);

好吧…不要吐槽这个功能是多么的不实用,还是稍微考虑下实现吧

    void setZero(CCSprite* sp)

    {

        sp->setPosition(CCPointZero);

}

如果我们的对象是CCSprite*,这么做是相当没问题的。可如果是个CCLayer呢?CCNode呢?甚至是一个组合了CCSprite的自定义对象呢?(我们假设该对象重写了setPosition函数)。

所有我们可以采用如下的方式。

    template<typename T>

    void setZero(T* sp)

    {

        sp->setPosition(CCPointZero);

    }

嗯,这样就不担心将来的不合理要求啦。

//实话,上面这个例子也不咋地,没有达到必然要实用模板的程度,但是掌握这门技术还是有好处的,我曾经看见过某位勤奋的群友整整写了四个除了参数一模一样的函数,我当时想,假以时日,等代码量起来以后,这位同学就再也没有时间打dota泡妹子了,为了妹子,大家至少应该学会点偷懒的技术啊~

好吧,题外话说的够多了,接下来我们继续开我们的小飞机。

上节的最后,我们介绍了最可行的一种方案如下:

ccTouchesBegan用来初始化点坐标,并激活飞机的一个动作,此动作不停得向触摸点移动

ccTouchesMoved用来改变此点的坐标

ccTouchesEnded终结此动作。

 

OK,来思考我们需要什么东。

第一个,一个记录当前状态的布尔值,按下时设为真,抬起时重置为假

bool isPointActive=false;

第二个,用来记录当前目标点的CCPoint

CCPoint tagetPoint;

第三个,一个每帧向当前目标点移动函数

    额……

前两个都很简单,直接加到HelloWorld类里就行,重点是第三个呀呀呀,我们这里隆重介绍CCNode家族的重量级成员

scheduleUpdate();

 

当你在任意的CCNode家族成员(CCLayer啊,CCSprite啊..)的init()函数中添加这么一句时,它就会每帧调用当前类的update(float dt)函数。

    update函数也算是系出名门了,所有继承自CCObject类的成员都有个这么玩意。

当被启用时,此函数内部就成为一个状态机,所有被动的游戏逻辑都可以在这里面写。

大家需要很严格的写update函数,不然不予调用。

//非常操蛋,之前版本的update的参数是CCTime型,现在改成了float型,大家一定要注意参数的改动啊。

 

让我们来编写自己函数,改函数每次被调用时,会向tagetPoint移动一个速度的单位向量。

void HelloWorld::movePlayer()

{

    CCPoint playerCurPos=player->getPosition();

    //如果两点不重合,则向那个单位移动一速度单位

    if (ccpDistance(playerCurPos,target_Point)!=0)

    {

        CCPoint m_vector=target_Point-playerCurPos;

        //m_vector转换为单位向量

        m_vector=m_vector.computeUnitVector();

        //CCLog("%f and%f",m_vector.x,m_vector.y);

        //每次调用ccTouchesMoved的时候,都向单位方向移动一个速度值

        player->setPosition(playerCurPos+m_vector*playerSpd);

  

    }

   

}

//-----------------------------------------------

-   - 这里面我发现了一个之前教程的bug..特此声明,千万别造成大家困扰。这个bug是我在重载CCPoint里的 “+”号造成的,之前的方式是直接对this指针进行变换,这样的话,则下列的语句

Point1+Point2;

会造成Point1的变化!所以下面是修复后的符号重载函数

CCPoint CCPoint::operator- (constCCPoint& other)//11.16 try tooverload the minus synax

{

    CCPoint temp;

    temp.x= this->x-other.x;

    temp.y= this->y-other.y;

    return temp;

 

}

 

 CCPoint CCPoint::operator+(constCCPoint &other)

{

    CCPoint temp;

    temp.x= this->x+other.x;

    temp.y= this->y+other.y;

    return temp;

}

//-------------------------------------------

之后我们在ccTouches各函数里里添加如下代码:

void HelloWorld::ccTouchesBegan(CCSet *pTouches,CCEvent *pEvent)

{

    isPointActive=true;

    target_Point=convertToGL(pTouches);

 

}

void HelloWorld::ccTouchesMoved(CCSet *pTouches,CCEvent *pEvent)

{

    target_Point=convertToGL(pTouches);

 

}

void HelloWorld::ccTouchesEnded(CCSet *pTouches,CCEvent *pEvent)

{

    isPointActive=false;

}

在update中加入如下代码:

void HelloWorld::update(float dt)

{

  

    if (isPointActive)

    {

        movePlayer();

    }

 

}

看懂了么?

在Began中将旗标isPointActive设置为true,同时将首次点击的位置坐标赋值给target_Point,Moved中负责更新此坐标,Ended中则将旗标设为false。

而update则负责检查旗标,如果旗标为真,则目标向target_Point位置移动一个速度值。

现在运行我们的游戏,就可以让飞机向点中的位置移动了!

大家很可能发现飞机在接近目标值的时候会发生颤抖,那是因为飞机要反复修正当前坐标与目标坐标的差值。而这个差值小于飞机的速度…所以就会反复修正,我们更改一下movePlayer里的判断条件就好了。

void HelloWorld::movePlayer()

{

    CCPoint playerCurPos=player->getPosition();

//之前是“如果两点不重合,则向那个单位移动一速度单位”

//现在是“如果两点间距离大于一个速度单位,则移动~

    if (ccpDistance(playerCurPos,target_Point)>playerSpd)

    {

        CCPoint m_vector=target_Point-playerCurPos;

        //m_vector转换为单位向量

        m_vector=m_vector.computeUnitVector();

        //CCLog("%f and%f",m_vector.x,m_vector.y);

        //每次调用ccTouchesMoved的时候,都向单位方向移动一个速度值

        player->setPosition(playerCurPos+m_vector*playerSpd);

  

    }

   

}

0 0
原创粉丝点击