内存:首先,不会有任何明显的差异。您必须加载瓦片纹理,纹理将占地图+瓦片内存的至少99%。
纹理重用:纹理会自动被重用/缓存。使用相同纹理的两个精灵将引用同一纹理,而不是每个精灵都拥有自己的纹理副本。
帧率/批处理:这完全取决于适当地批处理。Sprite Kit通过按照它们添加到子节点数组中的顺序渲染它们来批处理节点的子项。只要下一个子节点使用与上一个子节点相同的纹理,它们就会全部批处理为一个绘制调用。可能最糟糕的事情是添加一个精灵、一个标签、一个精灵、一个标签等等。您需要尽可能多地添加使用连续顺序的相同纹理的精灵。
Atlas使用: 这里是你可以获得最多优势的地方。通常开发者会尝试对他们的图集进行分类,这是错误的做法。相反,你应该尽可能地创建尽可能少的纹理图集,每个图集包含尽可能多的瓷砖(和动画)。在所有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是一个有趣的概念,但在游戏开发中尚未有真正的应用。
ignoresSiblingOrder
设置为YES
,则无需过于担心子节点的顺序 - Sprite Kit会自动进行排序以实现高效绘制(例如,同一纹理集中的所有精灵在一个绘制调用中)。在纹理集方面,考虑到ignoresSiblingOrder
,如果有太多/太大的纹理需要放入一个纹理集中,可以使用节点层次结构将场景组织成“图层”,每个图层可以是一个绘制调用和一个纹理集(例如,世界与HUD)。 - rickster[sprite.userData setObject: forKey:@"controller"];
。应该是[sprite.userData setObject:controller forKey:@"controller"];
。 - rizzes