本文原鏈接: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%,甚是可觀。
本文原鏈接:http://www.cnblogs.com/zouzf/p/4450861.html