Cocos的动作系统以及自定义Action

来源:互联网 发布:c 查看dll源码 编辑:程序博客网 时间:2024/06/06 02:45

cocos引擎里面比较有特色的一个地方,如果接触过unity或者其他引擎就会发现其他就没这样的特性(unity的post effect也很帅)。我一个Node直接runAction一个CCAction类就能实现简单的动画。自己的项目也使用了拓展CCAction实现一个新的动作,这个以后再细讲。这里不谈论怎么使用这些动作,官方Tests里面有,这里就讲讲个人理解的源码。

首先是一个Node使用runAction方法后:

cocos引擎里面的ActionManager源码图片

 

其会调用ActionManager里面的addAction方法,不过这里首先看看ActionManager里面中的一些变量:

Image2

Image3

其是使用一个数组来放置结构体对象,这些动作结构体对象就是我们对应节点执行的动作。引擎是使用一个调度器来对这些节点上的动作进行执行的。

下面看看addAction方法:

Image4

这里注意到最后执行了Action的startWithTarget方法,这个是这个动作首先会被执行的方法。

然后呢?我们看Director的初始化时候会初始化ActionManager:

这里同时开启了调度器,也就是其会执行ActionManager中的update方法:

这里注意到其会执行Action的step方法,最后如果动作播放完了就执行stop方法。

源码就讲到这里,下面看看如何实际运用,也就是我自己写一个Action。

先看头文件吧:

class BoxfilterAct : public ActionInterval

{
public:
static BoxfilterAct* create(float durtion = 1.0f ,float from = 0.0f, float to = 0.01f,bool isSkenAnimation = false);

virtual BoxfilterAct* clone() const override ;
virtual BoxfilterAct* reverse() const override ;
virtual void startWithTarget(Node *target) override;

virtual void update(float time);

void setShader(std::string name);
protected:

BoxfilterAct(){};
virtual ~BoxfilterAct(){};

bool init(float durtion, float from, float to, bool isSkenAnimation = false);
private:

//动作持续的时间
float _durition;

//动作开始的数字
float _from;
//动作结束的数字
float _to;

//动作持续的差值
float _deltaNumber;

//记录当前的num
float _num;
//是否是骨骼动画
bool _isSke;

GLProgram* _shader;
GLProgramState* _state;
};

这里注意到首先会有一个startWithTarget方法,也就是这个动作刚执行的时候会执行的方法:

void BoxfilterAct::startWithTarget(Node *target)
{
ActionInterval::startWithTarget(target);

_num = _from;
target->setGLProgramState(_state);
_state->setUniformFloat(_shader->getUniformLocationForName("u_number"), _num);

}

这里首先调用了父类中的startWithTartget方法,然后下面是自定义的,设置这个动作初始值,设置GLProgramState,并且设置传入值。

这个类是使用一个Shader来进行的,这里的作用的因为我需要事件渐变,比如我人物变灰不是一瞬间便灰的,而是在一定时间段内渐变变灰的。

接下去看的是熟悉的Create方法:

BoxfilterAct* BoxfilterAct::create(float time, float from, float to, bool isSke )
{
auto filter = new BoxfilterAct();
filter->init(time, from, to, isSke);
filter->autorelease();

return filter;

}

bool BoxfilterAct::init(float time, float from, float to, bool isSke)
{
if (ActionInterval::initWithDuration(time))
{
_durition = time;
_from = from;
_to = to;
_deltaNumber = _to - _from;
_isSke = isSke;

if (_isSke)
{
this->setShader("BoxFilterMVP");
}
else
{
this->setShader("BoxFilterP");
}

_state = GLProgramState::getOrCreateWithGLProgram(_shader);
return true;
}

return false;
}

这个是这个动作创建的方法,创建之后runAction之后真正执行会执行其的update方法:

void BoxfilterAct::update(float time)
{
auto animationInter = 1.0f / Director::getInstance()->getAnimationInterval();
_num += _deltaNumber / ((animationInter)* _duration);
_state->setUniformFloat(_shader->getUniformLocationForName("u_number"), _num );
}

这里是在一定时间内反复传入uniform值给shader来制作出画面特效。

具体的自定义动作就是这样,你可以这样使用延时动作,也可以直接使用瞬时动作。

关于ACTIONMANAGER的缺点:

因为其是有一个集合里面遍历所有动作然后执行其update方法,所以这就存在一点的延迟,如果你是比较粗略的动作就可以忽略,但是如果是需要比较精细的可能会出现最后update不是在规定时间停止而是在其后一点点的时间。我想不出来什么好办法处理,要么在stop函数中进行设置最后效果,要么就使用Sequence最后再加上一个CallFunc动作来进行设置。

0 0
原创粉丝点击