从零开始的小游戏开发之第三篇 Dragonbones龙骨使用多个图集的办法
这篇文章分享一点我制作游戏时的一点小技巧吧:
Dragonbones龙骨如何在cocos creator 里同时使用多个DragonBonesAtlasAsset图集的办法
这是我现在正在做的游戏视频:
https://www.bilibili.com/video/BV1j5411h7ZY/
我的qq群:1142732961 欢迎一起讨论交流啦
1.Dragonbones 龙骨在cocos creator 里使用多个图集
在学习做龙骨动画时,我发现很多组件是重复的,在小游戏里出现大量重复的素材还是很占包体的,所以我尝试着把重复的东东单独放到一个独立的图集里,龙骨动画则会同时加载两个图集的素材
完整没拆图集的角色
拆出的通用图集,我把角色通用的部位和所有可替换的衣服和武器抽了出来
角色拆出自己的图集,我这里只抽出了发型和头饰,表情服装这些都用通用图集的
合并后在游戏里不同角色的样子(这里我偷懒没换衣服,衣服其实是可以换成图集里其他的)
1.实现方法
我没有改cocos creator原生的代码逻辑,而是纯用ts脚本来实现出来的
当然我写代码比较业余,里面好多东东写的很临时,没有做封装,我就贴出一下重要的代码和步骤吧,当然如果遇到问题可以随时问我的.
首先,我们拿到要公用的图集,如果这个图集没有被用到最好手动init一次,否则是拿不到龙骨图集里的textures的.
我的逻辑是这样写的:
// 初始化公共贴图 initHeroAtlas(dragonAtlasAsset: dragonBones.DragonBonesAtlasAsset) { if (this.heroCommonAtlas != undefined) return let heroCommonAtlas = {} this.heroCommonAtlas = heroCommonAtlas let textureAtlasData = dragonAtlasAsset['_textureAtlasData'] if (!textureAtlasData) { dragonAtlasAsset['init'](dragonBones.CCFactory.getInstance()) textureAtlasData = dragonAtlasAsset['_textureAtlasData'] } if (!textureAtlasData) return let textures = textureAtlasData.textures let whiteListTexture = this.whiteListTexture for (let i = 0; i < whiteListTexture.length; i++) { let name = whiteListTexture[i] heroCommonAtlas[name] = textures[name] } let name = 'empty' heroCommonAtlas[name] = textures[name] }
这样我们获得了一个 heroCommonAtlas 的对象结构,存放图集里的texture ,这个可以用来替换.当然,如果你需要两个以上的图集也没关系,把其他的图集里的texture保存起来就可以了
我们生成的角色贴图只有头发和头饰,其他的东东要从 heroCommonAtlas 里找到,当然你可以写逻辑选择合并时具体使用图集里的哪个武器或者哪件衣服
这是我合并两个图集的逻辑:
// 合并佣兵图集 combineHero(ad: dragonBones.ArmatureDisplay) { if (ad == undefined) return let dragonAsset = ad.dragonAsset let dragonAtlasAsset = ad.dragonAtlasAsset if (dragonAsset == undefined || dragonAtlasAsset == undefined) return let armature = ad.armature() if (armature == undefined) return let slots = armature['_slots'] let whiteListSlot = this.whiteListSlot for (let i = 0; i < slots.length; i++) { let slot = slots[i] if (slot == undefined) continue if (this.contain(whiteListSlot, slot.name)) { let displayDatas = slot['_displayDatas'] for (let j = 0; j < displayDatas.length; j++) this._replaceDisplay(displayDatas[j]) if (this._replaceDisplay(slot['_displayData'])) slot['_updateDisplayData']() } } }
这里比较重要的是 更新 displayData 以及最后slot的贴图被更换成功后要手动_updateDisplayData的,否则不会立刻生效的.之所以用更新displayData数据的办法是避免龙骨播放其他动画时把插槽里的贴图给还原掉,索性我们就先让它用从公共图集里借来的texture吧
这里还有个地方要小心就是龙骨的回收,如果你做了回收一定要记得把借来的texture还回去哈,不然龙骨的factory会按照当前的atlas全部回收的,当然公共图集的我们肯定不希望被删掉啦.回收的逻辑我后面会贴出代码,这里贴一下 replaceDisplay 的实现吧:
// 使用佣兵公共贴图 _replaceDisplay(display: dragonBones.DisplayData) { let whiteListTexture = this.whiteListTexture let texture = display['texture'] if (texture == undefined) return false if (this.contain(whiteListTexture, texture.name)) { display['texture'] = this.heroCommonAtlas[texture.name] return true } return false }
这里就用到前面生成的 heroCommonAtlas 了,其实原理还是很简单的,只是换掉了texture,当然摸索的过程可不这么简单,我尝试了好多种办法,最后觉得这个办法是最安全的了
主要的逻辑就是这些接下来说一下回收龙骨时要注意的东东:
我在替换贴图前把老的texture保存下来,存到一个叫revert的字段里,因为回收的时候要把texture换回去,
我的回收逻辑大致是这个样子.龙骨一些逻辑没有做容错,所以这类偷梁换柱的操作一定要保证彻底还原回去,否则问题会非常难查:
let atlasSearch = this.atlasSearch let res = atlasSearch[k] let revert = res['revert'] // 是否有回收需要恢复的 if (revert) { let textureAtlasData = res['_textureAtlasData'] if (textureAtlasData) { let textures = textureAtlasData.textures let empty = [] // 空的key要删掉,不然龙骨会报错 for (let i in revert) { let o = revert[i] textures[i] = o // 用回原来的 if (o == undefined) empty.push(i) else { let p = o['parent'] if (p && p['imagePath'] == 'part_tex.png') { console.log('[ERROR]:', o['name'], i, ' in ', k, ' error!') empty.push(i) // 不回收就不回收,总比报错好 } } } for (let i = 0; i < empty.length; i++)delete textures[empty[i]]; } res['revert'] = undefined } atlasSearch[k] = undefined cc.loader.releaseRes(k, dragonBones.DragonBonesAtlasAsset)
原理虽然挺简单,代码也不多,但是摸索出这些花掉我好多时间,我之前试过很多种办法,换slot,换display,都会在播动画重置时出现各种问题,最后还是觉得从textureAtlasData里抽出texture换掉最稳最简单,这是我自己摸索出来的,或许有更好更专业的办法吧...