关闭

cocos2dx3.1-3.2 action MoveBy源码分析

标签: cocos2dx MoveBy 源码 分
177人阅读 评论(0) 收藏 举报
分类:

先来一个cocos2dx的MoveBy的例子

<span style="white-space:pre">	</span>auto cat = Sprite::create("bang.png");
	this->addChild(cat,100);
	cat->setPosition(Vec2(visibleSize / 2) + origin);

	auto moveb = MoveBy::create(5,ccp(100,100));
	cat->runAction(moveb);

OK,既然有了例子,我们就仔细研究一下他的代码,

显然,MoveBy::create()入手

先看看代码

<span style="font-size:18px;">MoveBy* MoveBy::create(float duration, const Vec2& deltaPosition)
{
    MoveBy *ret = new MoveBy();
    ret->initWithDuration(duration, deltaPosition);
    ret->autorelease();

    return ret;
}</span>


//显然,看过cocos2dx源码的人都会觉得没什么好看,就是有一个函数比较特别
initWithDuration,来,我们走进去

<span style="font-size:18px;">bool MoveBy::initWithDuration(float duration, const Vec2& deltaPosition)
{
    if (ActionInterval::initWithDuration(duration))
    {
        _positionDelta = deltaPosition;
        return true;
    }

    return false;
}</span>





其实这里做的事情并不多,最明显的就是记录一下偏移位置,

把他保存了一下,除此之外就是

里面究竟干了些什么,来,走进去看看


bool ActionInterval::initWithDuration(float d)
{
    _duration = d;//保存了一下持续时间

    // prevent division by 0
    // This comparison could be in step:, but it might decrease the performance
    // by 3% in heavy based action games.
    if (_duration == 0)//这个貌似是边界条件
    {
        _duration = FLT_EPSILON;//#define FLT_EPSILON     1.192092896e-07F 
				//F是不是浮点的意思(有知道的朋友可以告诉一下我),
				//1.x*10负7次方(只会用中文表示)
				//也就是 1.x / ( 10的负7次方)    
				//就是一个很小的数嘛,写的那么神秘,能改个人看得懂的名字吗?
				//例如:VERY_SMALL_POSITIVE_NUM  ?  哈哈,开玩笑
    }

    _elapsed = 0;//这个有什么用?后面提到,先说一下,不然后面在提,估计就忘记了
<span style="white-space:pre">		</span>//科普一下,elapsed 是过去的意思,
<span style="white-space:pre">		</span>//可能作者想表达过去了多长时间的意思( 我知道你们会,google)

    _firstTick = true;//不用想,是不是第一次的?问我吗?  :true

    return true;
}




好了,这是时候我们已经看完了MoveBy::create()了,下面就F12点进去

Action * Node::runAction(Action* action)
{
    CCASSERT( action != nullptr, "Argument must be non-nil");
    _actionManager->addAction(action, this, !_running);
    return action;
}
想都不用想就是点进去
看一下参数:

action //我们创建的MoveBy

this //this?是谁,肯定是我们调用runAction的对象,cat

_running //这个有是干什么用的,后面再看,说不定不管我们事,嘻嘻

我管你是谁,先进去,爽完再说,嘻嘻,F12



<span style="font-size:18px;">void ActionManager::addAction(Action *action, Node *target, bool paused)//这里的paused不就是上面说的_running吗,噢,原来是控制暂停所用的
{
    CCASSERT(action != nullptr, "");
    CCASSERT(target != nullptr, "");

    tHashElement *element = nullptr; //hash元素,点进去好像是一个复杂的结构体
    <span style="white-space:pre">					</span>// we should convert it to Ref*, because we save it as 
    Ref* Ref *tmp = target;	//把对象,就是cat保存了下来
    HASH_FIND_PTR(_targets, &tmp, element);
    if (! element)
    {
        element = (tHashElement*)calloc(sizeof(*element), 1);
        element->paused = paused;
        target->retain();
        element->target = target;
        HASH_ADD_PTR(_targets, target, element);
    }

     actionAllocWithHashElement(element);
 
     CCASSERT(! ccArrayContainsObject(element->actions, action), "");
     ccArrayAppendObject(element->actions, action);//到这句为止,其实我也不太懂,
<span style="white-space:pre">						</span>//也不想懂,好像不大管我事
    action->startWithTarget(target);//这一句有点熟悉,点进去</span>




噢,动作类保存了cat  (为什么我一直用cat,因为那只猫是我第一个学习的项目开始就要用,好像是喵星人什么鬼来着,玩了,就是打飞机,不说了,说正事)

OK,好像runAction都看完了哦,来,显然,这些之前都是一些初始化,细心的同学都会发现MoveBy::update有一个这样的函数,这个显然就是MoveBy控制位置核心函数

然而,由于第一次,你懂的,什么都不会,当然是MoveBy的所有函数都断一下点,哈哈,笨,但是有效,来,gogogo

点点点,进去发现,原来剩下的函数不多了,而且,clone跟reverse,根本就不关事情,我要的是工作原来,OK,剩下就是update,跟startWithTarget,好两处的开头断点


void MoveBy::startWithTarget(Node *target)
{
    ActionInterval::startWithTarget(target);
    _previousPosition = _startPosition = target->getPosition();
}
显然第一处就是starWithTarget,OK,他好像保存了一下target的位置,target是谁?,还有
ActionInterval::startWithTarget(target);
里面做了些什么?  我们进去看看F11

void ActionInterval::startWithTarget(Node *target)
{
    FiniteTimeAction::startWithTarget(target);//这么坑!又有一个,等一下在上你!!
   _elapsed = 0.0f;		//这个在上面已经提到过,英文是过去的意思?</span>
<span style="white-space:pre">				</span>//还记得吗,参数意义就是累计计算过了多长时间</span>
   _firstTick = true;		//是不是第一次,呵呵~
}

void Action::startWithTarget(Node *aTarget)
{
    _originalTarget = _target = aTarget;
}


来,看完注释,继续旅游,别迷路,

进去

void Action::startWithTarget(Node *aTarget)
{
    _originalTarget = _target = aTarget;
}
这好像在哪里见过?
我不知道是不是坑爹,反正我好像第二次看到这个初始化了

显然,上述这个代码在addAction的最后一行已经用过(  也就是我们进去 runAction的时候路过这个代码,哎,不鸟他,继续走)

我们使用F10来一句一句走,直到走出MoveBy::startWithTarget,好,我日,这不是坑了自己吗,原来我们就是进入runAction()的时候

进入starWithTarget的,好吧,我斗笔了!

好!走出addAction之后,果然就是runAction,

OK,到这里剩下的就是updte了,我们按F5,让程序自己走,直到走到我们的断点

nice!到了我们梦寐以求的update了,如果有一点变成经验的同学,可能觉得,呵呵,update,参数0.0167777...,其实我刚开始也是这么想,没鸟他,

直到我读到后面的代码,我才醒悟,来,跟着代码走

<span style="font-size:18px;">void MoveBy::update(float t)
{
    if (_target)
    {
#if CC_ENABLE_STACKABLE_ACTIONS
        Vec2 currentPos = _target->getPosition();//获取对象,就是 cat,那只猫 的位置
       Vec2 diff = currentPos - _previousPosition; //这个我后面细心调试了一下,发现他一直都是(0,0)  比较斗笔
      _startPosition = _startPosition + diff;	//这个也是一个比较斗笔的行为,
					<span style="white-space:pre">	</span>//如果不细心点就会对下面的那句话有疑惑,开始位置+X1+X2,完全理解不了,
<span style="white-space:pre">						</span>//但是其实_starPosition一直没变过,都是原来的位置
        Vec2 newPos =  _startPosition + (_positionDelta * t);//其实这句话就是  开始位置 + 总偏移 * 百分比   
<span style="white-space:pre">							</span>//为什么我上面说百分比,这里有文章
<span style="white-space:pre">							</span>//想想,我当时的思路是这样子的,如果我穿的duration是2秒,那么t的最大值是2
<span style="white-space:pre">							</span>//而从看上面的公式来看,好像不对,肯定是做了处理
        _target->setPosition(newPos);
        _previousPosition = newPos;
#else
        _target->setPosition(_startPosition + _positionDelta * t);
#endif // CC_ENABLE_STACKABLE_ACTIONS
    }
}</span><span style="font-size: 24px;">
</span>



OK,让我们先看注释


看完注释之后我们按F10,一直到这个函数结束,因为我想看看其实是哪里调用了update,还有,那个参数 t 是怎么一回事

OK,我们到达一个新函数

<pre name="code" class="cpp">void ActionInterval::step(float dt)
{
    if (_firstTick)	//初始化,没什么特别
    {
        _firstTick = false;
        _elapsed = 0;	//置 0
    }
    else
    {
        _elapsed += dt;<span style="white-space:pre">		</span>//elapsed 中文翻译过去, 意思就是过去了多长时间,
<span style="white-space:pre">				</span>//简称时间累计  --用自己的语言来翻译,比较烂,哈哈,将就将就!!

    }
    
    this->update(MAX (0,                                  // needed for rewind. elapsed could be negative//什么鬼这么复杂,我们着重分析这个
                      MIN(1, _elapsed /
                          MAX(_duration, FLT_EPSILON)   // division by 0
                          )
                      )
                 );
}



这里的step,貌似也是一个一直会调用的函数,我们看一下注释

显然这里就是调用我们的update函数

参数比较复杂,独立弄出来

MAX (0,                                  
                      MIN(1, _elapsed /
                          MAX(_duration, FLT_EPSILON)  
                          )
                      )


再割一层,不看最外层的MAX,整理一下

MIN(1, _elapsed / MAX(_duration, FLT_EPSILON)  )

好像是跟1比较的数字,看看里面的MAX是是那么鬼

MAX(_duration, FLT_EPSILON) //FLT_EPSILON,进去看一下知道是一下极小正数,接近0,就这么想吧

好,假设我们的参数_duration都是正常传参,1 or 2 or 3 ...

那么 我们有 _duration >= FLT_EPSILON,OK化简一下我们的MIN

MIN(1, _elapsed / _duration )

好, 过去了的时间     除以     持续时间 ,慢慢看一下,是不是有点什么感觉,如果我们过去时间是1( 假设持续时间_duration = 5),

1秒,2秒,3秒...5秒,如果到5秒,这个方程就是变成5/5 == 1,噢,这样子,原来,我们传过去update的数字就是这么来的,并不是我们想想中的

0.0167777...  而是比这个更小的数字

有同学可能还不明白,来,我们假设一下

加入_duration = 5,那么,每次传参应该是

_elapsed  / 5,加入是第一次    //这个第一次老是出现,好烦,忍耐,忍耐

也就是0.01677...,我们可以用另外一种方式来写,就是1/60,

ok,第一次传进去的参数是

1/60/5  == 1/300  //第一次

2/60/5 = 2/300   //第二次

.....3/300  // 第三次

.

.

.

... 300 / 300 //第300次传进update的数字就是1

OK,一切的谜团终于解开了

t = 1

Vec2 newPos =  _startPosition + (_positionDelta * t);

慢着!,还有呢,你不想知道他什么时候停吗?

F10,走出我们的step函数


到达 void ActionManager::update(float dt)

发现有一个判断_currentTarget->currentAction->isDone()

bool ActionInterval::isDone(void) const
{
    return _elapsed >= _duration;
}

过去了的时间 >= 持续时间, OK大家都明白了MoveBy的原理了吗






0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:178次
    • 积分:11
    • 等级:
    • 排名:千里之外
    • 原创:1篇
    • 转载:0篇
    • 译文:0篇
    • 评论:0条
    文章分类
    文章存档