我有一个4纹理地图集的游戏。每个地图集大小为4096 x 4096,包含大约300个精灵,大小约为15kb - 50kb。在纹理包装器中没有优化,它们每个大约是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秒。
那么,单例方法比使用静态变量更好吗?预加载这种大小的地图集是否明智?
发布于 2015-05-22 21:24:07
对于你的问题有很大的不同意见的空间。牢记这一点,我建议如下:
在创建纹理地图集时,您应该始终提前计划真正需要的内容。例如,你有5种不同类型的敌人,但游戏的第一级只显示敌人#1和#2。在这种情况下,你应该创建一个纹理地图集,其中只包含你的第一级(敌人#1和#2)所需的资源。
单例优点:将所有代码集中到一个类中。你只需要有一个类的实例。避免了必须重复加载某些资产。
单例缺点:如果有大量资产需要管理,代码量可能会变得不堪重负。
sprite子类化的优点:将你的所有代码和资产放在一个类中处理,以满足精灵的需求。
子类化缺点:在某些情况下,不同的类可能会多次加载相同的动画或图像。例如,某个类型的展开可能被一个或多个子类利用。
我更喜欢使用单例方法,因为我喜欢集中我的代码。下面是我如何做到这一点的一个简化示例。在创建纹理图集时,我也使用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];
}来回答你最后一个关于预加载地图集的问题。是的,预先加载你的地图集总是明智的,因为它可以让你的游戏玩得更流畅。必须在游戏中加载资源可能会导致延迟。请记住,仅加载当前所在场景所需的资源。好的计划会给你带来好的结果。
https://stackoverflow.com/questions/30395040
复制相似问题