我有一个带有4个纹理地图集的游戏。每个地图集是4096 x 4096,包含大约300个精灵,全部~15kb - 50kb。在Texture Packer中未经优化,每个大约2到4MB,大约是优化时的一半。
它们包含绘制字符的资源,但我发现绘制单个字符(大约24个节点)~0.5秒的速度非常慢。这是因为多次调用[atlas textureNamed:textureName]
为了加快速度,我想预先加载地图集。理想情况下,我会将它们留在记忆中,因为我总是需要它们。所以我的第一个问题是,这个尺寸的地图集可能吗?我试过调用[SKTextureAtlas preloadTextureAtlases:@[MaleFeatureAtlas] withCompletionHandler...]
但是我遇到了没有堆栈跟踪的崩溃,只是丢失了与设备的连接。
目前,我有一个AtlasManager类,它具有初始化到每个纹理图集的静态变量:
static SKTextureAtlas *MaleFeatureAtlas;
static SKTextureAtlas *MaleItemAtlas;
static SKTextureAtlas *FemaleFeatureAtlas;
static SKTextureAtlas *FemaleItemAtlas;
@implementation AtlasManager
{
}
#pragma mark - Initialisation Methods
+ (void)initialize
{
MaleFeatureAtlas = [SKTextureAtlas atlasNamed:MaleFeatures];
MaleItemAtlas = [SKTextureAtlas atlasNamed:MaleItems];
FemaleFeatureAtlas = [SKTextureAtlas atlasNamed:FemaleFeatures];
FemaleItemAtlas = [SKTextureAtlas atlasNamed:FemaleItems];
}
每个字符精灵都有一个AtlasManager实例,但由于SKTextureAtlases是静态变量,我认为它们绘制起来很快。但是对[atlas textureNamed:textureName]
的不断调用确实减慢了抽签速度。我一旦绘制了节点的NSDictionary存储,所以重绘非常快,但初始绘制需要太长时间。渲染8个字符,总共只有100多个节点,大约需要5秒钟。
那么,单例方法比使用静态变量更好吗?预装这样大小的地图是明智的吗?
答案 0 :(得分:0)
对于您的问题,有很多不同意见的空间。记住这一点,我建议如下:
在创建纹理图集时,您应该始终提前计划实际需要的内容。例如,你有5种不同的敌人,但你的游戏的第一级只会出现敌人#1和#2。在这种情况下,您应该创建一个纹理图集,其中只包含第一级所需的资源(敌人#1&#2)。
Singleton职业选手: 将所有代码集中到一个类中。 您只需要有1个类的实例。 防止不得不加载一些资产。
Singleton Cons: 如果要管理大量资产,代码量可能会非常大。
Subclassing Pros: 拥有所有代码和资产在一个特定于精灵需求的类中处理。
子类缺点: 在某些情况下,您可能会有不同类多次加载相同的动画或图像。例如,某种爆炸可能会被一个或多个子类使用。
我更喜欢使用单例方法,因为我喜欢集中我的代码。下面是我如何做到这一点的简化示例。我还使用TexturePacker并在创建纹理图集时使用头文件选项。我的单身类叫做Animations。
在Animations头文件中我有:
-(void)loadPlayer0Atlas;
@property (strong) SKTexture *player0_startLeft;
@property (strong) SKAction *player0_idleLeft;
@property (strong) SKAction *player0_idleRight;
@property (strong) SKAction *player0_walkLeft;
@property (strong) SKAction *player0_walkRight;
在Animations实施文件中:
-(void)loadPlayer0Atlas {
if(self.player0Atlas == nil) {
self.player0Atlas = [SKTextureAtlas atlasNamed:PLAYER0ATLAS_ATLAS_NAME];
[SKTextureAtlas preloadTextureAtlases:[NSArray arrayWithObject:self.player0Atlas] withCompletionHandler:^{
[self loadPlayer0Assets];
}];
} else {
[[NSNotificationCenter defaultCenter]postNotificationName:@"player0AtlasLoaded" object:self];
}
}
-(void)loadPlayer0Assets {
self.player0_startLeft = PLAYER0ATLAS_TEX_PLAYERIDLEL__000;
self.player0_idleLeft = [SKAction repeatActionForever:[SKAction animateWithTextures:PLAYER0ATLAS_ANIM_PLAYERIDLEL timePerFrame:0.2]];
self.player0_idleRight = [SKAction repeatActionForever:[SKAction animateWithTextures:PLAYER0ATLAS_ANIM_PLAYERIDLE timePerFrame:0.2]];
self.player0_walkLeft = [SKAction repeatActionForever:[SKAction animateWithTextures:PLAYER0ATLAS_ANIM_PLAYERWALKL timePerFrame:0.1]];
self.player0_walkRight = [SKAction repeatActionForever:[SKAction animateWithTextures:PLAYER0ATLAS_ANIM_PLAYERWALK timePerFrame:0.1]];
[[NSNotificationCenter defaultCenter]postNotificationName:@"player0AtlasLoaded" object:self];
}
以上代码允许我通过调用方法loadPlayer0Atlas
来加载播放器的资源。此方法检查是否已创建地图集。如果是,它会发布一个NSNotification,表明这一点。如果不是,它会加载图集,将资产分配给类属性,然后发布NSNotification。
回到调用loadPlayer0Atlas
的类中,您需要在init或didMoveToView方法中注册NSNotification。
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(myMethod)
name:@"player0AtlasLoaded"
object:nil];
收到通知后,myMethod
可以继续使用代码知道现在已加载播放器地图集。
为了保持良好的内务,请记住从NSNotifications中删除调用类,如下所示:
-(void)willMoveFromView:(SKView *)view {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
回答关于预装地图册的最后一个问题。是的,预装地图集总是明智的,因为它可以让游戏更流畅。在游戏中加载资产可能会造成滞后。请记住只加载您当前所在场景所需的资产。良好的计划将为您带来良好的效果。