本文原链接:http://www.cnblogs.com/zouzf/p/4450861.html
公司用的骨骼动画的版本貌似还停留在2.1之前的年代而已没有更新,该因各种历史原因吧,而有个大项目“一直”处于马上发布准备大推的阶段,没人敢动。恩,公司的骨骼动画貌似是用Flash做然后通过插件导出成 plist、png、xml格式的,现在,大项目负责人说骨骼动画卡,要优化,恩,交给我来做~~~
前期分析
通过耗时比较,90%的时间消耗在了 CCDataReaderHelper::addDataFromCache 这个方法里,其中90%的时间又花在 CCDataReaderHelper::decodeAnimation 这里。一看xml文件:3M多,25600多行,其中 animation部分有25000行。。。通过层层筛选分析,得出结论:xml文件过大导致xml树节点过多,最终导致遍历xml树构建 CCArmatureData、CCAnimationData、CCMovmentBoneData、CCFrameData等数据时消耗过大,其中遍历xml树时的查询也是比较耗时的一个环节。
方案选择
在涉及到文件的优化方式里,序列化一直是考虑首选。查看了 CCBoneData、CCFrameData的类之后,发现它们的数据成员除了 name 是string之外 其他的都是 int、float、bool,挺好的,比xml优化那时的满地字符串好多了,成员name可以用char[],大小固定64差不多了,就这样愉快地决定了用序列化来优化。
大概思路
CCArmatureData、CCAnimationData等几个类的关系是:CCArmatureData->CCBoneData->CCDisplayData; CCAnimationData->CCMovmentData->CCMovmentBoneData->CCFrameData; CCTextureData->CCContourData->CCContourVertex2,组合关系用了CCDictionary和CCArray。定义一系列struct和上面那十个类一一对应,如 arm_struct、ani_struct等,把类的数据都存到struct里,然后把struct直接写到文本;加载的时候就读文本,把数据写到struct里,然后根据struct构建出CCArmatureData等数据。
细节实现
CCArmatureData等类和struct之间转换时怎么实现呢?深度优先和广度优先二选一,由于CCArmatureData等类之间的关系是包含关系,就是一棵树,深度优先会更好一点。
骨骼数据转struct:(本来写了很多的,但又删了,还是代码说得清楚点)
1 struct CC_DLL skeleton_struct 2 { 3 char name[64]; 4 float version; //version 5 int childCount_arm; 6 int childCount_arm_b; 7 int childCount_arm_d; 8 int childCount_ani; 9 int childCount_ani_m; 10 int childCount_ani_b; 11 int childCount_ani_f; 12 int childCount_tex; 13 int childCount_tex_con; 14 int childCount_tex_vt; 15 16 //float frameRate; //no work yet? 17 }; 18 19 20 // CCArmatureData 21 struct CC_DLL Armature_struct 22 { 23 //有多个struct_Armature_b 24 char name[64]; 25 int child_count; 26 int child_index; 27 }; 28 29 30 // CCBoneData 31 struct CC_DLL Armature_b_struct 32 { 33 //有多个struct_Armature_b_d 34 35 // BaseData_struct baseData;//只用到了 只Order 属性 36 char name[64]; 37 char parentName[64]; 38 int child_count; 39 int child_index; 40 int zOrder; 41 }; 42 43 44 // CCDisplayData 45 struct CC_DLL Armature_b_d_struct 46 { 47 char name[64]; 48 int displayType; //displayType base on this gay 49 // int child_count; 50 // int child_index; 51 52 //float pX; //no useed in cocos2dx 53 //float pY; //no useed in cocos2dx 54 };
1 /** 2 * 深度遍历 CCArmatureData 3 * 4 * @return 返回 true 表示转换成功 5 */ 6 bool CCDataReaderHelper::ArmatureDataToStructData() 7 { 8 int child_index_arm = 0; 9 int child_index_arm_b = 0; 10 bool result = true; 11 12 CCDictionary* arm_datas = CCArmatureDataManager::sharedArmatureDataManager()->getArmarureDatas(); 13 CCDictElement* pArmElement; 14 CCDICT_FOREACH(arm_datas, pArmElement) 15 { 16 CCArmatureData* arm = (CCArmatureData*)pArmElement->getObject(); 17 18 /* save data from CCArmatureData object to Armature_struct */ 19 Armature_struct armStruct; 20 21 /* set child info */ 22 armStruct.child_count = arm->boneDataDic.count(); 23 armStruct.child_index = child_index_arm; 24 child_index_arm += armStruct.child_count; 25 26 /* length of name is beyond 63 */ 27 if (isNameIllegal(arm->name)) 28 { 29 result = false; 30 strncpy(armStruct.name, arm->name.c_str(), 63); 31 } 32 else 33 { 34 strncpy(armStruct.name, arm->name.c_str(), arm->name.length() + 1); 35 } 36 37 wydArmLst.push_back(armStruct); 38 39 40 /* ergodic CCBoneData in one CCArmatureData */ 41 CCDictElement* pArmBElement; 42 CCDictionary* arm_b_dic = &(arm->boneDataDic); 43 CCDICT_FOREACH(arm_b_dic, pArmBElement) 44 { 45 CCBoneData* bone = (CCBoneData*)pArmBElement->getObject(); 46 47 /* save data from CCBoneData object to Armature_b_struct */ 48 Armature_b_struct boneStruct; 49 50 boneStruct.zOrder = bone->zOrder; 51 // strcpy(boneStruct.name, bone->name.c_str()); 52 // boneStruct.skewX = bone->skewX; 53 // boneStruct.skewY = bone->skewY; 54 // boneStruct.tweenRotate = bone->tweenRotate; 55 56 boneStruct.child_count = bone->displayDataList.count(); 57 boneStruct.child_index = child_index_arm_b; 58 child_index_arm_b += boneStruct.child_count; 59 60 if (isNameIllegal(bone->name)) 61 { 62 result = false; 63 strncpy(boneStruct.name, bone->name.c_str(), 63); 64 } 65 else 66 { 67 strncpy(boneStruct.name, bone->name.c_str(), bone->name.length() + 1); 68 } 69 70 if (isNameIllegal(bone->parentName)) 71 { 72 result = false; 73 strncpy(boneStruct.parentName, bone->parentName.c_str(), 63); 74 } 75 else 76 { 77 strncpy(boneStruct.parentName, bone->parentName.c_str(), bone->parentName.length() + 1); 78 } 79 80 wydArm_bLst.push_back(boneStruct); 81 82 83 /* ergodic CCDisplayData in one CCBoneData */ 84 CCArray* displayArr = &(bone->displayDataList); 85 CCObject* objD; 86 CCARRAY_FOREACH(displayArr, objD) 87 { 88 CCDisplayData* display = (CCDisplayData*) objD; 89 90 /* save data from CCDisplayData object to Armature_b_d_struct */ 91 Armature_b_d_struct displayStruct; 92 displayStruct.displayType = display->displayType;//zou 93 std::string displayName; 94 95 if (display->displayType == CS_DISPLAY_SPRITE) 96 { 97 displayName = ((CCSpriteDisplayData *)display)->displayName; 98 } 99 else 100 { 101 displayName = ((CCArmatureDisplayData *)display)->displayName; 102 } 103 104 if (isNameIllegal(displayName)) 105 { 106 result = false; 107 strncpy(displayStruct.name, displayName.c_str(), 63); 108 } 109 else 110 { 111 strncpy(displayStruct.name, displayName.c_str(), displayName.length() + 1); 112 } 113 114 115 wydArm_dLst.push_back(displayStruct);// for write to file 116 117 } 118 } 119 120 } 121 122 wydSkeleton.childCount_arm = arm_datas->count(); 123 wydSkeleton.childCount_arm_b = child_index_arm; 124 wydSkeleton.childCount_arm_d = child_index_arm_b; 125 126 return result; 127 }
里面的struct用到 child_index和child_count,这个东西用于 struct转armature时控制armature孩子的位置和数量的。上面代码的大概意思就是:遍历CCArmatureData、CCBoneData、CCDisplayData类,一一创建结构体 arm_struct、arm_b_struct、arm_b_d_struct,每次循环都对应创建一个struct然后加到对应的list列表里。
struct转骨骼数据:
1 void CCDataReaderHelper::decodeArmatureStructData() 2 { 3 for (int i = 0; i < wydSkeleton.childCount_arm; i++) 4 { 5 6 CCArmatureData* arm = CCArmatureData::create(); 7 Armature_struct armStruct = wydArms[i]; 8 arm->name = armStruct.name; 9 10 for (int j = 0; j < armStruct.child_count; j++) 11 { 12 CCBoneData* bone = CCBoneData::create(); 13 Armature_b_struct boneStruct = wydArms_b[j + armStruct.child_index]; 14 bone->name = boneStruct.name; 15 bone->parentName = boneStruct.parentName; 16 bone->zOrder = boneStruct.zOrder; 17 18 for (int k = 0; k < boneStruct.child_count; k++) 19 { 20 CCDisplayData* display;// = CCDisplayData::create(); 21 Armature_b_d_struct displayStruct = wydArms_d[k + boneStruct.child_index]; 22 23 if ((DisplayType)displayStruct.displayType == CS_DISPLAY_SPRITE) 24 { 25 display = CCSpriteDisplayData::create(); 26 display->displayType = CS_DISPLAY_SPRITE; 27 ((CCSpriteDisplayData *)display)->displayName = displayStruct.name; 28 } 29 else 30 { 31 display = CCArmatureDisplayData::create(); 32 display->displayType = CS_DISPLAY_ARMATURE; 33 ((CCArmatureDisplayData *)display)->displayName = displayStruct.name; 34 } 35 36 bone->addDisplayData(display); 37 } 38 39 arm->addBoneData(bone); 40 } 41 42 s_armatureDataInfo.data1.push_back(arm->name); 43 CCArmatureDataManager::sharedArmatureDataManager()->addArmatureData(arm->name.c_str(), arm); 44 } 45 }
每个类型的struct都对应创建一个数组和一个list列表。
骨骼数组转struct时:遍历CCArmatureData、CCBoneData、CCDisplayData时,每次遍历都对应创建一个对应的struct加到list列表里,重点在于获取到struct对象的child_index和child_count。
struct转骨骼动画时:遍历arm_struct、arm_b_struct、arm_b_d_struct对应的数组,每次遍历都对应创建一个CCArmatureData、CCBoneData、CCDisplayData,重点在于根据struct对象的child_index和child_count来控制循环的次数、子节点在struct数组里的起始位置
优化效果
那个3M多、25600多行的xml文件,转成struct保存好,如果struct里没有 strMovement、strEvent 、strSound、strSoundEffect 这四个字段的话,大小是原来的一半,如果有这四个字节,大小是原来的两倍,其实,cocos2dx里对这四个字段的备注是: m_strMovement, m_strEvent, m_strSound, m_strSoundEffect do not support yet(2.1)。解析耗时这块,耗时大概减少80%~90%,甚是可观。