Cocos2d-x 之使用 TexurePacker+Cocos Studio 协同工作

来源:互联网 发布:淘宝欧舒丹是正品吗 编辑:程序博客网 时间:2024/06/06 01:20

写在前面

Cocos Studio 是 cocos2d-x 一个比较好用的编辑器,我们常用它来搭建场景 UI,导出场景文件和资源,然后在代码中直接加载场景文件。一般导出场景文件的时候会连同资源一起导出,场景文件保存了资源的读取方式,游戏启动时会根据这个读取方式正确找到资源并加载。将小图片打包成一张大图片,导出合图和 plist 文件,然后在程序中加载 plist 文件是一种比较常见和可行的方法。Cocos Studio 提供了打包合图的功能,能满足基本的需求。这两天使整个项目的图片打包和资源管理工作变得脚本化,自动化,还想对图片资源进行加密,Cocos Studio 已经满足不了需求了。使用 TexturePacker 能很好地解决问题,首先 TexturePacker 有脚本工具,可以使用脚本来自动打包图片;其次 TexturePacker 在打包图片的时候可以进行加密。但是,使用 Cocos Studio 和 TexturePacker 协同工作,有些坑我们不得不注意。

解密 csd

Cocos Studio 中的场景和层源文件都是 csd 格式,导出的文件是 csb 格式,csb 是二进制文件,它是 csd 文件编译后的结果,可以实现加密。我们重点关注的是 csd 文件的格式,使用文本编辑工具打开一个 csd 文件

<GameFile>  <PropertyGroup Name="GameScene" Type="Scene" ID="8c1fcf53-2f4f-46bf-a636-8fc966e42aef" Version="3.10.0.0" />  <Content ctype="GameProjectContent">    <Content>      <Animation Duration="0" Speed="1.0000" />      <ObjectData Name="Scene" Tag="23" ctype="GameNodeObjectData">        <Size X="800.0000" Y="600.0000" />        <Children>            <AbstractNodeData Name="user_text" ActionTag="593766283" Tag="15" IconVisible="False" LeftMargin="363.5000" RightMargin="363.5000" TopMargin="533.5000" BottomMargin="37.5000" FontSize="24" LabelText="的房子" ShadowOffsetX="2.0000" ShadowOffsetY="-2.0000" ctype="TextObjectData">                <Size X="73.0000" Y="29.0000" />                <AnchorPoint ScaleX="0.5000" ScaleY="0.5000" />                <Position X="400.0000" Y="52.0000" />                <Scale ScaleX="1.0000" ScaleY="1.0000" />                <CColor A="255" R="255" G="255" B="255" />                <PrePosition X="0.5000" Y="0.0867" />                <PreSize X="0.0913" Y="0.0483" />                <FontResource Type="Normal" Path="font/xiaoxin.ttf" Plist="" />                <OutlineColor A="255" R="255" G="0" B="0" />                <ShadowColor A="255" R="110" G="110" B="110" />          </AbstractNodeData>            ...        </Children>      </ObjectData>    </Content>  </Content></GameFile>

可以看到 csd 文件的内容其实就是使用 xml 来组织的,上面是一个场景 GameScene 的内容,它的数据从 ObjectData 标签开始,Children 标签下保存的就是每个 UI 的数据,AbstractNodeData 保存的就是一个 UI 的数据。上面代码列出的是一个 Text 结点的数据,AbstractNodeData 标签最后有个属性 ctype 保存了该结点的类型,我们要关注的是 Sprite,Button 等用到图片资源的结点,这些结点有个标签标明了资源的加载方式,主要有两种。

直接加载单张图片

<AbstractNodeData Name="bg" ActionTag="-1266017908" Tag="13" IconVisible="False" ctype="SpriteObjectData">    <Size X="800.0000" Y="600.0000" />    <AnchorPoint ScaleX="0.5000" ScaleY="0.5000" />    <Position X="400.0000" Y="300.0000" />    <Scale ScaleX="1.3600" ScaleY="1.3600" />    <CColor A="255" R="255" G="255" B="255" />    <PrePosition X="0.5000" Y="0.5000" />    <PreSize X="1.0000" Y="1.0000" />    <FileData Type="Normal" Path="ui/aquarium1.jpg" Plist="" />    <BlendFunc Src="770" Dst="771" /></AbstractNodeData>

我们看到 AbstractNodeData 结点下面有个子结点 FileData,所有使用到图片资源的结点都会有这个子结点,这个结点就标明了 UI 加载时如何读取这张图片。Type 标明了加载的方式,Normal 就是直接读取这张图片,图片的路径保存在 Path 中,这种方式不需要用 Plist 文件。

加载 Plist 文件

<AbstractNodeData Name="shove_sp" ActionTag="-109570670" Tag="30" IconVisible="False" LeftMargin="12.0000" RightMargin="12.0000" TopMargin="13.0000" BottomMargin="13.0000" ctype="SpriteObjectData">    <Size X="80.0000" Y="80.0000" />    <AnchorPoint ScaleX="0.5000" ScaleY="0.5000" />    <Position X="35.0000" Y="36.0000" />    <Scale ScaleX="1.0000" ScaleY="1.0000" />    <CColor A="255" R="255" G="255" B="255" />    <PrePosition X="0.5000" Y="0.5000" />    <PreSize X="0.6571" Y="0.6389" />    <FileData Type="MarkedSubImage" Path="ui/Shovel.png" Plist="ui.plist" />    <BlendFunc Src="1" Dst="771" /></AbstractNodeData>

这种方式把小图片打包成合图,所以加载的方式是根据 plist 文件从合图取到小图片。MarkedSubImage 标明是加载 plist 的方式,此时会把 Plist 文件保存到 Plist 属性中,而 Path 属性保存的则是小图片在合图中的名字。

使用哪种方式

既然有两种读取资源的方式,那么使用哪一种呢? csd 文件的内容是自动生成的,一般我们不会用文本工具打开 csd 文件来修改,所以选择哪种方式是 Cocos Studio 来做的。但也不是说我们控制不了,Cocos Studio 也是根据我们对资源的处理方式来自动选择读取方式。默认情况下是使用第一种,即直接读取单张图片,这种方式我们在导出 csb 文件时得把图片也导出,否则 csb 文件加载时找不到图片就无法正常显示 UI。前面说到 Cocos Studio 也可以打包图片,如果我们在 Cocos Studio 把场景用到的图片打包成合图,这时 Cocos Studio 就能检测到哪张图片被并到合图中,它就会选择第二种方式。此时导出 csb 文件就得同时导出合图和 Plist 文件。其实应该导出什么资源并不需要我们手动控制,Cocos Studio 会智能地判断并导出,我们要做的就是发布时选择 发布资源和项目文件 选项即可。

发布设置

使用 TexturePacker 会有什么问题

其实只是单纯地使用 Cocos Studio 来设计场景和管理资源是不会有什么问题的,无论选择哪一种资源加载方式,整个工作流程都不复杂,搭建完场景后一键发布就完事了。但是,为了使用脚本和加密功能,我们需要使用 TexturePacker 来管理资源;这时 Cocos Studio 发布时就不能导出资源了,否则就会存在两份资源,Cocos Studio 导出的一份,TexturePacker 生成的一份。所以发布项目时要选择 仅发布项目文件

发布设置

既然 Cocos Studio 不能发布资源,那么场景文件加载时只能使用 TexturePacker 发布的资源,关键是 TexturePacker 发布的资源场景文件能直接使用吗?这里要针对上面的两种资源加载方式分别讨论。

直接加载单张图片

很显然这种方式是不可行的,TexturePacker 是把图片打包成合图,所以单张小图片已经不存在了,使用这种方式场景 UI 肯定是加载不出来的。

加载 Plist 文件

直接加载单张图片的方式已经被 pass 掉了,那加载 plist 文件的方式可行吗?毫无疑问,必须可行,否则这些文章就没存在的意义了。这种方式可行的基础是 Cocos Studio 和 TexturePacker 打包图片的方式一样,都是导出 plist 文件,当然合图就可能不一样了,Cocos Studio 只能导出 png 格式的合图,而 TexturePacker 可以导出很多格式的合图,所幸的是场景文件读取资源靠的只是 plist 文件而与合图无关(事实上,程序运行时先根据 plist 文件和合图把所有小图片加载到缓冲区,场景加载时是根据 plist 文件和小图片的名称从缓冲区中取到这张小图片)。这种方式虽然可行,但还有两个问题需要解决。

精灵帧缓冲区并没有图片

使用 TexturePacker 打包图片,然后场景设计时用到的资源也使用合图的方式,但游戏运行时你还是会发现场景 UI 没法加载出来;这时断点看一个精灵帧缓冲区 SpriteFrameCache,你会发现它的内容是空的。原因很简单,我们并没有做任何工具把资源加载到缓冲区,缓冲区当然是空的,UI 也自然加载不出来。但为什么使用 Cocos Studio 导出的资源却能在缓冲区找到呢,这应该是引擎的一个机制,会在游戏启动时把 Cocos Studio 场景用到的所有资源加载到缓冲区;虽然我不知道它是怎么做的,在哪里做的,但缓冲区中确确实实已经有了这些资源,否则 UI 无法正常显示。我们不用管 Cocos Studio 这种机制,使用 Cocos Studio 来导出资源已经被放弃了,所以我们现在要考虑的是怎么把 TexturePacker 导出的资源加载到缓冲区。很简单,在第一次加载场景文件 csb 之前,把用到的所有资源事先加载到缓冲区。

cc.SpriteFrameCache:getInstance():addSpriteFrames("ui.plist", "ui.png")

plist 文件中小图片的命名方式不统一

把 TexturePacker 导出的资源加载到缓冲后,可能发现场景 UI 还是显示不出来,这就是 TexturePacker 导出的 plist 文件和 Cocos Studio 导出的存在差异性造成的。两者导出的 plist 文件格式虽然一样,但小图片的命名方式却不一样。Cocos Studio 的命名方式是路径+文件名,而在 csd 文件我们可以看到小图片的文件名也是路径+文件名,所以场景文件可以根据小图片的文件名正确从缓冲区找到它

<FileData Type="MarkedSubImage" Path="ui/Shovel.png" Plist="ui.plist" />
<!--ui.plist--><key>ui/Shovel.png</key><dict>    <key>aliases</key>    <array/>    <key>spriteOffset</key>    <string>{0,0}</string>    <key>spriteSize</key>    <string>{80,80}</string>    <key>spriteSourceSize</key>    <string>{80,80}</string>    <key>textureRect</key>    <string>{{431,0},{80,80}}</string>    <key>textureRotated</key>    <false/></dict>

比如这个例子,使用 Cocos Studio 导出的 ui.plist 文件中有个结点,它的 key 值为 ui/Shovel.png,与场景文件中的 Path 属性 ui/Shovel.png 一致,所以场景文件能够通过文件 ui.plist 和图片名称 ui/Shovel.png 顺利在缓冲区中找到图片。但使用 TexturePacker 导出的 ui.plist 文件如下

<!--ui.plist--><key>Shovel.png</key><dict>    <key>aliases</key>    <array/>    <key>spriteOffset</key>    <string>{0,0}</string>    <key>spriteSize</key>    <string>{80,80}</string>    <key>spriteSourceSize</key>    <string>{80,80}</string>    <key>textureRect</key>    <string>{{431,0},{80,80}}</string>    <key>textureRotated</key>    <false/></dict>

在这个文件中,结点的名称是 Shovel.png,加载之后缓冲区中会有个叫 Shovel.png 的 SpriteFrame,而没有叫 ui/Shovel.png 的 SpriteFrame;所以场景文件通过文件 ui.plist 和图片名称 ui/Shovel.png 是无法在精灵帧缓冲区找到相应图片的,场景 UI 也自然加载不出来。

解决这问题的办法其实很简单,只是我之前没去尝试白白苦恼了很久。TexturePacker 默认导出的 plist 文件中小图片的文件名是不包括的路径的,但并不代表它不能做到包含路径。事实上,是否包含路径是可选的,只是自己想当然地以为不能。

导出文件名

第一个选项 Trim sprite names 选中时,导出的文件名不包括后缀名,比如上面的例子导出的结果就是 <key>Shove</key>。第二个选项 Prepend folder name 选中时,导出的文件名就包括了路径,这就是我们想要的功能。比如上面的例子导出结果为 <key>ui/Shove.png</key>,这就和 Cocos Studio 导出的一样,问题也就迎刃而解了。如果这两个选项都勾上,则结果为 <key>ui/Shove</key>,我们可以根据需求灵活搭配。
使用命令行的方式则加上这两个参数即可

--trim-sprite-names--prepend-folder-name 

总结

Cocos Studio 和 TexturePacker 协同工作,Cocos Studio 负责搭建场景,使用图片资源但不导出资源;TexturePacker 负责导出和管理图片资源。为了使用 Cocos Studio 搭建的场景能够正确加载 TexturePacker 导出的资源,要遵循下面的步骤

  • 开始前,组织好资源的目录结构,避免后期不断修改;一般把“生”资源放在 cocostudio 目录下,一旦确定了目录结构,就不再修改;后期使用 Cocos Studio 搭建场景和使用 TexturePacker 脚本打包合图,都会基于这个目录结构

  • 第一步,使用 Cocos Studio 正常搭建场景

  • 第二步,在 Cocos Studio 新建合图 csi 文件,把场景用到的所有资源打包成合图(目的是为了让 Cocos Studio 知道我们的资源加载想用 plist 的方式而不是直接加载单张图片)

  • 第三步,导出项目文件而不导出资源(在 Cocos Studio 新建合图,导出项目文件会把合图也导出,我们只需要使用 TexturePacker 打包的文件覆盖即可)

  • 第四步,使用 TexturePacker 打包合图,TexturePacker 管理所有的图片资源,包括搭建场景没用到的,我们可以使用脚本智能化地管理这些资源,但搭建场景用到的资源打包时的名称必须与 Cocos Studio 使用的名称完全对应,即要加上路径

  • 第五步,在加载第一个场景文件之前把 TexturePacker 导出的所有合图资源加载到缓冲区,可以使用配置文件的方式列出所有要加载的资源

阅读全文
0 0
原创粉丝点击