先来一个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>
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的原理了吗