导入和创建许多大型精灵的正确方法:SpriteKit
Correct way to import and create many large sprites: SpriteKit
尽管阅读了 Apple 的大部分文档并阅读了他们的论坛并观看了他们的 WWDC 视频,但我似乎错过了利用 Sprite Kit 的纹理打包和性能制作许多大型 Sprites 所需的步骤。
想象 100 个不同的精灵图像,100 个不同的精灵。如果每张图片都是 256x256 像素,它们将占用多个 2048x2048 的 SpriteSheet。让我们假设这是限制,而不是 4096x4096,这样就可以理解需要不止一个 spritesheet。
在任何给定时间屏幕上可能有多达 20 个不同的精灵。所以性能是一个考虑因素。
如何导入这些图像,然后按照 SpriteKit 的纹理打包和游戏使用性能考虑的方式创建这些精灵?
只需按照 Apple 推荐的特定 link 绝对步骤即可。我一定是刚刚浏览了一下。
Apple 的新文档做到了这一点:
完全无法传达:
- 导入文件夹,还是图片?
- 导入到项目中的 WHERE?
- 如何加载并引用它们?
所以让我看看我是否可以合理地回答你的问题。请记住,虽然我使用过 SpriteKit,但我并不是它的忠实粉丝。当我确实使用它时,我没有使用 Apple 提供的任何工具(例如 SKTextureAtlas
、sks
文件等)。尽管如此,这应该仍然适用。 Apple 的最佳做法?我不知道。
问题:导入文件夹,还是图片?
如果您使用 Xcode 生成图集的方法,您 Add Files...
为您的项目创建一个名为 XYZ.atlas
的文件夹,其中 XYZ
将是纹理名称。此文件夹包含您将在图集中的所有纹理。
问题:导入到项目中的 WHERE?
只需要在您的项目中。但是您的项目文件中应该有一些组织 Group
层次结构。
问题: 加载并引用它们,如何?
一个加载示例类似于(是的,我知道 boo,Obj-C):
self.myTextureAtlas = [SKTextureAtlas atlasNamed:@"MyTexture"];
不可避免地,您会想要访问实际的纹理,并且需要执行以下操作:
self.tex0 = [self.myTextureAtlas textureNamed:@"tex0"];
这里有一个很好的教程:https://www.raywenderlich.com/45152/sprite-kit-tutorial-animations-and-texture-atlases
这里还有一张显示 .atlas
文件夹的屏幕截图。它还显示了几个工具生成的图集文件。
所以在这里你可以看到我有 2 个组,它们是对文件夹 MyTexture.atlas 和 ProgressBar.atlas 的引用。在游戏中,他们将被称为MyTexture
和ProgressBar
。
我还包括了相同的地图集,只是作为示例,但是是预先构建的。 你不会在你的项目中同时拥有这两者,我只是用它来展示包括预建厕所在内的东西。 我使用 TexturePacker 生成了它们。稍后我会解释为什么这可能是更好的选择。 TexturePacker 还可以创建 SpriteKit 图集而不是图集 PNG。
实际上,图集实际上只是一个带有子纹理的纹理。子纹理是纹理的 X/Y/W/H 部分。纹理的实际内存是图集 "held"。理解什么是图集是一件很有用的事情,因为它可以让你思考如果你必须自己实现它,你将如何支持它。或者加强它。
现在让我们来看看你将如何使用它来做一些事情useful.And为此你将需要一个纹理管理器(又名TextureManager
),精灵管理器(又名SpriteManager
) 和某种清单。
清单实际上是 "sprite name" 到 atlas:sub 纹理对之间的某种关联形式。例如:
{
"progressbar": {
"atlas": "ProgressBar",
"subtexture": progressbarbacking"
},
"progressbarfillr": {
"atlas": "ProgressBar",
"subtexture": progressbarfillr"
}
}
在本例中是一些 JSON,但您可以使用任何您想要的格式。当我构建我的游戏时,我有一个构建资产阶段,它生成我所有的纹理,并从中构建一个清单。此清单不仅告诉我存在哪些纹理,而且稍后用于找到 "sprite name" 与实际图集和子纹理的正确关联。 "sprite name" 只是一些您具有相关含义的名称。例如,它可以是 "zombie"。
您使用 TextureManager
作为您的异步加载器。此外,它还是您所有纹理的库存管理器。作为库存管理器,它将防止您重复加载纹理,并在请求时为您提供对 textures/atlases 的正确引用(如果它们存在)。
您可以使用 SpriteManager
使用清单中的数据创建一个新的 SKSpriteNode
。例如:
SKSpriteNode *progressBar = [[SpriteManager sharedInstance] createSprite:@"progressbar"];
这里我把它设为单例。使用你想要的任何实现。如果你讨厌单例,那很好,这只是一个例子。您会注意到它 returns 一个 SKSpriteNode
。我看到很多人从 SKSpriteNode
s 制作 subclasses。我从不这样做。对我来说,图形组件始终是 "has a"。但是,如果您正在执行 "is a",您仍然可以这样做。您只需要输入您需要的 class 即可。我正在考虑处理超出此问题范围的方法。
现在,如果您查看清单,您会注意到 progressbar
与名为 ProgressBar
的图集和名为 progressbarbacking
的子纹理相关联。要获得您需要的纹理,您需要在 SpriteManager
中有一些实现代码,例如:
// NOTE the literal names would be variables which contained the unpacked association from progressbar
// literal strings are used to make the idea easier to follow
SKTextureAtlas *atlas = [[TextureMaanger sharedInstance] findAtlasNamed:@"ProgressBar"];
//Error check of course
SKTexture *tex = [atlas textureNamed:@"progressbarbacking"];
// Error check of course
SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithTexture:tex];
或者您可能只是打个电话:
SKTexture *tex = [[TextureManager] sharedInstance] texNamed:@"progressbarbacking" atlas:@"ProgressBar"];
SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithTexture:tex];
好了。一种从地图集获取精灵的方法。
Apple 的最佳做法?不知道,但这与我所做的一致。
有些人会抱怨会涉及字典,其中一些会 "slow"。是的,这会受到惩罚。事实上,我也没有为此使用字典,但通过字典更容易理解这个想法。还要记住,我认为这种用法发生在加载阶段,而在游戏过程中很少发生,如果有的话。高性能游戏的技巧之一是在实际玩游戏之前预加载您需要的全部或尽可能多的数据。
哦,为什么我要预先构建地图集。所以你的部分问题是组织纹理到图集。这就是为什么。我喜欢看生成的图集,了解大小是多少,里面有什么。此外,它使可下载的地图集更容易。
作为一种优化,您可能想要尝试放置相对同时绘制的纹理。例如,将所有 HUD 项目放在同一个图集中而不是将 HUD 与背景混合是有意义的。
尽管阅读了 Apple 的大部分文档并阅读了他们的论坛并观看了他们的 WWDC 视频,但我似乎错过了利用 Sprite Kit 的纹理打包和性能制作许多大型 Sprites 所需的步骤。
想象 100 个不同的精灵图像,100 个不同的精灵。如果每张图片都是 256x256 像素,它们将占用多个 2048x2048 的 SpriteSheet。让我们假设这是限制,而不是 4096x4096,这样就可以理解需要不止一个 spritesheet。
在任何给定时间屏幕上可能有多达 20 个不同的精灵。所以性能是一个考虑因素。
如何导入这些图像,然后按照 SpriteKit 的纹理打包和游戏使用性能考虑的方式创建这些精灵?
只需按照 Apple 推荐的特定 link 绝对步骤即可。我一定是刚刚浏览了一下。
Apple 的新文档做到了这一点:
完全无法传达:
- 导入文件夹,还是图片?
- 导入到项目中的 WHERE?
- 如何加载并引用它们?
所以让我看看我是否可以合理地回答你的问题。请记住,虽然我使用过 SpriteKit,但我并不是它的忠实粉丝。当我确实使用它时,我没有使用 Apple 提供的任何工具(例如 SKTextureAtlas
、sks
文件等)。尽管如此,这应该仍然适用。 Apple 的最佳做法?我不知道。
问题:导入文件夹,还是图片?
如果您使用 Xcode 生成图集的方法,您 Add Files...
为您的项目创建一个名为 XYZ.atlas
的文件夹,其中 XYZ
将是纹理名称。此文件夹包含您将在图集中的所有纹理。
问题:导入到项目中的 WHERE?
只需要在您的项目中。但是您的项目文件中应该有一些组织 Group
层次结构。
问题: 加载并引用它们,如何?
一个加载示例类似于(是的,我知道 boo,Obj-C):
self.myTextureAtlas = [SKTextureAtlas atlasNamed:@"MyTexture"];
不可避免地,您会想要访问实际的纹理,并且需要执行以下操作:
self.tex0 = [self.myTextureAtlas textureNamed:@"tex0"];
这里有一个很好的教程:https://www.raywenderlich.com/45152/sprite-kit-tutorial-animations-and-texture-atlases
这里还有一张显示 .atlas
文件夹的屏幕截图。它还显示了几个工具生成的图集文件。
所以在这里你可以看到我有 2 个组,它们是对文件夹 MyTexture.atlas 和 ProgressBar.atlas 的引用。在游戏中,他们将被称为MyTexture
和ProgressBar
。
我还包括了相同的地图集,只是作为示例,但是是预先构建的。 你不会在你的项目中同时拥有这两者,我只是用它来展示包括预建厕所在内的东西。 我使用 TexturePacker 生成了它们。稍后我会解释为什么这可能是更好的选择。 TexturePacker 还可以创建 SpriteKit 图集而不是图集 PNG。
实际上,图集实际上只是一个带有子纹理的纹理。子纹理是纹理的 X/Y/W/H 部分。纹理的实际内存是图集 "held"。理解什么是图集是一件很有用的事情,因为它可以让你思考如果你必须自己实现它,你将如何支持它。或者加强它。
现在让我们来看看你将如何使用它来做一些事情useful.And为此你将需要一个纹理管理器(又名TextureManager
),精灵管理器(又名SpriteManager
) 和某种清单。
清单实际上是 "sprite name" 到 atlas:sub 纹理对之间的某种关联形式。例如:
{
"progressbar": {
"atlas": "ProgressBar",
"subtexture": progressbarbacking"
},
"progressbarfillr": {
"atlas": "ProgressBar",
"subtexture": progressbarfillr"
}
}
在本例中是一些 JSON,但您可以使用任何您想要的格式。当我构建我的游戏时,我有一个构建资产阶段,它生成我所有的纹理,并从中构建一个清单。此清单不仅告诉我存在哪些纹理,而且稍后用于找到 "sprite name" 与实际图集和子纹理的正确关联。 "sprite name" 只是一些您具有相关含义的名称。例如,它可以是 "zombie"。
您使用 TextureManager
作为您的异步加载器。此外,它还是您所有纹理的库存管理器。作为库存管理器,它将防止您重复加载纹理,并在请求时为您提供对 textures/atlases 的正确引用(如果它们存在)。
您可以使用 SpriteManager
使用清单中的数据创建一个新的 SKSpriteNode
。例如:
SKSpriteNode *progressBar = [[SpriteManager sharedInstance] createSprite:@"progressbar"];
这里我把它设为单例。使用你想要的任何实现。如果你讨厌单例,那很好,这只是一个例子。您会注意到它 returns 一个 SKSpriteNode
。我看到很多人从 SKSpriteNode
s 制作 subclasses。我从不这样做。对我来说,图形组件始终是 "has a"。但是,如果您正在执行 "is a",您仍然可以这样做。您只需要输入您需要的 class 即可。我正在考虑处理超出此问题范围的方法。
现在,如果您查看清单,您会注意到 progressbar
与名为 ProgressBar
的图集和名为 progressbarbacking
的子纹理相关联。要获得您需要的纹理,您需要在 SpriteManager
中有一些实现代码,例如:
// NOTE the literal names would be variables which contained the unpacked association from progressbar
// literal strings are used to make the idea easier to follow
SKTextureAtlas *atlas = [[TextureMaanger sharedInstance] findAtlasNamed:@"ProgressBar"];
//Error check of course
SKTexture *tex = [atlas textureNamed:@"progressbarbacking"];
// Error check of course
SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithTexture:tex];
或者您可能只是打个电话:
SKTexture *tex = [[TextureManager] sharedInstance] texNamed:@"progressbarbacking" atlas:@"ProgressBar"];
SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithTexture:tex];
好了。一种从地图集获取精灵的方法。
Apple 的最佳做法?不知道,但这与我所做的一致。
有些人会抱怨会涉及字典,其中一些会 "slow"。是的,这会受到惩罚。事实上,我也没有为此使用字典,但通过字典更容易理解这个想法。还要记住,我认为这种用法发生在加载阶段,而在游戏过程中很少发生,如果有的话。高性能游戏的技巧之一是在实际玩游戏之前预加载您需要的全部或尽可能多的数据。
哦,为什么我要预先构建地图集。所以你的部分问题是组织纹理到图集。这就是为什么。我喜欢看生成的图集,了解大小是多少,里面有什么。此外,它使可下载的地图集更容易。
作为一种优化,您可能想要尝试放置相对同时绘制的纹理。例如,将所有 HUD 项目放在同一个图集中而不是将 HUD 与背景混合是有意义的。