I'm using spine-runtimes with cocos2dx-3.2. I'm now working on an application for saving an spine animation's frames into png pictures. I found that the update method of class Layer doesn't run syncronized. I have many animations in one spine file(jsonFile, atlasFile). I want the application plays all the animation one by one and record each frames into png files. I found that the final frame of an animation goes overlap the first frame of next animtion after animation siwtched. I don't know how to modify the program. Can someone please give me some advice?
And also, I tested these codes with a spine file which has only one animation. I got first frame and last frame binary identical png files.
#include "HelloWorldScene.h"
#include <direct.h>
#include "../proj.win32/misc.h"
#include "../proj.win32/GmDebug.h"
#include <fstream>
#include <vector>
#include <map>
#pragma warning (disable: 4996)
USING_NS_CC;
using namespace spine;
using namespace std;
extern wstring gStrJsonFilePath, gStrAtlasFilePath, gOutPutPath;
extern string jsonFile, atlasFile, sffFileName, outputPath;
extern int gFPS;
extern int posX, posY;
extern int offsetX, offsetY;
extern int backGroundColor;
HelloWorld* hInstance = nullptr;
vector<string> animationNames;
vector<string> fpsNames;
vector<string> frameNames;
string pngFilePath;
unsigned int animIdx = 0;
float gScale = 0.5f;
float ticks;
int groupId = 0;
int picId = 0;
int debugIndex = 0;
bool animJustSwitched = false;
ofstream ofs_airFile("character.air");
ofstream ofs_defFile("character-sff.def");
Scene* HelloWorld::createScene()
{
auto scene = Scene::create();
auto layer = HelloWorld::create();
scene->addChild(layer);
hInstance = layer;
return scene;
}
bool HelloWorld::init()
{
//////////////////////////////
if ( !Layer::init() ) {
return false;
}
//
ofs_defFile << "[Option]\n";
ofs_defFile << "sprite.compress.5 = lz5\n";
ofs_defFile << "sprite.compress.8 = rle8\n";
ofs_defFile << "sprite.compress.24 = none\n";
ofs_defFile << "sprite.decompressonload = 0\n";
ofs_defFile << "sprite.detectduplicates = 0\n";
ofs_defFile << "sprite.autocrop = 1\n";
ofs_defFile << "pal.detectduplicates = 1\n";
ofs_defFile << "pal.discardduplicates = 1\n";
ofs_defFile << "pal.reverseact = 0\n";
ofs_defFile << "pal.reversepng = 0\n";
ofs_defFile << "sprite.usepal = -1\n\n";
ofs_defFile << "[Output]\n";
ofs_defFile << "filename = "<<sffFileName<<"\n\n";
ofs_defFile << "[Sprite]\n";
//
Size visibleSize = Director::getInstance()->getVisibleSize();
Vec2 origin = Director::getInstance()->getVisibleOrigin();
actor = SkeletonAnimation::createWithFile(jsonFile.c_str(), atlasFile.c_str(), gScale);
spSkeleton* skeleton = actor->getSkeleton();
for (int i = 0; i < skeleton->data->animationsCount; i++) {
string animName = skeleton->data->animations[i]->name;
animationNames.push_back(animName);
DebugPrintln("[idx]%d, [animName]%s", i, animName.c_str());
}
DebugPrintln("animation counts:[%d]", animationNames.size());
animIdx = 0;
_animName = animationNames[animIdx];
_lastAnimName = "";
actor->setScaleX(1.0f);
actor->setScaleY(1.0f);
actor->setAnimation(0, animationNames[animIdx].c_str(), false);
actor->setPosition(posX, posY);
actor->setAnchorPoint(Vec2(0, 0));
this->addChild(actor);
ticks = 60 / gFPS;
_lastGroupIdx = "0";
//
auto animComplete = [this] (int trackIdx, int loopCount) {
DebugPrintln("animation [%s] complete, picId = [%d]", _animName.c_str(), picId);
if (!this->animationPlayNext())
{
Director::getInstance()->end();
}
};
auto animStart = [this](int trackIdx) {
picId = 0;
DebugPrintln("(
) picId reset to ZERO! animation start! groupIdx:[%s] _animName:[%s] _lastAnimName:[%s]",
_groupIdx.c_str(), _animName.c_str(), _lastAnimName.c_str());
//actor->setToSetupPose();
//actor->setBonesToSetupPose();
//actor->setSlotsToSetupPose();
};
auto animEnd = [this](int trackIdx) {
DebugPrintln("animation [%s] end, picId = [%d]", _animName.c_str(), picId);
};
actor->setStartListener(animStart);
actor->setEndListener(animEnd);
actor->setCompleteListener(animComplete);
pngFilePath = ws2s(gOutPutPath);
scheduleUpdate();
DebugPrintf("\n
Animation Start
\n\n");
return true;
}
bool HelloWorld::animationPlayNext()
{
++groupId;
if (animIdx < animationNames.size()) {
auto animName = animationNames[animIdx++];
actor->setAnimation(0, animName.c_str(), false);
int idx = animName.find('_');
_groupIdx = animName.substr(0, idx);
ofs_airFile<<"\n\n; "<<_groupIdx<<endl;
char buf[256] = {0};
sprintf(buf, "[Begin Action %s]", _groupIdx.c_str());
ofs_airFile<<buf<<endl;
DebugPrintln("animIdx:%d groupNumber:%s animName:%s", animIdx, _groupIdx.c_str(), animName.c_str());
float _x, _y;
actor->getPosition(&_x, &_y);
DebugPrintln("actor position:%f, %f", _x, _y);
return true;
}
return false;
}
void HelloWorld::render()
{
DebugPrintln("render group:%s", _groupIdx.c_str());
if (!_groupIdx.empty() && isdigit(_groupIdx.c_str()[0])) {
if (picId == 0)
{
auto animName = animationNames[animIdx - 1];
int idx = animName.find('_');
_groupIdx = animName.substr(0, idx);
_animName = animName;
}
//
saving *.def Mugen Picture Data
//
char strBuf[256] = { 0 };
sprintf(strBuf, "%s, %d, %s\%s_%04d.png, %d, %d",
_groupIdx.c_str(), picId, outputPath.c_str(), _animName.c_str(), picId, offsetX, offsetY);
ofs_defFile<<strBuf<<endl;
//
saving *.air Mugen Animation Data
//
char buf[256] = {0};
sprintf(buf, "%s, %d, 0, 0, %d", _groupIdx.c_str(), picId, (int)ticks);
ofs_airFile<<buf<<endl;
//
saving *.png File
//
char path[256] = { 0 };
sprintf(path, "%s_%04d.png", _animName.c_str(), picId);
DebugPrintln("save picture:
%s, debugIndex:%d", path, debugIndex++);
saveScreenShot(path);
++picId;
}
}
void HelloWorld::update(float deltaTime)
{
Director* director = Director::getInstance();
director->pause();
DebugPrintln("update animation
deltaTime:%f picId:%d", deltaTime, picId);
if (animIdx <= animationNames.size()) {
actor->setScale(1.0);
render();
}
animJustSwitched = false;
director->resume();
}
void HelloWorld::saveScreenShot(const std::string& pngFileName)
{
byte r=0, g=0, b=0, a=0;
r = (backGroundColor&0xff000000) >> 24;
g = (backGroundColor&0x00ff0000) >> 16;
b = (backGroundColor&0x0000ff00) >> 8;
a = backGroundColor&0x000000ff;
CCSize size = CCDirector::sharedDirector()->getWinSize();
CCRenderTexture* texture = CCRenderTexture::create((int)size.width, (int)size.height);
texture->retain();
texture->setAnchorPoint(Vec2(0, 0));
texture->setPosition(Vec2(posX, posY));
glClearColor(r, g, b, a);
texture->begin();
CCDirector::sharedDirector()->getRunningScene()->visit();
texture->end();
//
bool ret = texture->saveToFile(pngFileName, Image::Format::PNG);
assert(ret);
DebugPrintln("saving png File Name%s", pngFileName.c_str());
}
void HelloWorld::menuCloseCallback(Ref* pSender)
{
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WP8) || (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT)
MessageBox("You pressed the close button. Windows Store Apps do not implement a close button.","Alert");
return;
#endif
Director::getInstance()->end();
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
exit(0);
#endif
}
can someone tell me if I get first and last frame identical is correct or wrong?