spine在cocos2d-x中进行局部换肤

最近发现越来越多的游戏热衷用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

spine在cocos2d-x中进行局部换肤的相关文章

Android学习之 换肤功能模块的实现&lt;二&gt;

在上篇中 主要有学习到皮肤资源内置到应用程序中 的方式实现换肤的 基本思路,本篇将继续以上篇的思路学习 皮肤资源内置的方式实现换肤效果.但本篇侧重于应用中换肤功能的代码设计实现上!切换的皮肤资源位于assets下不同的皮肤资源文件夹中. 本篇demo程序的代码结构如下: 本篇实现换肤功能的代码设计 UML类图如下: 本篇demo的换肤效果如下:               主要的实现代码在于: 1.SkinConfigManager.java 作用:皮肤配置管理,封装了SharedPrefere

Android学习之 换肤功能模块的实现&amp;lt;二&amp;gt;

在上篇中 主要有学习到皮肤资源内置到应用程序中 的方式实现换肤的 基本思路,本篇将继续以上篇的思路学习 皮肤资源内置的方式实现换肤效果.但本篇側重于应用中换肤功能的代码设计实现上.切换的皮肤资源位于assets下不同的皮肤资源目录中. 本篇demo程序的代码结构例如以下: watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvamFuaWNlMDUyOQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissol

换肤框架

序言 现在说是换肤框架还有点夸大其词,因为目前只实现了颜色的替换,目前网上已有的换肤框架我都研究过,主要感觉给每个View设置样式,还要保存每个需要换肤的View,实在是太繁琐,而且目前我的项目中不需要皮肤功能,开发这个框架也仅仅是为了实现夜间模式,而又不用过多的改造原有的代码,比如给每个颜色替换成引用等等.从目前实现的效果来看,基本能达到简单方便的目的,而且也能实现WebView的换肤,且不会重启Activity,还有过渡动画,我相信这个框架已经能满足大多数的项目了. 效果 功能 1.支持通过

Qt编写安防视频监控系统11-动态换肤

一.前言 Qt中的动态换肤技术是非常一流的,直接调用qApp->setStyleSheet(qss);就可以对整个应用程序进行换肤,如果样式表内容不多,或者对应的贴图不对,效率还是蛮好的,不过据说有些人写了几千行的样式表,加载起来能够明显感觉卡顿,毕竟此时全部刷新加载重绘,是非常耗CPU的,这种情况就需要避免尽量统一样式表,能合并的就合并,当然,在性能比较好的电脑上,这个基本上遇不到的,也感觉不到的,好比现在的手机相比于十年前的手机,现在动不动都6GB内存了,比起以前256MB内存的时候,不知道

DragonBones龙骨换装(局部换装)

参考: Egret官方换装动画 换装,主要是替换任意插槽的图片,来达到局部换装的目的. 游戏中可以只制作一个人物动画,然后局部替换100套衣服.100套发型或者100种眼睛.甚至可以把人和衣服所有插槽都换了. 下图替换了衣服.       一.创建一个测试骨骼动画 public armatureDisplay:dragonBones.EgretArmatureDisplay; public createBone(){ let factory = dragonBones.EgretFactory.

C# Winform中窗体的美化—— 用IrisSkin轻松实现换肤功能

IrisSkin2.dll是一款很不错的免费皮肤控件,利用它可以轻松的实现WinForm窗体换肤然而IrisSkin2.dll只能在.NET Faremwork 4.0以及之前的版本使用,所以要在VS2010中使用它需要将自己的项目框架设置为.NET Framework 4.0,还有另一种方法就是使用IrisSkin4.dll,他的使用方法和IrisSkin2.dll基本是完全一样的 一.首先在网上下载 IrisSkin4.dll (已与皮肤一起打包),将IrisSkin4.dll与Skins(

WPF换肤之三:WPF中的WndProc

原文:WPF换肤之三:WPF中的WndProc 在上篇文章中,我有提到过WndProc中可以处理所有经过窗体的事件,但是没有具体的来说怎么可以处理的. 其实,在WPF中,要想利用WndProc来处理所有的事件,需要利用到SourceInitialized  Event,首先需要创建一个HwndSource对象,然后利用其AddHook方法来将所有的windows消息附加到一个现有的事件中,这个就是WndProc. void WSInitialized(object sender, EventAr

.NET vs2010中使用IrisSkin2.dll轻松实现winForm窗体换肤功能

.NET vs2010中使用IrisSkin2.dll轻松实现winForm窗体换肤功能 转载▼ 大家好,从事c-s开发的C#程序员经常为winForm的界面设计苦恼,笔者曾经也深受“美工神话”的危害,如今提到美工,界面布局设计就开始蛋疼…. 所幸的是,笔者无意间接触到了一些比较可爱的第三方控件,可以为我们程序员省掉很多美工上面的麻烦…在陆续的博客中我会为大家介绍,今天我们的主题是:IrisSkin2.dll IrisSkin2.dll是一款很不错的免费皮肤控件,利用它可以轻松的实现winFor

SQL Server 中本地(局部)临时表

一个SSMS查询窗口就是一个会话 通过本地临时表验证: 本地临时表仅在当前会话中可见:全局临时表在所有会话中都可见.本地临时表的名称前面有一个编号符 (#table_name),而全局临时表的名称前面有两个编号符 (##table_name). 打开一个查询窗口,新建本地临时表,插入数据,并查询 create table #temptable (id int identity(1,1) primary key, name nvarchar(10) null); go insert into #t