Unity动态换装之Spine换装
注:转载请注明转载,并附原链接 http://www.cnblogs.com/liaoguipeng/p/5867510.html 燕双飞情侣
一、动态换装原理
- 换装,无非就是对模型的网格,或者贴图进行针对性置换;
- 对于3D局部换装,我们可能需要单独换模型和贴图,也可能只需要单独置换贴图即可
- 对与Spine2D角色换装,我们基本上只需要针对性置换贴图,也就是Slot插槽上对应的附着物Attachment即可
二、换装理论分析
- Spine目前提供的换装是整体换装,也就是动画那边做好几套Skin,需要哪套直接调用SKeletonAnimation中的InitialSkin进行置换就行了,这个看起来很简单嘛。
- 但是,如果我们需要局部换装,难道让动画把每个局部都单独列出来,比如我们一个角色10套
- 皮肤,每套皮肤有对于10个位置可以进行任意更换,那么动画岂不是要做10! = 3 628 800 套皮肤?计算机装得下?程序调用逻辑不会出错?这么看来,这个方案不科学。
- 那么,我们有没有一种方法,可以实现到局部换装?于是,开始针对Spine导出文件进行分析;Spine可到处二进制和Json数据文件,为了方便分析,我们这次使用Json文件进行分析:
Spine导出文件有Png,Json和Altas,Png只是静态贴图,我们暂且可以忽略;那么我们观察Altas,截取部分数据:
1 Lead.png 2 size: 2048 , 128 3 format: RGBA8888 4 filter: Linear,Linear 5 repeat: none 6 L_hand000 7 rotate: true 8 xy: 790 , 69 9 size: 57 , 87 10 orig: 57 , 87 11 offset: 0 , 0 12 index: - 1 13 L_leg000 14 rotate: true 15 xy: 1354 , 93 16 size: 33 , 91 17 orig: 33 , 91 18 offset: 0 , 0 19 index: - 1
并没有多大作用… 那么我们在看看另一个Json文件,截取部分信息:
套装1: clothing001部分插槽和贴图数据
1 "clothing001": { 2 // 此处省略一大堆动画数据和插槽数据,我们目前看套装1的武器 weapon_C部分 3 "hair_C" : { 4 "hair_C": { "name": "clothing001/hair001", "x": -14.38, "y": -11.92, "rotation": -93.18, "width": 100, "height": 78 } 5 }, 6 "shield_C": { 7 "shield_C" : { "name": "clothing001/shield001", "x": 20.78, "y": -0.75, "rotation": -11.65, "width": 62, "height": 77 } 8 }, 9 "weapon_C": { 10 "weapon_C" : { "name": "clothing001/weapon001", "x": 50.69, "y": -1.75, "rotation": -153.42, "width": 142, "height": 87 } 11 } 12 },
套装2:clothing002部分插槽和贴图数据
1 "clothing002": { 2 "hair_C" : { 3 "hair_C": { "name": "cloting002/hair002", "x": 15.26, "y": -9.01, "rotation": -93.18, "width": 84, "height": 63 } 4 }, 5 "shield_C": { 6 "shield_C" : { "name": "cloting002/shield002", "x": 20.78, "y": -0.75, "rotation": 92.7, "width": 80, "height": 84 } 7 }, 8 "weapon_C": { 9 "weapon_C" : { "name": "cloting002/weapon002", "x": 74.02, "y": 0.77, "rotation": -153.42, "width": 190, "height": 108 } 10 } 11 } 12 },
通过数据对比,我们可以发现套装数据结构一致,只是内部存储的数据不同,那么我们尝试将clothing001套装和clothing002套装内的其中一部分数据进行对调,比如我们对调“weapon_C”的所有数据,把文件导入Unity会发生什么呢?
对调前:
“cloth001” “cloth002”
对调后:
“cloth001” “cloth002”
预料之中,那么局部换装的实现,就应该不成问题了,那么我们回到代码层开始分析.
三、Spine底层库源代码分析
- 我们先观察Spine提供的整套换装源代码:
1 namespace Spine.Unity { 2 [ExecuteInEditMode, RequireComponent( typeof (CanvasRenderer), typeof (RectTransform)), DisallowMultipleComponent] 3 [AddComponentMenu( " Spine/SkeletonGraphic (Unity UI Canvas) " )] 4 public class SkeletonGraphic : MaskableGraphic, ISkeletonComponent, IAnimationStateComponent, ISkeletonAnimation { 5 6 #region Inspector 7 public SkeletonDataAsset skeletonDataAsset; 8 public SkeletonDataAsset SkeletonDataAsset { get { return skeletonDataAsset; } } 9 10 [SpineSkin(dataField: " skeletonDataAsset " )] 11 public string initialSkinName = " default " ; 12 13 [SpineAnimation(dataField: " skeletonDataAsset " )] 14 public string startingAnimation; 15 public bool startingLoop; 16 public float timeScale = 1f; 17 public bool freeze;
根据initialSkinName我们继续往下查找
1 // Set the initial Skin and Animation 2 if ( ! string .IsNullOrEmpty(initialSkinName)) 3 skeleton.SetSkin(initialSkinName); 4
1 ///Sets a skin by name (see SetSkin). 2 public void SetSkin (String skinName) { 3 Skin skin = data.FindSkin(skinName); 4 if (skin == null ) throw new ArgumentException( " Skin not found: " + skinName, " skinName " ); 5 SetSkin(skin); 6 }
1 ///Sets the skin used to look up attachments before looking in the {@link SkeletonData#getDefaultSkin() default 2 /// skin}. Attachmentsfrom the new skin are attached if the corresponding attachment from the old skin was attached. If 3 /// there was no old skin, each slot's setup mode attachment is attached from the new skin. 4 /// May be null. 5 public void SetSkin (Skin newSkin) { 6 if (newSkin != null ) { 7 if (skin != null ) 8 newSkin.AttachAll( this , skin); 9 else { 10 ExposedList < Slot > slots = this .slots; 11 for ( int i = 0 , n = slots.Count; i < n; i ++ ) { 12 Slot slot = slots.Items[i]; 13 String name = slot.data.attachmentName; 14 if (name != null ) { 15 Attachment attachment = newSkin.GetAttachment(i, name); 16 if (attachment != null ) slot.Attachment = attachment; 17 } 18 } 19 } 20 } 21 skin = newSkin; 22 }
就是这个函数,SetSkin(Skin newSkin),通过传入Skin套装数据,遍历所有对应的骨骼,更新骨骼上对应的贴图数据。 - 那么,思路就来了,我们Spine进行局部换装,不也就是更新某个SKin中某个Slot上对应的Attachment数据么?
因此,想想现实世界中的拼图游戏,假设我们有N块整体拼图(数据库Skin),整体拼图中对于的每一块小拼图碎片(Attachment),都能在其他N-1块整体配图(数据库Skin)找到唯一一块相同规格(相同的Slot)但是画面不同的拼图碎片(Attachment),我们需要一个动态的拼图底板(动态Skin来容纳我们的拼图碎片(Attachment),而要想拼一块完整的拼图,我们只需要从N块整体拼图(数据库Skin)中,任意取对应规格(Slot)的配图碎片(Attachment)组装到动态的拼图底板(动态Skin)中,全部Slot组装完成以后,我们就可以得到一幅完整又与其他表现不同的拼图。
- 分析得出,我们要求动画提供一套动态贴图DynamicSkin(作为新手初始套装),并且保证同一个角色的所有套装贴图卡槽保持一致,然后需要换装的贴图,做成多套完整的整套Skin贴图当作数据使用。
四、修改源代码,实现Spine局部换装目的
(1) 数据处理
1.1 全局枚举Slot数据
- 首先,我们得有套装数据,我们才可以进行数据获取,那么你可以使用数据库或者配置文件进行获取,此处暂时以临时文件替代:
全局枚举数据方便调用和传值,待会我们可以手动做数据映射:
1
public
enum
ESkin
2
{
3
[Description(
"
不存在此套装,错误反馈专用
"
)]
4
Null
=
100
,
5
[Description(
"
动态组装套装基础
"
)]
6
Dynamic
=
101
,
7
[Description(
"
可选套装1
"
)]
8
Clothing001
=
102
,
9
[Description(
"
可选套装2
"
)]
10
Clothing002
=
103
,
11
};
12
13
public
enum
ESlot
14
{
15
Null
=
200
,
16
Blet
=
201
,
//
腰带
//
17
Weapon
=
202
,
//
武器
//
18
BodyArmour
=
203
,
//
盔甲
//
19
Hair
=
204
,
//
头发
//
20
LeftHand
=
205
,
//
左手
//
21
LeftPauldron
=
206
,
//
左护肩
//
22
Leftleg
=
207
,
//
左腿
//
23
LeftShoes
=
208
,
//
左鞋
//
24
Shield
=
209
,
//
护盾
//
25
RightHand
=
210
,
//
右手
//
26
RightPauldron
=
211
,
//
右护肩
//
27
Rightleg
=
212
,
//
右腿
//
28
RightShoes
=
213
,
//
右鞋子
//
29
}
1.2 Skin_Attactment 字典数据
然后我们针对角色有一个套装更改类 SetSkin ,如果后期拓展多个角色,你完全可以手动抽象出父类和共性的操作方法,此处以一个进行展示:
1
///
2
///
初始化贴图套装数据
3
///
4
private
void
InitSkinData( )
5
{
6
_skinContainAttachments.Add(ESkin.Clothing001 ,
new
List
<
string
>
()
7
{
8
"
clothing001/belt001
"
,
9
"
clothing001/body001
"
,
10
"
clothing001/hair001
"
,
11
"
clothing001/L_hand001
"
,
12
"
clothing001/shield001
"
,
13
"
clothing001/L_pauldron001
"
,
14
"
clothing001/R_hand001
"
,
15
"
clothing001/weapon001
"
,
16
"
clothing001/R_pauldron001
"
,
17
"
clothing001/R_leg001
"
,
18
"
clothing001/L_leg001
"
,
19
"
clothing001/L_shoes001
"
,
20
"
clothing001/R_shoes001
"
,
21
});
22
_skinContainAttachments.Add(ESkin.Clothing002 ,
new
List
<
string
>
()
23
{
24
"
cloting002/body002
"
,
25
"
cloting002/hair002
"
,
26
"
cloting002/L_hand002
"
,
27
"
cloting002/shield002
"
,
28
"
cloting002/L_pauldron002
"
,
29
"
cloting002/R_hand002
"
,
30
"
cloting002/weapon002
"
,
31
"
cloting002/R_pauldron002
"
,
32
"
cloting002/L_leg002
"
,
33
"
cloting002/L_shoes002
"
,
34
"
cloting002/R_leg002
"
,
35
"
cloting002/R_shoes002
"
,
36
});
37
}
38
1.3 角色数据
也就是需要更换的部位:通常我们可能从背包中取出某部分装备,然后让玩家点击就进行更换
针对数据方便处理,我们需要给对应的某个装备进行标示,其中包括表现层中的数据:该装备属于哪套贴图Skin,属于哪个卡槽Slot;还有数据层的数据:该装备对玩家某些属性产生什么影响,此处我们只预留接口,不进行讨论;
代码如下:
1
using
UnityEngine;
2
using
UnityEngine.UI;
3
4
namespace
Assets.Game.Animation
5
{
6
///
7
///
带按钮功能的皮肤部位映射
8
///
9
public
class
SkinInfo : MonoBehaviour
10
{
11
[SerializeField, Header(
"
皮肤套装
"
)]
12
public
ESkin ESkin;
13
[SerializeField, Header(
"
插槽
"
)]
14
public
ESlot Slot;
15
16
public
SetSkin SkinTarget;
17
18
#region
属性字段
19
//
TODO 套装属性,进行哪些更改
20
#endregion
21
public
SkinInfo( ESkin eSkin , ESlot slot )
22
{
23
ESkin
=
eSkin;
24
Slot
=
slot;
25
}
26
27
public
Button SkinButton;
28
29
///
30
///
更新装备数据
31
///
32
private
void
InvokeDataRefresh()
33
{
34
}
35
36
public
void
Awake( )
37
{
38
SkinTarget
=
GameObject.Find(
"
LeadSpineAnim
"
).GetComponent
<
SetSkin
>
();
39
SkinButton
=
GetComponent
<
Button
>
();
40
41
SkinButton.onClick.AddListener(()
=>
42
{
43
var clickSuccessed
=
SkinTarget.ReceiveClick(SkinButton, ESkin , Slot);
44
if
(clickSuccessed)
//
回调函数处理,进行属性数据更新
45
{
46
InvokeDataRefresh();
47
}
48
});
49
}
50
51
52
}
53
}
(2)数据映射
得到数据以后,我们需要把数据跟枚举进行相互映射,方便我们调用
2.1 插槽数据
1
///
2
///
根据插槽枚举映射对应插槽名称
3
///
4
public
string
MappingESlot2Name( ESlot eSlot )
5
{
6
//
TODO 映射,通过附着物名称找对应插槽
7
switch
( eSlot )
8
{
9
case
ESlot.Blet:
10
return
"
blet_C
"
;
11
case
ESlot.Weapon:
12
return
"
weapon_C
"
;
13
case
ESlot.BodyArmour:
14
return
"
body_C
"
;
15
case
ESlot.Hair:
16
return
"
hair_C
"
;
17
case
ESlot.Shield:
18
return
"
shield_C
"
;
19
case
ESlot.LeftHand:
20
return
"
L_hand_C
"
;
21
case
ESlot.Leftleg:
22
return
"
L_leg_C
"
;
23
case
ESlot.LeftShoes:
24
return
"
L_shoes_C
"
;
25
case
ESlot.LeftPauldron:
26
return
"
L_pauldron_C
"
;
27
case
ESlot.RightHand:
28
return
"
R_hand_C
"
;
29
case
ESlot.RightPauldron:
30
return
"
R_pauldron_C
"
;
31
case
ESlot.Rightleg:
32
return
"
R_leg_C
"
;
33
case
ESlot.RightShoes:
34
return
"
R_shoes_C
"
;
35
default
:
36
throw
new
ArgumentOutOfRangeException(
"
attachment
"
, eSlot ,
"
换装目标不存在
"
);
37
}
38
}
1
///
2
///
根据插槽名称映射插槽枚举,与MappingESlot2Name( ESlot eSlot )互逆
3
///
4
///
5
///
6
public
ESlot MappingName2ESlot(
string
slotName )
7
{
8
//
TODO 映射,通过插槽找对应附着物类型
9
if
( slotName
==
"
blet_C
"
)
return
ESlot.Blet;
10
if
( slotName
==
"
weapon_C
"
)
return
ESlot.Weapon;
11
if
( slotName
==
"
body_C
"
)
return
ESlot.BodyArmour;
12
if
( slotName
==
"
hair_C
"
)
return
ESlot.Hair;
13
if
( slotName
==
"
shield_C
"
)
return
ESlot.Shield;
14
if
( slotName
==
"
L_hand_C
"
)
return
ESlot.LeftHand;
15
if
( slotName
==
"
L_leg_C
"
)
return
ESlot.Leftleg;
16
if
( slotName
==
"
L_shoes_C
"
)
return
ESlot.LeftShoes;
17
if
( slotName
==
"
L_pauldron_C
"
)
return
ESlot.LeftPauldron;
18
if
( slotName
==
"
R_hand_C
"
)
return
ESlot.RightHand;
19
if
( slotName
==
"
R_pauldron_C
"
)
return
ESlot.RightPauldron;
20
if
( slotName
==
"
R_leg_C
"
)
return
ESlot.Rightleg;
21
if
( slotName
==
"
L_pauldron_C
"
)
return
ESlot.LeftPauldron;
22
if
( slotName
==
"
R_shoes_C
"
)
return
ESlot.RightShoes;
23
return
ESlot.Null;
24
}
2.2 Skin贴图数据映射
1
///
2
///
根据套装贴图枚举映射贴图名称
3
///
4
private
string
MappingEskin2Name( ESkin eSkin )
5
{
6
switch
( eSkin )
7
{
8
case
ESkin.Clothing001:
9
return
"
clothing001
"
;
10
case
ESkin.Clothing002:
11
return
"
clothing002
"
;
12
default
:
13
throw
new
ArgumentOutOfRangeException(
"
eSkin
"
, eSkin ,
"
The Skin Cannot Found in Character's Spine
"
);
14
}
15
}
16
17
///
18
///
通过附着物名称查找对应的套装,对_skinContainAttachments进行遍历取Key
19
///
20
///
插槽对应的附着物名称
21
private
ESkin GetSkinByAttachment(
string
attachmentName )
22
{
23
if
(
!
_skinContainAttachments.Any(skins
=>
skins.Value.Contains(attachmentName)) )
return
ESkin.Null;
24
var eSkins
=
_skinContainAttachments.SingleOrDefault(skin
=>
skin.Value.Contains(attachmentName));
25
return
eSkins.Key;
26
}
(3) 换装代码
3.1 换装顺序:
我们需要进行的操作是:
① 等待Spine官方源码加载套装(此套装必须设置为新手动态套装,在此基础上我们再进行动态组合)
② 读取新手套装中所有插槽数据和附着物数据,缓存到字典
③ 反序列化从数据库/系统存储/文本数据存储中得到的实际套装数据
④ 缓存数据和新手套装数据进行差异化对比,对有差别部分,以缓存数据为准,进行局部换图
⑤ 换图操作后,需要对缓存数据表进行及时更新
⑥ 切记,一切操作需要在官方Spine加载完成后,在Start函数中进行更新,否则会异常报空
1
///
2
///
数据层初始化
3
///
4
public
void
Awake( )
5
{
6
InitSkinData();
7
8
}
9
10
///
11
///
行为表现层操作
12
///
13
public
void
Start( )
14
{
15
_skeletonAnimation
=
GetComponent
<
SkeletonAnimation
>
();
16
_skeleton
=
_skeletonAnimation.skeleton;
17
18
//
TODO 测试数据
19
//
PlayerPrefsDataHlpers.SetBool(_playerSkinInitFlag , false);
//
注释部分用于清理套装缓存数据
20
21
//
_isPlayerSkinInit = PlayerPrefsDataHlpers.GetBool(_playerSkinInitFlag);
22
//
if ( !_isPlayerSkinInit )
23
InitSkinDataAtStart();
24
//
PlayerPrefsDataHlpers.SetBool(_playerSkinInitFlag , true);
25
ReloadSkinByDataAtGameStart();
26
27
}
3.2 数据定义和数据表缓存校验
1
#region
Spine Animation Script
2
private
SkeletonAnimation _skeletonAnimation;
3
4
private
Skeleton _skeleton;
5
#endregion
6
7
#region
User Data
8
private
readonly
Dictionary
<
ESkin , List
<
string
>>
_skinContainAttachments
=
new
Dictionary
<
ESkin , List
<
string
>>
();
//
通过附着物映射skin
9
10
private
readonly
Dictionary
<
ESlot ,
string
>
_dynamicSlotToAttachments
=
new
Dictionary
<
ESlot ,
string
>
();
//
实际使用中的数据
11
12
private
readonly
string
_dynamicSkinName
=
"
clothing000
"
;
13
#endregion
14
15
16
#region
数据表缓存校验
17
18
private
void
SetSlotToAttachment( ESlot eAttach ,
string
attchmentName,
bool
saveDatabase )
19
{
20
bool
isExitKey
=
_dynamicSlotToAttachments.ContainsKey(eAttach);
21
if
(
!
isExitKey )
22
{
23
_dynamicSlotToAttachments.Add(eAttach , attchmentName);
24
}
25
else
26
{
27
_dynamicSlotToAttachments[eAttach]
=
attchmentName;
//
Reference Type Don't need to Reassignment Value
28
}
29
30
//
是否写入数据表
//
31
if
(saveDatabase)
32
{
33
EncodingAttachment(eAttach , attchmentName);
34
}
35
}
36
37
///
38
///
编码写入缓存(也可另改写为服务器存储)
39
///
40
///
对应插槽
41
///
对于贴图名称
42
private
void
EncodingAttachment( ESlot eAttach ,
string
attchmentName )
43
{
44
int
id
=
(
int
) eAttach;
45
string
flag
=
string
.Concat(
"
slot
"
, id.ToString());
46
PlayerPrefsDataHlpers.SetString(flag , attchmentName);
47
}
48
49
///
50
///
解码取出缓存套装数据
51
///
52
///
53
private
Dictionary
<
ESlot ,
string
>
DecodingAttachment( )
54
{
55
var slotToAttachments
=
new
Dictionary
<
ESlot ,
string
>
();
56
57
var fristSlot
=
(ESlot) Enum.Parse(
typeof
(ESlot) ,
"
Null
"
,
true
);
58
int
start
=
(
int
) fristSlot;
59
int
length
=
Enum.GetNames(
typeof
(ESlot)).Length;
60
int
end
=
start
+
length;
61
for
(
int
i
=
start ; i
<
end ; i
++
)
62
{
63
string
flag
=
string
.Concat(
"
slot
"
, i.ToString());
64
string
attchmentName
=
PlayerPrefsDataHlpers.GetString(flag);
65
if
( attchmentName
!=
string
.Empty )
66
{
67
ESlot eSlot
=
(ESlot) i;
68
slotToAttachments.Add(eSlot , attchmentName);
69
}
70
}
71
72
return
slotToAttachments;
73
}
74
75
#endregion
3.3 读取默认套装数据,作为动态套装的基础对比数据表
1
private
bool
_isPlayerSkinInit;
//
开关: 用于测试数据缓存
//
2
3
///
4
///
TODO : 完善局部换装逻辑
5
///
1、初次游戏,记录基准 Slot -> Attachment Table
6
///
2、任何时刻实时更改套装任何部分,立刻更新映射表数据层
7
///
3、再次游戏,基于基准装,重新根据数据表缓存数据映射表现层
8
///
4、双重数据表校验,只要基准表和实际表任何部分不一致,认定装备需要Reloading
9
///
10
public
void
InitSkinDataAtStart( )
11
{
12
//
默认设置必须为基准装 Clothing000
//
13
_skeletonAnimation.initialSkinName
=
_dynamicSkinName;
14
15
//
var curSkin = _skeleton.Skin;
16
17
ExposedList
<
Slot
>
slots
=
_skeleton.slots;
18
for
(
int
i
=
0
, n
=
slots.Count ; i
<
n ; i
++
)
19
{
20
Slot slot
=
slots.Items[i];
21
String slotName
=
slot.data.attachmentName;
22
if
( slotName
!=
null
)
23
{
24
ESlot eSlot
=
MappingName2ESlot(slotName);
25
Attachment attachment
=
LGetAttachment(i , slotName , _dynamicSkinName);
//
Find Attachment By Slot With Base Skin
26
if
( attachment
==
null
)
continue
;
27
28
string
attahName
=
attachment.Name;
29
30
//
是否写入数据表
31
SetSlotToAttachment(eSlot , attahName,
false
);
32
33
}
34
}
35
}
3.4 读取基础数据表以后,读取缓存数据,对比数据进行局部换装
1
2
///
3
///
在基础套装自动加载完成以后,手动调用此函数
4
///
为了局部换装数据不错乱,哪怕整套Skin换都需要更新数据表中的数据
5
///
6
public
void
ReloadSkinByDataAtGameStart( )
7
{
8
var slotToAttachments
=
DecodingAttachment();
9
CompareAndSetAttachments(slotToAttachments);
10
}
数据对比函数:
1
///
2
///
对比数据表跟目前数据表(游戏初始加载后的Spine内置套装数据)差异,并更新数据和表现
3
///
4
///
缓存数据表(目标数据)
5
private
void
CompareAndSetAttachments(Dictionary
<
ESlot,
string
>
targetAttchments)
6
{
7
var curAttachments
=
_dynamicSlotToAttachments;
8
9
var fristSlot
=
(ESlot) Enum.Parse(
typeof
(ESlot) ,
"
Null
"
,
true
);
10
int
start
=
(
int
) fristSlot;
11
12
foreach
(var eSlotKey
in
targetAttchments )
13
{
14
ESlot slotKey
=
eSlotKey.Key;
15
var curAttachment
=
curAttachments[slotKey];
16
var targetAttachment
=
targetAttchments[slotKey];
17
18
if
( curAttachment
==
null
||
curAttachment
!=
targetAttachment )
19
{
20
ESkin eSkins
=
GetSkinByAttachment(targetAttachment);
21
if
( eSkins
==
ESkin.Null )
22
{
23
throw
new
Exception(
"
Eskin 不存在与=数据表_skinContainAttachments中
"
);
24
}
25
LChangeSkinBaseOnDynamicSkin(eSkins , slotKey);
26
}
27
}
28
}
29
3.4 核心换装代码
换装函数对外入口点:
1
///
2
///
基于动态套装,改变局部并重新组合动态套装
3
///
4
///
取值套装
5
///
目标插槽
6
public
bool
LChangeSkinBaseOnDynamicSkin( ESkin eTargetSkin , ESlot eSlot )
7
{
8
Skin dynamicSkin
=
_skeleton.data.FindSkin(_dynamicSkinName);
9
10
var success
=
LSetSkin(dynamicSkin , eTargetSkin , eSlot);
11
return
success;
12
}
批量换装操作:
1
///
2
///
批量换装,必须保证传入的数组一一对应
3
///
4
///
批量换装只要有其中一处换不成功,整体算作失败,需要手动进行数据回滚
5
public
bool
LChangeBitchSkinBaseOnDynamicSkin(ESkin[] eTargetSkins, ESlot[] eSlots)
6
{
7
if
(eTargetSkins.Length
!=
eSlots.Length)
return
false
;
8
for
(
int
i
=
0
; i
<
eSlots.Length; i
++
)
9
{
10
var success
=
LChangeSkinBaseOnDynamicSkin(eTargetSkins[i],eSlots[i]);
11
if
(
!
success)
12
{
13
return
false
;
//
任意一件换不成功,整体换装失败
14
}
15
}
16
return
true
;
17
}
内部换装处理函数:
1
///
2
///
内部处理:针对传入的需要更改的套装(实时套装),从目标皮肤中根据目标卡槽取出皮肤数据进行替换赋值操作,
3
///
数据层和表现层同时处理变化
4
///
5
///
赋值套装
6
///
取值套装枚举
7
///
目标插槽枚举
8
private
bool
LSetSkin( Skin dynamicSkin , ESkin eSkin , ESlot eSlot )
9
{
10
11
if
( dynamicSkin
!=
null
)
12
{
13
ExposedList
<
Slot
>
slots
=
_skeleton.slots;
14
for
(
int
i
=
0
, n
=
slots.Count ; i
<
n ; i
++
)
15
{
16
//
Get
//
17
Slot slot
=
slots.Items[i];
18
var targetSlotName
=
MappingESlot2Name(eSlot);
19
if
( slot.data.name
!=
targetSlotName )
continue
;
20
21
string
attachName
=
slot.data.attachmentName;
22
if
( attachName
!=
null
)
23
{
24
string
targetSkinName
=
MappingEskin2Name(eSkin);
25
Attachment attachment;
26
if
( attachName
==
targetSlotName )
27
{
28
attachment
=
LGetAttachment(i , targetSlotName , targetSkinName);
//
重写L Get
29
dynamicSkin.Attachments.Remove(
new
Skin.AttachmentKeyTuple(i , targetSlotName));
30
dynamicSkin.Attachments.Add(
new
Skin.AttachmentKeyTuple(i , targetSlotName) , attachment);
31
32
}
33
else
34
{
35
attachment
=
dynamicSkin.GetAttachment(i , attachName);
//
默认Skeleton Get
36
}
37
38
//
Set
//
39
if
( attachment
!=
null
)
40
{
41
slot.Attachment
=
attachment;
42
var attahName
=
attachment.Name;
43
SetSlotToAttachment(eSlot , attahName,
true
);
44
break
;
45
}
46
}
47
}
48
_skeleton.slots
=
slots;
49
}
50
_skeleton.skin
=
dynamicSkin;
51
return
true
;
52
}
针对性查找真实贴图中的附着点数据(是数据不是String哦,有效的完整的Attachment数据)
1
///
2
///
通过指定的Skin找到对应附着点的附着物Attachment
3
///
4
public
Attachment LGetAttachment(
int
slotIndex ,
string
slotName ,
string
skinName )
5
{
6
var targetSkin
=
_skeleton.data.FindSkin(skinName);
7
var attachments
=
targetSkin.Attachments;
8
9
Attachment attachment;
10
attachments.TryGetValue(
new
Skin.AttachmentKeyTuple(slotIndex , slotName) ,
out
attachment);
11
return
attachment;
12
}
(4) 用户交互部分:
1
#region
接受用户点击处理
2
///
3
///
可交互对象内部绑定了对应的SkinInfo,根据SkinInfo赋值字段进行查找
4
///
5
///
换装按键
6
///
对应皮肤
7
///
对应插槽
8
public
bool
ReceiveClick( Button skinButton , ESkin eSkin , ESlot slot )
9
{
10
return
ResetActulSlotToAttachment(skinButton , eSkin , slot);
11
}
四、结果展示
最终测试结果,实现了动态换装,批量换装,局部换装,整体换装,动画运行时换装,换装数据缓存等,如图所示:
五、结束感言
如果你喜欢本篇博课,或者想和我探讨,请关注我的博客园(燕双飞情侣),感谢您的观看。
注:转载请注明转载,并附原链接 http://www.cnblogs.com/liaoguipeng/p/5867510.html 燕双飞情侣
posted @
2016-09-13 10:25 燕双飞情侣 阅读(
...) 评论(
...) 编辑 收藏