8

在我的游戏中,我有数以千计的“平铺”节点组成游戏地图(想想模拟城市),我想知道每个节点的纹理和动画处理的帧速率/内存效率最高的路线是什么?有一些独特的瓷砖“类型”,每个都有自己的纹理图集/动画,因此确保尽可能重复使用纹理是关键。

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

或者,每种瓦片类型都是某个子类。每个 SKSpriteNode 瓦片处理自己的精灵图集加载会更好吗[tileInstance texturise];(例如精灵套件如何处理这个?这种方法是否会导致相同的纹理图集被加载到内存中,以用于特定瓦片类型的每个实例?)

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

4

1 回答 1

23

记忆第一:不会有任何明显的差异。您必须加载瓦片的纹理,纹理将至少占 Map+Tiles 内存的 99%,仅此而已。

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

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

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

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

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

要向节点添加数据或附加代码,您可以通过创建自己的 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 设计错误是拥有 Player 和 Enemy 类,然后意识到两者都需要能够射箭和装备盔甲。因此,您将代码移动到根 GameObject 类,使代码可用于所有子类。使用组件,您只需将设备和射击组件添加到需要它的对象中。
  • 基于组件的设计的最大好处是,您可以将各个方面与其他事物分开开发,因此可以根据需要重用和添加它们。你几乎自然会写出更好的代码,因为你以不同的心态处理事情。
  • 根据我自己的经验,一旦你将游戏模块化成组件,你会发现更少的错误,而且它们更容易解决,因为你不必查看或考虑其他组件的代码——除非被一个组件使用,但即便如此组件触发另一个你有一个明确的边界,即当另一个组件接管时传递的值是否仍然正确?如果不是,则错误必须在第一个组件中。

这是对基于组件的设计的一个很好的介绍。混合方法当然是要走的路。这里有更多关于基于组件的设计的资源,但我强烈建议不要偏离路径并按照“公认答案的作者”的建议研究 FRP - FRP 是一个有趣的概念,但在游戏开发中还没有实际应用。

于 2013-10-14T08:37:36.727 回答