在我的游戏中,我有数以千计的“平铺”节点构成了一个游戏地图(想想simcity),我想知道每个节点的纹理和动画的帧速率/内存效率最高的路径是什么?有一些独特的瓷砖“类型”,每个都有自己的纹理图集/动画,所以确保纹理在可能的情况下被重用是关键。
我的所有tile
节点都是单个map
节点的子节点,如果地图节点处理识别拼贴类型并加载必要的地图集&动画(例如通过从plist加载纹理和地图集名称?)
或者,每个tile类型都是某个子类。每个SKSpriteNode磁贴是否更好地处理它们自己的精灵图集加载,例如[tileInstance texturise];
(sprite kit如何处理这个?这个方法会导致为某个tile类型的每个实例加载到内存中的相同纹理图集吗?)
我一直在研究文档以更深入地解释地图集和纹理重用,但我不知道这种情况的典型程序是什么。任何帮助将不胜感激,谢谢。
答案 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赞成并实施):
这是good introduction on component-based design。混合方法肯定是要走的路。以下是more resources on component based design但我强烈建议不要偏离路径并将FRP视为接受的答案的作者"建议 - FRP是一个有趣的概念,但在游戏开发中还没有现实世界的应用。