Sprite kit 如何通过预加载优化来自大型SKTextureAtlas文件的图形?

Sprite kit 如何通过预加载优化来自大型SKTextureAtlas文件的图形?,sprite-kit,singleton,preloading,texturepacker,sktextureatlas,Sprite Kit,Singleton,Preloading,Texturepacker,Sktextureatlas,我有一个有4个纹理地图集的游戏。每个图集是4096 x 4096,包含大约300个精灵,全部约15kb-50kb。在纹理打包机中未经优化,它们每个约为2-4MB,优化后约为一半 它们包含用于绘制角色的资源,但我发现绘制单个角色(大约24个节点)的速度非常慢,约为0.5秒。这是因为多次调用[atlas textureName:textureName] 为了加快速度,我想预加载地图集。理想情况下,我会把它们留在记忆中,因为我总是需要它们。所以我的第一个问题是,对于这样大小的地图集,这可能吗?我尝试调

我有一个有4个纹理地图集的游戏。每个图集是4096 x 4096,包含大约300个精灵,全部约15kb-50kb。在纹理打包机中未经优化,它们每个约为2-4MB,优化后约为一半

它们包含用于绘制角色的资源,但我发现绘制单个角色(大约24个节点)的速度非常慢,约为0.5秒。这是因为多次调用
[atlas textureName:textureName]

为了加快速度,我想预加载地图集。理想情况下,我会把它们留在记忆中,因为我总是需要它们。所以我的第一个问题是,对于这样大小的地图集,这可能吗?我尝试调用
[SKTextureAtlas Preload TextureAtlases:@[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的实例,但由于SkTextureAtlase是静态变量,我想它们绘制起来会很快。但是对
[atlas textureName:textureName]
的不断调用确实会减慢绘图速度。我正在存储绘制后的节点字典,因此重绘非常快,但初始绘制时间太长。渲染8个字符,总共超过100个节点,大约需要5秒钟


那么,单例方法比使用静态变量好吗?预加载这种大小的地图集明智吗

对于你的问题,有很多不同意见的空间。考虑到这一点,我建议如下:

创建纹理图集时,应始终提前计划真正需要的内容。例如,你有5种不同类型的敌人,但你的游戏第一关只显示敌人1和2。在这种情况下,您应该创建一个纹理图集,其中只包含第一个关卡(敌人1和2)所需的资源

单身人士的优点: 将所有代码集中到一个类中。 您只需要有一个类的实例。 防止必须加倍加载某些资产

单例缺点: 如果有很多资产需要管理,那么代码量可能会非常大

子类化优点: 让您的所有代码和资产在一个类中处理,以满足精灵的需求

子类化Cons: 在某些情况下,不同的类可能多次加载相同的动画或图像。例如,某类爆炸可能会被一个或多个子类利用

我更喜欢使用单例方法,因为我喜欢集中我的代码。下面是一个关于我如何做到这一点的简化示例。在创建纹理图集时,我还使用TexturePacker和头文件选项。我的单人课叫做动画

在动画头文件中,我有以下内容:

-(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;
在动画实现文件中:

-(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
来加载玩家的资产。此方法检查是否已创建图集。如果是,它将发布一个NSN通知,指出这一点。如果否,则加载atlas,将资产分配给类属性,然后发布通知

回到名为
loadPlayer0Atlas
的类中,您需要在init或didMoveToView方法中注册NSNotification

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(myMethod)
                                             name:@"player0AtlasLoaded"
                                           object:nil];
一旦收到通知,
myMethod
就可以继续编码,知道玩家地图册已经加载

要实现良好的内务管理,请记住从NSN通知中删除调用类,如下所示:

-(void)willMoveFromView:(SKView *)view {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

来回答您关于预加载atlas的最后一个问题。是的,预装地图册总是明智的,因为它有助于更流畅的游戏。必须在游戏中期加载资产可能会导致延迟。请记住,仅加载当前场景所需的资源。好的计划会给你带来好的结果。

谢谢你的详细回答。我的问题是,我的地图集包含创建用户制作的化身所需的所有精灵,并且可能有数百万种组合,因此在任何时候,我都可能需要所有地图集(特别是如果我在屏幕上有多个角色的话)。考虑到它们的大小,据你所知,这应该是可以预加载的东西吗?还是会消耗太多内存?@Smikey-如果你有大量可能的组合,这是否意味着你对每个可能的组合都有一个纹理,或者你有其他机制来处理它?