SpriteKit导入TexturePacker导出的纹理集

1、为什么要使用纹理集?

纹理集是将多张小图合成一张大图,使用纹理集有以下优点:
1、减少内存占用,减少磁盘占用;
2、减少磁盘读取次数,一次性读取一张大图比多次读取多张小图速度更快;
3、减少OpenGL绘制次数;
可以参照这里的讲解https://blog.csdn.net/tonny_guan/article/details/26232685

2、SpriteKit自带的纹理集工具SKTextureAtlas

SKTextureAtlas的用法比较简单,接口也很简单。将纹理图片放入文件夹,并将文件夹命名为xxx.atlas,然后导入工程,项目中就可以使用方法:

+ (instancetype)atlasNamed:(NSString *)name;

创建纹理集,之后使用方法

- (SKTexture *)textureNamed:(NSString *)name;

获取纹理集中的纹理。

3、TexturePacker

实际上大多数的游戏开发者都会用TexturePacker工具来打包纹理集,TexturePacker可以将一系列的小图整合成一张大的.png图片,外加一个描述文件,描述文件可以是.plist格式或者其他格式。描述文件的作用是描述每张小图在大图中的位置,旋转等信息。TexturePacker支持市面上流行的Unity,Cocos等游戏引擎,在导出的时候可以选择导出不同的游戏引擎。
遗憾的是SKTextureAtlas不支持TexturePacker导出的格式,而.atlas的文件夹的形式也不适合图片从服务器端加载的情况。因此我想自己写一个工具,支持读取TexturePacker导出的支持Cocos格式的.plist文件,如果需要支持其他引擎,可以分析相应的描述文件做相应的调整。

4、TexturePacker导出的Cocos格式的.plist描述文件分析

.plist文件是一个字典,主要分为两部分:
1、metadata:主要包含格式,png图片大小,png图片的名称等信息

image.png

2、frames:这一部分就是原来的各个小图片在合成到一张大图片中之后的控制信息,这是一个数组。每个数组的数据如下:
aliases:不懂,不重要
spriteOffset:TexturePacker在合图的时候可以选择修剪模式(自动裁减掉原图的透明部分),表示裁剪之后的图中心相对于原图中心的偏移量
spriteSize:合图之后的大小,这个值会小于或等于原图大小
spriteSourceSize:原图的大小
textureRect:这张图在整张大图里面的位置(要注意这个坐标是以左上角为原点的)
textureRotated:是否顺时针旋转90度,合图的时候可以勾选rotated,有的小图有可能会被旋转以最大限度利用空余部分
更详细的介绍可以参考https://www.codeandweb.com/blog/2016/01/29/cocos2d-plist-format-explained
image.png

5、WPTeatureAtlas和WPTexture

我写的WPTeatureAtlas主要负责解析.plist文件,存储解析出的纹理,提供便捷方法访问纹理

@interface WPTextureAtlas : NSObject

@property(nonatomic, strong, readonly) NSString *plistFile;
@property(nonatomic, strong, readonly) NSString *imageFile;
@property(nonatomic, strong, readonly) SKTexture *totalTexture;
@property(nonatomic, strong, readonly) NSMutableDictionary<NSString *, WPTexture *> *texturesDict;
@property(nonatomic, strong, readonly) NSArray<WPTexture *> *sortTexturesArr;

- (instancetype)initWithPlistFile:(NSString *)plistFile
                        imageFile:(NSString *)imageFile;

- (WPTexture *)textureWithName:(NSString *)name;

@end

WPTexture继承自SKTexture,并添加前面描述的.plist文件中的属性

@interface WPTexture : SKTexture

// 裁剪后图片的中心相对于裁剪前图片的中心偏移量
@property(nonatomic, assign, readonly) CGPoint spriteOffset;
// 裁剪之后的大小
@property(nonatomic, assign, readonly) CGSize spriteSize;
// 裁剪之前的大小
@property(nonatomic, assign, readonly) CGSize spriteSourceSize;
// 是否顺时针旋转90度
@property(nonatomic, assign, readonly) BOOL textureRotated;

+ (instancetype)textureWithDict:(NSDictionary *)dict
                   totalTexture:(SKTexture *)totalTexture;

@end

下面是解析代码,这里面主要用到了SKTexture的textureWithRect:inTexture:方法;这里对textureRect做了转换,因为在OpenGL里面纹理坐标是左下角为原点,宽和高都是1,textureWithRect:inTexture:方法里面的rect参数就是纹理坐标下面的rect。这个方法返回的texture就是一整张大图中的一部分,达到我们截取一部分纹理的目的。

+ (instancetype)textureWithDict:(NSDictionary *)dict
                   totalTexture:(SKTexture *)totalTexture
{
    BOOL textureRotated = [dict[@"textureRotated"] boolValue];
    
    CGRect rect = CGRectFromString(dict[@"textureRect"]);
    // TexturePacker中获取的rect是原点在左上角点,但是SpriteKit、OpenGL默认加载的纹理坐标是原点在左下角点
    CGFloat x, y, w, h;
    if (textureRotated) { // 如果顺时针旋转了90度,那么width和heigth调换了
        x = rect.origin.x / totalTexture.size.width;
        y = (totalTexture.size.height - rect.origin.y - rect.size.width) / totalTexture.size.height;
        w = rect.size.height / totalTexture.size.width;
        h = rect.size.width / totalTexture.size.height;
    } else {
        x = rect.origin.x / totalTexture.size.width;
        y = (totalTexture.size.height - rect.origin.y - rect.size.height) / totalTexture.size.height;
        w = rect.size.width / totalTexture.size.width;
        h = rect.size.height / totalTexture.size.height;
    }
    
    WPTexture *texture = [WPTexture textureWithRect:CGRectMake(x, y, w, h) inTexture:totalTexture];
    
    texture.spriteOffset = CGPointFromString(dict[@"spriteOffset"]);
    texture.spriteSize = CGSizeFromString(dict[@"spriteSize"]);
    texture.spriteSourceSize = CGSizeFromString(dict[@"spriteSourceSize"]);
    texture.textureRotated = textureRotated;
    
    return texture;
}

6、支持纹理的裁剪和旋转

TexturePacker在合成图片的时候是支持裁剪和旋转的,裁剪可以裁剪掉透明像素,而旋转则可以更大限度利用空间。但是前面的SKTexture创建的纹理仅仅是截取大图中的一部分,如果这个图被裁剪,或者旋转了,那么我们直接在SKSpriteNode中使用SKTexture会导致贴图的位置或方向不正确,这个时候我们需要对SKSpriteNode做几何变换才能达到正确的效果。我写了个WPSpriteNode来处理这种情况:

@interface WPSpriteNode : SKSpriteNode

+ (instancetype)spriteNodeWithWPTexture:(WPTexture *)texture;
- (instancetype)initWithWPTexture:(WPTexture *)texture;

@end

@implementation WPSpriteNode

+ (instancetype)spriteNodeWithWPTexture:(WPTexture *)texture
{
    return [[WPSpriteNode alloc] initWithWPTexture:texture];
}

- (instancetype)initWithWPTexture:(WPTexture *)texture
{
    if (self = [super initWithTexture:texture]) {
        if (texture.textureRotated) {
            // 顺时针转了90度之后,原来的Y转成与X轴同向,而原来的X变成与-Y同向
            self.anchorPoint = CGPointMake(0.5 - texture.spriteOffset.y / texture.spriteSize.height, 0.5 + texture.spriteOffset.x / texture.spriteSize.width);
            // 逆时针转回90度
            self.zRotation = M_PI_2;
        } else {
            // 根据偏移量将裁剪后的图反向移回原图的中心
            self.anchorPoint = CGPointMake(0.5 - texture.spriteOffset.x / texture.spriteSize.width, 0.5 - texture.spriteOffset.y / texture.spriteSize.height);
        }
    }
    
    return self;
}

@end

demo工程在这里

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容

  • Cocoa利用TexturePacker创建的纹理图集实现角色的帧动画 by 大熊猫侯佩 什么是TexturePa...
    hopy阅读 3,014评论 0 5
  • DragonBones旧版本动画文件转新版动画 DragonBones(简称:DB)2.2版本的骨骼动画是早期Co...
    丶忒阅读 4,352评论 1 1
  • 推开沾满灰尘的镂榥 阳光 透过指缝照在我满是胡茬的脸上 微风 偷偷吹散你残留的味道 回忆的沙漏 又倒转了 果然 还...
    CHITYzz首肯阅读 740评论 0 0
  • 最美户外瑜伽行前段时间已结束,现在整理各地图片,看着真心喜欢,晒晒图,看看瑜伽男神女神们是如何积极向上生活的哈!
    二月份的天阅读 193评论 0 0
  • 田华20171130(第79天) 【一个目标】三个月店里完成纯收入10万元【与此相关的好种子】 (1)格西老师智库...
    竹林千顷田华阅读 340评论 0 1