class TestFrameEvent : public ArmatureTestLayer
{
public:
virtual void onEnter() override;
virtual std::string title() const override;
void onFrameEvent(cocostudio::Bone *bone, const std::string& evt, int originFrameIndex, int currentFrameIndex);
void checkAction(float dt);
protected:
NodeGrid* _gridNode;
};
ArmatureDataManager::getInstance()->addArmatureFileInfoAsync("armature/HeroAnimation.ExportJson", this, schedule_selector(TestAsynchronousLoading::dataLoaded));
#define FRAME_EVENT_ACTION_TAG 10000
void TestFrameEvent::onEnter()
{
ArmatureTestLayer::onEnter();
_gridNode = NodeGrid::create();
armature = Armature::create("HeroAnimation");//创建一套骨骼
armature->getAnimation()->play("attack");//得到一个动作
//
// armature->getAnimation()->gotoAndPlay(0);
armature->getAnimation()->setSpeedScale(0.5);//设置播放速度
//设置位置
armature->setPosition(Point(VisibleRect::center().x - 50, VisibleRect::center().y -100));
/*
* Set armature's frame event callback function
* To disconnect this event, just setFrameEventCallFunc(nullptr);
2、用途与原理
首先介绍一下帧事件。正如其名:一个与帧相关联的事件。
为什么要这么做呢?首先没人想做一大堆碎动画,然后一点一点拼着播放吧。另外,有时候流程与事件控制最好关联到帧。比如一个攻击动作,有出刀和收刀两部分。伤害自然是在刀所触到敌人时候产生的。做成两个动画比较麻烦,程序要管理大量的动画,而且美工也会很郁闷:不但给你们切图,还要给你们切动画。如果用固定时间来做,也是会有问题。比如机器卡了,帧数就会下降。这个时候,时间就不准确了。用帧事件的方式就会比较好,到了“触刀”那帧,就会有事件,能较好的处理这种情况。
它的原理是监听。当运行到事件所在的帧时,会触发回调。我们要做的就是在回调函数中操作,判断这一帧是不是我们想要的,标记的方式是Tag字符串。
3、设置帧事件
打开项目,运行动画编辑器,切换到动画模式。
将时间轴拖动到相应帧处,选择一个对象,这里我选择的是左手。然后在它的属性中设置帧事件。
*/
armature->getAnimation()->setFrameEventCallFunc(CC_CALLBACK_0(TestFrameEvent::onFrameEvent, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
_gridNode->addChild(armature);
addChild(_gridNode);//添加到场景树上
//开启更新函数checkAction
schedule( schedule_selector(TestFrameEvent::checkAction) );
}
std::string TestFrameEvent::title() const
{
return "Test Frame Event";
}
void TestFrameEvent::onFrameEvent(Bone *bone, const std::string& evt, int originFrameIndex, int currentFrameIndex)
{
CCLOG("(%s) emit a frame event (%s) at frame index (%d).", bone->getName().c_str(), evt.c_str(), currentFrameIndex);
Action * getTempAct = _gridNode->getActionByTag(FRAME_EVENT_ACTION_TAG);
//如果动作不存在 或者 动作完成
if (!getTempAct || getTempAct->isDone())
{
_gridNode->stopAllActions();//停止精灵的所有动作
//使用 范围、是否 shatter(粉碎)z顶点、grid 的尺寸、duration 创建一个 网格action
/** creates the action with a range, whether of not to shatter Z vertices, a grid size and duration */
// static ShatteredTiles3D* create(float duration, const Size& gridSize, int range, bool shatterZ);
ActionInterval *action = ShatteredTiles3D::create(0.2f, Size(16,12), 5, false);
action->setTag(FRAME_EVENT_ACTION_TAG);
//_gridNode->runAction(action);
}
}
void TestFrameEvent::checkAction(float dt)
{
if (armature==nullptr) {
return;
}
// int mm_movementList = armature->getAnimation()->getMovementCount();
// for (int i=0; i<mm_movementList; i++) {
// armature->getAnimation()->playWithIndex(i);
// }
if ( _gridNode->getNumberOfRunningActions() == 0 && _gridNode->getGrid() != nullptr)
_gridNode->setGrid(nullptr);
}