SpriteKit在哪里加载数千个精灵的纹理图集

时间:2013-10-14 05:11:20

标签: iphone cocoa ios7 sprite-kit

在我的游戏中,我有数以千计的“平铺”节点构成了一个游戏地图(想想simcity),我想知道每个节点的纹理和动画的帧速率/内存效率最高的路径是什么?有一些独特的瓷砖“类型”,每个都有自己的纹理图集/动画,所以确保纹理在可能的情况下被重用是关键。

我的所有tile节点都是单个map节点的子节点,如果地图节点处理识别拼贴类型并加载必要的地图集&动画(例如通过从plist加载纹理和地图集名称?)

或者,每个tile类型都是某个子类。每个SKSpriteNode磁贴是否更好地处理它们自己的精灵图集加载,例如[tileInstance texturise];(sprite kit如何处理这个?这个方法会导致为某个tile类型的每个实例加载到内存中的相同纹理图集吗?)

我一直在研究文档以更深入地解释地图集和纹理重用,但我不知道这种情况的典型程序是什么。任何帮助将不胜感激,谢谢。

1 个答案:

答案 0 :(得分:22)

首先

记忆:没有任何明显的区别。你必须加载瓷砖的纹理,纹理将至少占地图+瓷砖内存的99%。

纹理重用:纹理被自动重用/缓存。使用相同纹理的两个精灵将引用相同的纹理,而不是每个都有自己的纹理副本。

帧率/批处理:这就是正确批处理。 Sprite Kit通过按照它们添加到children数组的顺序呈现它们来批处理节点的子节点。只要下一个子节点使用与前一个子节点相同的纹理,它们就会被批量分配到一个绘制调用中。可能你做的最糟糕的事情是添加一个精灵,一个标签,一个精灵,一个标签等等。您希望尽可能以连续的顺序添加尽可能多的精灵。

Atlas Usage :在这里您可以赢得最多。通常开发人员会尝试对其地图集进行分类,这是错误的方法。不是每个图块(及其动画)创建一个图集,而是要创建尽可能少的纹理图集,每个图块包含尽可能多的图块。在所有iOS 7设备上,纹理图集可以是2048x2048,除iPhone 4和iPad 1外,所有其他设备都可以使用高达4096x4096像素的纹理。

此规则有例外情况,例如,如果您拥有如此大量的纹理,则无法将所有纹理一次性加载到所有设备的内存中。在这种情况下,使用您的最佳判断来找到内存使用与批处理效率的良好折衷。例如,一种解决方案可能是为每个独特的场景创建一个或两个纹理图集,或者更确切地说是#34; scenery"即使这意味着复制另一个场景的其他纹理图集中的一些图块。如果您的瓷砖几乎总是出现在任何风景中,那么将它们放入“共享”中是有意义的。图谱。

对于子类化切片,我强烈支持避免子类化节点类。特别是如果将它们子类化的主要原因仅仅是改变它们正在使用/动画的纹理。精灵已经是纹理的容器,因此您也可以更改精灵纹理并从外部为其设置动画。

要向节点添加数据或其他代码,您可以通过创建自己的NSMutableDictionary并添加所需的任何对象来仔细阅读其userData属性。典型的基于组件的方法将如下所示:

SKSpriteNode* sprite = [SKSpriteNode spriteWithWhatever..];
[self addChild:sprite];

// create the controller object
sprite.userData = [NSMutableDictionary dictionary];
MyTileController* controller = [MyTileController controllerWithSprite:sprite];
[sprite.userData setObject: forKey:@"controller"];

然后,此控制器对象将执行切片所需的任何自定义代码。它可能是动画瓷砖和其他任何东西。唯一重要的一点是将对拥有节点(这里是:sprite)的引用作为弱引用:

@interface MySpriteController
@property (weak) sprite; // weak is important to avoid retain cycle!
@end

因为精灵保留了字典。字典保留了控制器。如果控制器将保留精灵,则精灵无法解除分配,因为仍然会有一个保留引用 - 因此它将继续保留保留保留精灵的控制器的字典。

使用基于组件的方法的优势(也受Kobold Kit赞成并实施):

  • 如果设计得当,可以与任何或多个节点配合使用。如果有一天你想要一个标签,效果,形状节点瓷砖怎么办?
  • 您不需要每个磁贴的子类。一些瓷砖可能是简单的静态精灵。因此,请使用简单的静态SKSpriteNode。
  • 它允许您根据需要启动/停止或添加/删除各个方面。即使在瓷砖上,您最初也不希望拥有或需要某个方面。
  • 组件允许您构建一系列您经常需要的功能,甚至可能在其他项目中。
  • 组件可以提供更好的架构。经典的OOP设计错误是拥有玩家和敌人的类,然后意识到两者都需要能够射箭和装备盔甲。所以你move the code to the root GameObject class,使代码可用于所有子类。使用组件,您只需将设备和拍摄组件添加到需要它的对象中。
  • 基于组件的设计的巨大好处是,您可以开始将其他方面与其他方面分开开发,以便可以根据需要重复使用和添加。你几乎可以自然地编写更好的代码,因为你用不同的心态处理事物。
  • 根据我自己的经验,一旦您将游戏模块化为组件,您可以获得更少的错误并且更容易解决,因为您不必查看或考虑其他组件的代码 - 除非组件使用,但即使这样,当一个组件触发另一个组件时,你有一个清晰的边界,即当另一个组件接管时传递的值仍然正确吗?如果没有,那么bug必须在第一个组件中。

这是good introduction on component-based design。混合方法肯定是要走的路。以下是more resources on component based design但我强烈建议不要偏离路径并将FRP视为接受的答案的作者"建议 - FRP是一个有趣的概念,但在游戏开发中还没有现实世界的应用。