最近发现越来越多的游戏热衷用spine来做动画,做出来的效果真是生动美妙啊。我也来研究一下。期间就遇到了两个问题,一个是局部换肤的问题,一个是碰撞的时候。我先来解决局部换肤的问题。
看了看cocos2d-x 提供的spine的demo,虽然没有局部换肤但是有整体换肤的功能,我觉得这就是解决问题的突破口。在cocos2d-x3.4中,换肤的函数是SkeletonRenderer 类中的这个函数:
bool setSkin (const std::string& skinName);
首先看看函数的实现,它是直接调用了另外一个函数:
bool SkeletonRenderer::setSkin (const std::string& skinName) { return spSkeleton_setSkinByName(_skeleton, skinName.c_str()) ? true : false; }
然后我再进入spSkeleton_setSkinByName函数里一探究竟。这个函数的实现是这样的:
int spSkeleton_setSkinByName (spSkeleton* self, const char* skinName) { spSkin *skin; if (!skinName) { spSkeleton_setSkin(self, 0); return 1; } skin = spSkeletonData_findSkin(self->data, skinName); if (!skin) return 0; spSkeleton_setSkin(self, skin); return 1; }
首先它判断皮肤名称是不是有效的,如果无效就设置皮肤为nullptr。如果有效就通过骨架数据根据皮肤名进行查找,看存不存在这个皮肤。如果存在就调用spSkeleton_setSkin函数,我们再进入这个函数看一看:
void spSkeleton_setSkin (spSkeleton* self, spSkin* newSkin) { if (newSkin) { if (self->skin) spSkin_attachAll(newSkin, self, self->skin); else { /* No previous skin, attach setup pose attachments. */ int i; for (i = 0; i < self->slotsCount; ++i) { spSlot* slot = self->slots[i]; if (slot->data->attachmentName) { spAttachment* attachment = spSkin_getAttachment(newSkin, i, slot->data->attachmentName); if (attachment) spSlot_setAttachment(slot, attachment); } } } } CONST_CAST(spSkin*, self->skin) = newSkin; }
我们来看看这个函数做了些什么事情。当当前皮肤不为nullptr的时候就调用spSkin_attachAll将新皮肤的所有附件替换到原皮肤上,否则(当前没有皮肤的时候)就遍历当前骨架的所有slot,将新皮肤的附件添加到骨架对应的slot上来。
写到这里我想我已经知道解决方法了。在遍历骨架的slot的时候,我可以在slot的数据中获取到附件的名称。在遍历的时候只有当这个名称为我想要替换掉的那个位置的名称时我才进行替换处理,这样就可以实现局部换肤了。
为此我新增了一个类继承自 spine::SkeletonAnimation类,在这个类里面增加了局部换肤的函数:
bool replacementParts(const std::string& skinName, const std::string& attachmentName);
下面给出完整代码:
#ifndef SPINENODE_H #define SPINENODE_H #include "spine/SkeletonAnimation.h" class SpineNode final : public spine::SkeletonAnimation { public: static SpineNode* createWithData(spSkeletonData* skeletonData); static SpineNode* createWithFile(const std::string& skeletonDataFile, spAtlas* atlas, float scale = 1); static SpineNode* createWithFile(const std::string& skeletonDataFile, const std::string& atlasFile, float scale = 1); public: /** * 新增函数 * 是否已经翻转x轴 */ bool isFlippedX() const; /** * 新增函数 * 设置x轴翻转 */ void setFlippedX(bool flippedX); /** * 新增函数 * 是否已经翻转y轴 */ bool isFlippedY() const; /** * 新增函数 * 设置y轴翻转 */ void setFlippedY(bool flippedY); /** * 新增函数 * 更换部件,将目标皮肤的的某个部件替换到当前皮肤上 * @ 参数 std::string 皮肤名称, std::string 附件名称 */ bool replacementParts(const std::string& skinName, const std::string& attachmentName); private: SpineNode(spSkeletonData* skeletonData); SpineNode(const std::string& skeletonDataFile, spAtlas* atlas, float scale = 1); SpineNode(const std::string& skeletonDataFile, const std::string& atlasFile, float scale = 1); private: cocos2d::CustomCommand _customCommand; }; #endif
#include "SpineNode.h" using namespace cocos2d; struct _Entry { int slotIndex; const char* name; spAttachment* attachment; _Entry* next; }; typedef struct { spSkin super; _Entry* entries; } _spSkin; SpineNode::SpineNode(spSkeletonData* skeletonData) : SkeletonAnimation(skeletonData) { } SpineNode::SpineNode(const std::string& skeletonDataFile, spAtlas* atlas, float scale) : SkeletonAnimation(skeletonDataFile, atlas, scale) { } SpineNode::SpineNode(const std::string& skeletonDataFile, const std::string& atlasFile, float scale) : SkeletonAnimation(skeletonDataFile, atlasFile, scale) { } SpineNode* SpineNode::createWithData(spSkeletonData* skeletonData) { SpineNode* node = new SpineNode(skeletonData); node->autorelease(); return node; } SpineNode* SpineNode::createWithFile(const std::string& skeletonDataFile, spAtlas* atlas, float scale) { SpineNode* node = new SpineNode(skeletonDataFile, atlas, scale); node->autorelease(); return node; } SpineNode* SpineNode::createWithFile(const std::string& skeletonDataFile, const std::string& atlasFile, float scale) { SpineNode* node = new SpineNode(skeletonDataFile, atlasFile, scale); node->autorelease(); return node; } bool SpineNode::isFlippedX() const { return _skeleton->flipX != 0; } void SpineNode::setFlippedX(bool flippedX) { _skeleton->flipX = flippedX ? 1 : 0; } bool SpineNode::isFlippedY() const { return _skeleton->flipY != 0; } void SpineNode::setFlippedY(bool flippedY) { _skeleton->flipY = flippedY ? 1 : 0; } bool SpineNode::replacementParts(const std::string& skinName, const std::string& attachmentName) { if (skinName.empty()) { return false; } spSkin *skin = spSkeletonData_findSkin(_skeleton->data, skinName.c_str()); if (!skin) return false; if (_skeleton->skin) { const _Entry *entry = reinterpret_cast<_spSkin *>(_skeleton->skin)->entries; while (entry) { spSlot *slot = _skeleton->slots[entry->slotIndex]; if (strcmp(slot->data->name, attachmentName.c_str()) == 0) { spAttachment *attachment = spSkin_getAttachment(skin, entry->slotIndex, entry->name); if (attachment) spSlot_setAttachment(slot, attachment); return true; } entry = entry->next; } } else { for (int i = 0; i < _skeleton->slotsCount; ++i) { spSlot* slot = _skeleton->slots[i]; if (strcmp(slot->data->name, attachmentName.c_str()) == 0) { spAttachment* attachment = spSkin_getAttachment(skin, i, slot->data->attachmentName); if (attachment) spSlot_setAttachment(slot, attachment); return true; } } } return false; }
这是测试效果图:
时间: 2024-10-06 08:35:12