1
votes

I developed an iOS game using Sprite Kit with a lot of enemies and obstacles which I animated. Every time the animation frames are loaded for the first time, the game lags a little and then it runs fine.

For example my one of the enemies looks like this:

Fox implementation file:

- (id)initWithTexture:(SKTexture *)nodetexture
{
    self = [super initWithTexture:nodetexture];
    if (self) {
        //Initialization code
        [self setScale:0.2];
        [self setSpeed:20];
        self.physicsBody.allowsRotation = false;
        self.physicsBody.contactTestBitMask = 0x1 << 1 | 0x1 << 3;
        self.physicsBody.categoryBitMask = 0x1 << 2;//enemy
        self.physicsBody.collisionBitMask = 0x1 << 1 | 0x1 << 3;
        [self animateSelf];
    }
    return self;
}

- (void) animateSelf{
    [super animateSelf];
    NSMutableArray *textures = [NSMutableArray arrayWithCapacity:16];
    for (int i = 1; i < 5; i++) {
        NSString *textureName = [NSString stringWithFormat:@"fox%d.png", i];
        SKTexture *texture =[SKTexture textureWithImageNamed:textureName];
        [textures addObject:texture];
    }
    [self removeActionForKey:[NSString stringWithFormat:@"animate %@", self.class]];
    [self runAction:[SKAction sequence:@[[SKAction repeatAction:[SKAction sequence:@[[SKAction runBlock:^{
        self.xScale = -0.2;
        [self jumpEntity];
        [self setSpeed:40];
        [self impulseEntityRight];
        [self setSpeed:20];
        self.runAnimation = [SKAction animateWithTextures:textures timePerFrame:3];
    }], [SKAction waitForDuration:20], [SKAction runBlock:^{
        self.xScale = 0.2;
        [self jumpEntity];
        [self setSpeed:40];
        [self impulseEntityLeft];
        [self setSpeed:20];
        [self runAction:[SKAction repeatActionForever:self.runAnimation]];
    }], [SKAction waitForDuration:20]]] count:5]]] withKey:[NSString stringWithFormat:@"animate %@", self.class]];
}

The frames are stored in an .atlas file and I believe the reason for the lag is running the for loop for the first time but that is just an assumption.

I tried preloading the frames in the GameViewController along those lines:

GameViewController Implementation File :

- (void)viewDidLoad
{
    [super viewDidLoad];
    //Load new game
    [self initialiseGameScene];
    [self.gamescene.physicsWorld setContactDelegate:self];
    [self checkTiltBool];
    self.gamestarted = false;
    self.startedbytilt = false;
    [self addListenersToButtons];
    [self instantiateGestureRecognizers];
    self.spawnedobjects = [[NSMutableArray alloc] init];
    if (self.tiltbool == true){
        [self startGame];
    }
    [self updateLifeIcons];
    NSMutableArray *frames = [[NSMutableArray alloc]init];
    for (int i=0; i<[SKTextureAtlas atlasNamed:@"fox"].textureNames.count; i++) {
        [frames addObject:[SKTexture textureWithImageNamed:[SKTextureAtlas atlasNamed:@"fox"].textureNames[i]]];
    }
    [SKTexture preloadTextures:frames withCompletionHandler:^{NSLog(@"Complete");}];

}

Am I going the right way? Ideally, I would like to preload all the animations before the game starts so the animations run smoothly.

1
You are preloading. You just need to make your code flow in a way that the preloading completes and only then does your game play start. For example, use a BOOL variable where you have your NSLog "complete" to let you know when loading is done. You can test the BOOL var in your update method to know when to start running your game play. - sangony
You should not be creating a new array of textures each time you run an animation. Instead store a single array of textures for each animation you have and reuse each array of textures. - Epic Byte
the texture preloading code is quite wasteful. Every time in the loop you get the fox atlas to get all texture names from it twice, once in the for loop and once inside the loop. - LearnCocos2D
So could I load the frames in the GameViewController and use then use the preloaded frames in my Fox class? - jenpaff
I would make it a shared class variable and load the animations in when your loading your scene data. You should definitely have a method for loading/preparing your scene. I personally would load the data in your scene class, but it's all up to you. GameViewController could work as well. - Epic Byte

1 Answers

0
votes

I would highly recommend that you initialize the texture-array only one time. At the moment, you create a new NSMutableArray everytime you call the animateSelf-method.

So take this part:

  NSMutableArray *textures = [NSMutableArray arrayWithCapacity:16];
    for (int i = 1; i < 5; i++) {
        NSString *textureName = [NSString stringWithFormat:@"fox%d.png", i];
        SKTexture *texture =[SKTexture textureWithImageNamed:textureName];
        [textures addObject:texture];
    }

And put it in a method, which will be called at application-start. You could for example create a global variable which you can access from within the code.

Also one issue could be, if you use it, the iOS-simulator. I've had often the problem that I ran a game on the iOS-simulator and it was laggy and didn't perform well(Around 23FPS). But on the real device it worked just fine with 60FPS. So if you are testing it on the simulator, check it also on a real device.