时间:2021-07-01 10:21:17 帮助过:26人阅读
目前我的cocos2d-x编辑器的动画部分接口采用的是spriter动画编辑器提供的接口,spriter动画编辑器虽然简陋,但一般的需求基本上能够满足。可以在http://www.brashmonkey.com/spriter.htm下载,另外cocos2d-x的接口可以在论坛http://www.brashmonkey.com/foru
目前我的cocos2d-x编辑器的动画部分接口采用的是spriter动画编辑器提供的接口,spriter动画编辑器虽然简陋,但一般的需求基本上能够满足。可以在http://www.brashmonkey.com/spriter.htm下载,另外cocos2d-x的接口可以在论坛http://www.brashmonkey.com/forum/viewtopic.php?f=3&t=870下载。
接口导入进来之后,一切都很正常,然而在采用该接口进行开发时,发现有几个比较严重的问题。
问题1:资源采用多文件夹形式存储,渲染效率低
问题2:一个png只能有一个ccsprite,在编辑器是正常的,然而采用论坛那个接口导入到cocos2d-x就有问题。
问题3:精灵不能flip
问题4:不支持scale变换
陆续修改了上述4个问题
资源采用Sprite Frame实现
png的bug线性实现,时间复杂度O(1)
flip的实现参考cocos2d-x源码中CCTransitionFlipX的实现——摄像头换位
scale参考angle的变换就OK了
代码如下:
(代码中掺杂了编辑器部分的代码,精力有限,本代码仅供参考)
头文件:
//------------------------------------------------------------------------ // // SCMLAnimator : KickStarter project Spriter renderer for cocos2d-x. // // Spriter website : http://www.kickstarter.com/projects/539087245/spriter // // Licensed under the BSD license, see LICENSE in root for details. // // Copyright (c) 2012 James Hui (a.k.a. Dr.Watson) // // For latest updates, please visit http://jameshui.com // //------------------------------------------------------------------------ #ifndef _CC_SPRITER_X_H_ #define _CC_SPRITER_X_H_ #include#include #include "JEvent.h" #include "cocos2d.h" #include "TouchSprite.h" #include "tinyxml.h" class CCSpriterX; #define FILE_SPRITE_SIZE 128 namespace SCMLHelper { struct File { File(); ~File(); void Init(TiXmlNode *node); int id; std::string name; float width; float height; //一个文件可能有多个关联 cocos2d::CCSprite* sprites[FILE_SPRITE_SIZE]; }; class Folder { public: Folder(); ~Folder(); void Init(TiXmlNode *node); int GetFileCount(); File *GetFile(int index); private: int mId; std::string mName; std::vector mFiles; }; struct ObjectRef { void Init(TiXmlNode *node); int id; int timeline; int key; int z_index; }; struct Object { void Init(TiXmlNode *node, CCSpriterX *animator, int timelineId); int folder; int file; float x; float y; float angle; float scaleX; float scaleY; float pivot_x; float pivot_y; int z_index; cocos2d::CCSprite *sprite; }; class Key { public: Key(); ~Key(); void Init(TiXmlNode *node, CCSpriterX *animator, int timelineId); int GetObjectRefCount(); ObjectRef *GetObjectRef(int index); int GetObjectCount(); Object *GetObject(int index); float GetTime(); bool IsSpinCounterClockwise(); private: int mId; float mTime; bool mSpinCounterClockwise; std::vector
//------------------------------------------------------------------------
//
// CCSpriterX : KickStarter project Spriter renderer for cocos2d-x.
//
// Spriter website : http://www.kickstarter.com/projects/539087245/spriter
//
// Licensed under the BSD license, see LICENSE in root for details.
//
// Copyright (c) 2012 James Hui (a.k.a. Dr.Watson)
//
// For latest updates, please visit http://jameshui.com
//
//------------------------------------------------------------------------
#include "CCSpriterX.h"
#include "Common.h"
#include "jerror.h"
USING_NS_CC;
namespace SCMLHelper
{
///////////////////////////////////////////////////////////////////////////////////
File::File()
{
for(size_t i=0; irelease();
}
}
}
void File::Init(TiXmlNode *node)
{
TiXmlElement *element = node->ToElement();
if (element)
{
int intValue;
float floatValue;
if (element->QueryIntAttribute("id", &intValue) == TIXML_SUCCESS)
id = intValue;
else
id = 0;
name = element->Attribute("name");
if (element->QueryFloatAttribute("width", &floatValue) == TIXML_SUCCESS)
width = floatValue;
else
width = 0;
if (element->QueryFloatAttribute("height", &floatValue) == TIXML_SUCCESS)
height = floatValue;
else
height = 0;
if (name.size()>0)
{
//资源全部放到scml目录中!
std::string path = workPath+gConfig->read("res")+"/";
//sprite = CCSprite::create((path+"scml/"+name).c_str());
sprites[0] = CCSprite::createWithSpriteFrameName(name.c_str());
sprites[0]->retain();
}
}
}
///////////////////////////////////////////////////////////////////////////////////
Folder::Folder()
: mId(0)
{
mFiles.reserve(50);
}
Folder::~Folder()
{
int count = mFiles.size();
for (int i=0;iToElement();
if (element)
{
int intValue;
if (element->QueryIntAttribute("id", &intValue) == TIXML_SUCCESS)
mId= intValue;
mName = element->Attribute("name")==0?".":element->Attribute("name");
for (TiXmlNode* fileNode = node->FirstChild(); fileNode; fileNode = fileNode->NextSibling())
{
File *file = new File();
file->Init(fileNode);
mFiles.push_back(file);
}
}
}
///////////////////////////////////////////////////////////////////////////////////
void ObjectRef::Init(TiXmlNode *node)
{
TiXmlElement *element = node->ToElement();
if (element)
{
int intValue;
if (element->QueryIntAttribute("id", &intValue) == TIXML_SUCCESS)
id = intValue;
else
id = 0;
if (element->QueryIntAttribute("timeline", &intValue) == TIXML_SUCCESS)
timeline = intValue;
else
timeline = 0;
if (element->QueryIntAttribute("key", &intValue) == TIXML_SUCCESS)
key = intValue;
else
key = 0;
if (element->QueryIntAttribute("z_index", &intValue) == TIXML_SUCCESS)
z_index = intValue;
else
z_index = 0;
}
}
///////////////////////////////////////////////////////////////////////////////////
void Object::Init(TiXmlNode *node, CCSpriterX *animator, int timelineId)
{
sprite = NULL;
float scaleFactor = CCDirector::sharedDirector()->getContentScaleFactor();
TiXmlElement *element = node->ToElement();
if (element)
{
int intValue;
float floatValue;
if (element->QueryIntAttribute("folder", &intValue) == TIXML_SUCCESS)
folder = intValue;
else
folder = 0;
if (element->QueryIntAttribute("file", &intValue) == TIXML_SUCCESS)
file = intValue;
else
file = 0;
if (element->QueryFloatAttribute("x", &floatValue) == TIXML_SUCCESS)
x = floatValue/scaleFactor;
else
x = 0;
if (element->QueryFloatAttribute("y", &floatValue) == TIXML_SUCCESS)
y = floatValue/scaleFactor;
else
y = 0;
if (element->QueryFloatAttribute("angle", &floatValue) == TIXML_SUCCESS)
angle = floatValue;
else
angle = 0;
if (element->QueryFloatAttribute("scale_x", &floatValue) == TIXML_SUCCESS)
scaleX = floatValue;
else
scaleX = 1;
if(scaleX < 1){
jlog("shit");
}
if (element->QueryFloatAttribute("scale_y", &floatValue) == TIXML_SUCCESS)
scaleY = floatValue;
else
scaleY = 1;
if (element->QueryFloatAttribute("pivot_x", &floatValue) == TIXML_SUCCESS)
pivot_x = floatValue;
else
pivot_x = 0;
if (element->QueryFloatAttribute("pivot_y", &floatValue) == TIXML_SUCCESS)
pivot_y = floatValue;
else
pivot_y = 1;
if (element->QueryIntAttribute("z_index", &intValue) == TIXML_SUCCESS)
z_index = intValue;
else
z_index = 0;
sprite = animator->getSprite(folder, file, timelineId);
}
}
///////////////////////////////////////////////////////////////////////////////////
Key::Key()
: mId(0)
, mTime(0)
, mSpinCounterClockwise(true)
{
mObjects.reserve(50);
mObjectRefs.reserve(50);
}
Key::~Key()
{
int count = mObjects.size();
for (int i=0;iToElement();
if (element)
{
int intValue;
float floatValue;
if (element->QueryIntAttribute("id", &intValue) == TIXML_SUCCESS)
mId = intValue;
float time = 0;
if (element->QueryFloatAttribute("time", &floatValue) == TIXML_SUCCESS) // was in milliseconds, convert to seconds instead
time = floatValue/1000.0f;
mTime = time;
if (element->QueryIntAttribute("spin", &intValue) == TIXML_SUCCESS)
mSpinCounterClockwise = !(intValue == -1);
for (TiXmlNode* objNode = node->FirstChild(); objNode; objNode = objNode->NextSibling())
{
element = objNode->ToElement();
const char *tabObj = element->Value();
if (strcmp(tabObj, "object_ref")==0)
{
ObjectRef *ref = new ObjectRef();
ref->Init(objNode);
mObjectRefs.push_back(ref);
}
else if (strcmp(tabObj, "object")==0)
{
Object *obj = new Object();
obj->Init(objNode, animator, timelineId);
mObjects.push_back(obj);
}
}
}
}
///////////////////////////////////////////////////////////////////////////////////
Timeline::Timeline()
: mId(0)
{
mKeyframes.reserve(50);
}
Timeline::~Timeline()
{
int count = mKeyframes.size();
for (int i=0;iToElement();
if (element)
{
if (element->QueryIntAttribute("id", &intValue) == TIXML_SUCCESS)
mId = intValue;
for (TiXmlNode* keyNode = node->FirstChild(); keyNode; keyNode = keyNode->NextSibling())
{
element = keyNode->ToElement();
if (element)
{
Key *keyframe = new Key();
keyframe->Init(keyNode, animator, mId);
mKeyframes.push_back(keyframe);
}
}
}
}
///////////////////////////////////////////////////////////////////////////////////
Animation::Animation(CCSpriterX * _spr)
: mId(0)
, spr(_spr)
, mCurrKeyframe(0)
, mMainline(NULL)
, mDone(false)
, mTimer(0)
,event(0),afterAction("")
{
mTimelines.reserve(50);
}
Animation::~Animation()
{
int count = mTimelines.size();
for (int i=0;iToElement();
if (element)
{
if (element->QueryIntAttribute("id", &intValue) == TIXML_SUCCESS)
mId = intValue;
mName = element->Attribute("name");
if (element->QueryFloatAttribute("length", &floatValue) == TIXML_SUCCESS)
mLength = floatValue/1000.0f; // was in milliseconds, convert to seconds instead
const char *looping = element->Attribute("looping"); // was set to "false" in alpha, but in fact looping all the time
mLooping = true;
for (TiXmlNode* lineNode = node->FirstChild(); lineNode; lineNode = lineNode->NextSibling())
{
element = lineNode->ToElement();
const char *tabLine = element->Value();
if (strcmp(tabLine, "mainline")==0) // 1 mainline only
{
mMainline = new Timeline();
mMainline->Init(lineNode, animator);
}
else if (strcmp(tabLine, "timeline")==0)
{
Timeline *timeline = new Timeline();
timeline->Init(lineNode, animator);
mTimelines.push_back(timeline);
}
}
}
}
bool Animation::IsDone()
{
return mDone;
}
void Animation::Restart()
{
mDone = false;
mTimer = 0;
mCurrKeyframe = 0;
}
float lerp(float a, float b, float t){
return a+(b-a)*t;
}
void Animation::Update(float dt)
{
mTimer += dt;
if (mTimer >= mLength)
{
mDone = true;
Restart(); // always looping for now
}
int count = mMainline->GetKeyframeCount();
Key *keyframe = mMainline->GetKeyframe(mCurrKeyframe);
float currTime = keyframe->GetTime();
Key *keyframeNext = NULL;
int next = mCurrKeyframe+1;
if (next > count-1) // looping
next = 0;
keyframeNext = mMainline->GetKeyframe(next);
if (keyframeNext)
{
float nextTime = keyframeNext->GetTime();
if (next == 0)
nextTime = mLength;
if (mTimer >= nextTime)
{
mCurrKeyframe = next;
keyframe = keyframeNext;
currTime = keyframe->GetTime();
next = mCurrKeyframe+1;
if (next > count-1) // looping
next = 0;
keyframeNext = mMainline->GetKeyframe(next);
if (keyframeNext == NULL)
return;
nextTime = keyframeNext->GetTime();
if (next == 0)
nextTime = mLength;
}
float t = (mTimer-currTime)/(nextTime-currTime);
int count = keyframe->GetObjectRefCount();
for (int i=0;iGetObjectRef(i);
ObjectRef *refNext = keyframeNext->GetObjectRef(i);
if (ref && refNext)
{
Key *keyRef = mTimelines[ref->timeline]->GetKeyframe(ref->key);
Object *obj = keyRef->GetObject(0); // should be only 1 object
Key *keyRefNext = mTimelines[refNext->timeline]->GetKeyframe(refNext->key);
Object *objNext = keyRefNext->GetObject(0);
float x = lerp(obj->x, objNext->x, t);
float y = lerp(obj->y, objNext->y, t);
float scaleX = lerp(obj->scaleX, objNext->scaleX, t);
float scaleY = lerp(obj->scaleY, objNext->scaleY, t);
float angle = objNext->angle-obj->angle;
if (keyRef->IsSpinCounterClockwise())
{
if (angle < 0)
angle = (objNext->angle+360)-obj->angle;
}
else
{
if (angle > 0)
{
angle = (objNext->angle-360)-obj->angle;
}
}
if (ref->timeline != refNext->timeline)
t = 0;
angle = obj->angle+(angle)*t;
if (angle >= 360)
angle -= 360;
float px = obj->pivot_x+(objNext->pivot_x-obj->pivot_x)*t;
float py = obj->pivot_y+(objNext->pivot_y-obj->pivot_y)*t;
CCPoint newPos = ccp(x, y);
obj->sprite->setPosition(newPos);
obj->sprite->setRotation(-angle);
obj->sprite->setScaleX(scaleX);
obj->sprite->setScaleY(scaleY);
obj->sprite->setAnchorPoint(ccp(px, py));
}
}
}
}
void Animation::Render()
{
Key *keyframe = mMainline->GetKeyframe(mCurrKeyframe);
int count = keyframe->GetObjectRefCount();
for (int i=0;iGetObjectRef(i);
if (ref)
{
Key *keyRef = mTimelines[ref->timeline]->GetKeyframe(ref->key);
Object *obj = keyRef->GetObject(0); // should be only 1 object
obj->sprite->setColor(spr->getColorX());
obj->sprite->visit();
}
}
}
///////////////////////////////////////////////////////////////////////////////////
Entity::Entity(CCSpriterX * _spr)
: mCurrAnimation(0)
, mId(0)
, spr(_spr)
{
mAnimations.reserve(50);
};
Entity::~Entity()
{
int count = mAnimations.size();
for (int i=0;iUpdate(dt);
}
std::string Entity::CurrentAction()
{
return mAnimations[mCurrAnimation]->getName();
}
void Entity::StartWithEvent(const char * name, JEvent * event)
{
for(size_t i=0; igetName() == std::string(name)){
mCurrAnimation = i;
mAnimations[i]->setEvent(event);
mAnimations[i]->Restart();
}
}
}
void Entity::Start(const char * name, const char * _afterAction)
{
for(size_t i=0; igetName() == std::string(name)){
mCurrAnimation = i;
mAnimations[i]->setAfterAction(_afterAction);
mAnimations[i]->Restart();
}
}
}
void Entity::Render()
{
Animation *animation = mAnimations[mCurrAnimation];
animation->Render();
}
void Entity::NextAnimation()
{
mCurrAnimation++;
if (mCurrAnimation >= (int)mAnimations.size())
mCurrAnimation = 0;
Animation *animation = mAnimations[mCurrAnimation];
animation->Restart();
}
void Entity::SetId(int id)
{
mId = id;
}
void Entity::SetName(const char *name)
{
mName = name;
}
void Entity::AddAnimation(Animation *animation)
{
mAnimations.push_back(animation);
}
}
///////////////////////////////////////////////////////////////////////////////////
using namespace SCMLHelper;
CCSpriterX::CCSpriterX()
{
mFolders.reserve(50);
mEntities.reserve(50);
for(int i=0; itype = SCML;
animator->m_state = kLivingStateUngrabbed;
if (animator && animator->initWithFile(filename))
{
//由于是动画层,没有大小,这里设置大小,使之能够拾取
animator->setContentSize(CCSizeMake(100, 100));
animator->autorelease();
return animator;
}
CC_SAFE_DELETE(animator);
return NULL;
}
void CCSpriterX::update(float dt)
{
if (dt > 0.0167f)
dt = 0.0167f;
Entity *entity = mEntities[mCurrEntity];
entity->Update(dt);
}
void CCSpriterX::draw(void)
{
Entity *entity = mEntities[mCurrEntity];
entity->Render();
}
std::string CCSpriterX::CurrentAction()
{
Entity *entity = mEntities[mCurrEntity];
return entity->CurrentAction();
}
void CCSpriterX::PlayWithEvent(const char * name, JEvent * event)
{
Entity *entity = mEntities[mCurrEntity];
entity->StartWithEvent(name, event);
}
void CCSpriterX::Play(const char* name, const char * _afterAction)
{
Entity *entity = mEntities[mCurrEntity];
entity->Start(name, _afterAction == 0?"":_afterAction);
}
void CCSpriterX::PlayNext()
{
Entity *entity = mEntities[mCurrEntity];
entity->NextAnimation();
}
CCSprite *CCSpriterX::getSprite(int folderId, int fileId, int timelineId)
{
if (folderId < (int)mFolders.size())
{
Folder *folder = mFolders[folderId];
if (folder)
{
File *file = folder->GetFile(fileId);
if (file){
int id = this->fileSprites[timelineId].id;
if(id == -1){
for(int i=0; isprites[id] == 0){
file->sprites[id] = CCSprite::createWithSpriteFrameName(file->name.c_str());
file->sprites[id]->retain();
}
return file->sprites[id];
}
}
}
return NULL;
}
void CCSpriterX::setFlipX(bool bFlipX)
{
if(bFlipX != m_bFlipX){
float ex,ey,ez;
this->getCamera()->getEyeXYZ(&ex, &ey, &ez);
this->getCamera()->setEyeXYZ(ex, ey, bFlipX?-fabs(ez):fabs(ez));
this->m_bFlipX = bFlipX;
}
}
bool CCSpriterX::initWithFile(const char *filename)
{
char cfilename[256];
strcpy(cfilename, filename);
string name = strtok(cfilename, ".");
string suffix = strtok(cfilename, ".");
mCurrEntity = 0;
unsigned long filesize;
string path = workPath+gConfig->read("res")+"/"+filename;
char *buffer = (char *)CCFileUtils::sharedFileUtils()->getFileData(path.c_str(), "rb", &filesize);
if (buffer == NULL)
return false;
//加载大图
string sfplist = workPath+gConfig->read("res")+"/scml/"+name+".plist";
string sfpng = workPath+gConfig->read("res")+"/scml/"+name+".png";
CCSpriteFrameCache::sharedSpriteFrameCache()->addSpriteFramesWithFile(sfplist.c_str(), sfpng.c_str());
TiXmlDocument doc;
doc.Parse(buffer);
TiXmlNode *root = doc.FirstChild("spriter_data");
if (root)
{
TiXmlElement *element = root->ToElement();
const char *version = element->Attribute("scml_version");
const char *generator = element->Attribute("generator");
const char *generatorVersion = element->Attribute("generator_version");
for (TiXmlNode* entityNode = root->FirstChild(); entityNode; entityNode = entityNode->NextSibling())
{
element = entityNode->ToElement();
if (element)
{
const char *tab = element->Value();
if (strcmp(tab, "folder")==0)
{
Folder *folder = new Folder();
folder->Init(entityNode);
mFolders.push_back(folder);
}
else if (strcmp(tab, "entity")==0)
{
int intValue;
Entity *entity = new Entity(this);
if (element->QueryIntAttribute("id", &intValue) == TIXML_SUCCESS)
entity->SetId(intValue);
entity->SetName(element->Attribute("name"));
for (TiXmlNode* animationNode = entityNode->FirstChild(); animationNode; animationNode = animationNode->NextSibling())
{
Animation *animation = new Animation(this);
animation->Init(animationNode, this);
entity->AddAnimation(animation);
}
mEntities.push_back(entity);
}
}
}
}
CC_SAFE_DELETE_ARRAY(buffer);
this->scheduleUpdate();
return true;
}