How to keep fps rate constant in sprite kit? - ios

Working on a game for iOS, using the sprite-kit framework to build it. The game runs smoothly for about the first minute in a half (maintaining the 60 fps rate), but as the player progresses into the game, the frame rate slowly starts to decrease. With time it even drops as low as 8 fps. I thought this was a result of the added debris and obstacles in the game, so I made an effort to remove a lot of them from the parent after time. This is how the game is set up:
There are 6 NSMutableArrays for the different types of debris that fall in the game. This is the format for each of them:
-(void)spawnDebris6 {
SKSpriteNode * debris6 = [SKSpriteNode spriteNodeWithTexture:[SKTexture textureWithImageNamed:#"debris6.png"] size:CGSizeMake(20, 20)];
debris6.zPosition = 1.0;
SKEmitterNode *debrisTrail = [SKEmitterNode kitty_emitterNamed:#"Dtrail"];
debrisTrail.zPosition = -1.0;
debrisTrail.targetNode = self;
[debris6 addChild:debrisTrail];
debris6.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:25];
debris6.physicsBody.allowsRotation = NO;
debris6.physicsBody.categoryBitMask = CollisionDebris;
//set up a random position for debris
//RandomPosition = arc4random() %248;
//RandomPosition = RandomPosition + 10;
debris6.position = CGPointMake (302, self.size.height + 65);
[_debris6 addObject:debris6];
[self addChild:debris6];
//next Spawn:
[self runAction:[SKAction sequence:#[
[SKAction waitForDuration:deb6Time],
[SKAction performSelector:#selector(spawnDebris6) onTarget:self],
]]];
if (_dead == YES) {
[self removeAllActions];
}
if (debris6.position.y > 568) {
[self removeFromParent];
}
}
Each of the NSMutableArrays appear over time - The first one appears at 0s, 2 # 10s, 3 # 40s, 4 #80s, etc. And when a new one appears I've added code to make former ones appear less frequently and also removed some (to lower the frame rate) yet I still notice a slower frame rate after about 90 seconds.
I don't see why this would be affecting the frame rate as I've minimised the particle birth rate, and life span, and made an effort to delete and slow down spawn rates of the debris over time. Why is the FPS rate slowly depreciating?
If more info is needed please let me know and I'll update this post.

I had a similar problem. It is probably the SKEmitterNodes. Continue to play around with them and lower birth rates and life spans, maybe even range etc. If the problem persists, see if you can take them out. It may not look as good, but sometimes you have to sacrifice looks for functionality. Also I'd recommend maybe having the debris appear at different times and heights, so you still have a full screen, but also a smooth running screen.

Related

SpriteKit physics giving different results each time

In my game I'm creating some balls which are effected by gravity. When game starts they fall from out of screen and get their places at the bottom. No other force is applied to them.
The problem is I'm creating these balls with exact coordinates, exact physics attributes like previous one but the result is not the same. It is similar but not same. But I think it should be exactly the same because in every time the values are same.
You can understand what I've said in these 3 pictures below.
Do you have any idea why this is happening? How can I solve it?
This is how I create the sprite nodes:
BallSpriteNode *sprite = [BallSpriteNode spriteNodeWithTexture:ballTexture];
sprite.xScale = scale;
sprite.yScale = scale;
sprite.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:sprite.size.width/2];
sprite.physicsBody.density = 1.0f;
sprite.physicsBody.restitution = 0;
sprite.physicsBody.dynamic = YES;
sprite.physicsBody.categoryBitMask = ballHitCategory;
sprite.physicsBody.contactTestBitMask = ballHitCategory;
sprite.physicsBody.collisionBitMask = ballHitCategory;
CGPoint startPosition = CGPointMake(xPosition, yPosition);
sprite.position = startPosition;
[bounceScene addChild:sprite];
There are lots of factors involved in making a physics engine less random.
The FPS greatly affects the randomness. In SKSpriteKit Physics, you have a Update method in the SKScene, which is called once every frame and carry the time difference from the previous frames. Usually to make it less random you have to somehow override the method.
Or use other Physics engine's which are customisable (Bullet Physics). It is very easy to get same result by only adjusting the StepSimulation function call.

Pause Sprite Kit game with physics doesn't work correctly on iOS 9

I have a problem in pausing a sprite kit game with physics. The game contains a ball which moves in the SpriteScene and has the following parameters:
self.ball.physicsBody.friction = 0;
self.ball.physicsBody.linearDamping = 0;
self.ball.physicsBody.restitution = 1.0f;
self.ball.physicsBody.affectedByGravity = NO;
self.ball.physicsBody.usesPreciseCollisionDetection = YES;
The problem is that when I pause the game, I call these methods:
self.scene.physicsWorld.speed = 0;
self.ball_velocity = self.ball.physicsBody.velocity;
self.ball.physicsBody.velocity = CGVectorMake(0, 0);
self.ball.speed = 0;
self.ball.physicsBody.dynamic = NO;
[self.scene.view setPaused:YES];
and when resume, call these:
self.scene.physicsWorld.speed = 1;
self.ball.physicsBody.velocity = self.ball_velocity;
self.ball.physicsBody.dynamic = YES;
self.ball.speed = 1;
[self.scene.view setPaused:NO];
This stops the ball animation, but when resume, the ball position is changed and it seems if that was moving during the pause duration.
BTW, it works fine on iOS 8 but on iOS 9 it always fails.
Any suggestions ?!!
After chatting, we have come to the conclusion that between iOS8 and iOS9, Apple has done a change that pausing the scene now pauses the update loop. Since the update loop is being paused, the change in time is not being calculated correctly. What is now happening, is the change in time will be the time at unpause - the time at pause, simulating a lag state. The velocity will take the math into effect, and move objects based on this difference in time. To combat this issue, just make a parent node that will house all of your scenes's objects, and pause the parent. This will allow the update to still be called, thus allowing the change in time to stay consistent with the frame rate.

SpriteKit game physics lagging

I have been working on a SpriteKit game for a while now and I have an annoying problem that I can't get rid of. I don't want to tell too much about the game, but it is very simple. I generate some objects (SKSpriteNodes) each second that are falling down from the top of the screen using SpriteKits physics and the player is interacting with them.
Most of the time the game runs perfectly (constant 60 FPS). The problem is that sometimes (maybe once per minute or something like that), the game starts to lag a little bit for about 3-5 seconds (still at 60 FPS) and then it runs perfectly again (note: i'm running the game on an iPhone 5s). It seems to be because of the physics, because if I add a normal move-action on an object, it runs very smoothly while the nodes affected by the physics are lagging.
I tried to remove some particles and effects that I have and I reuse my objects, but I can't remove the lag. I decided to create a very simple test project to see if the lag would be gone but it is still there. Here is the code:
#import "GameScene.h"
static const uint32_t groundCategory = 1 << 0;
static const uint32_t objectCategory = 1 << 1;
#implementation GameScene
-(void)didMoveToView:(SKView *)view
{
// Init
self.backgroundColor = [UIColor blackColor];
self.physicsWorld.gravity = CGVectorMake(0.0f, -3.2f);
self.physicsWorld.contactDelegate = self;
// Ground
SKPhysicsBody* ground = [SKPhysicsBody bodyWithEdgeFromPoint:CGPointMake(0, -88) toPoint:CGPointMake(self.size.width, -88)];
ground.categoryBitMask = groundCategory;
self.physicsBody = ground;
// Start game loop
SKAction* waitAction = [SKAction waitForDuration:1];
SKAction* sequence = [SKAction sequence:#[[SKAction performSelector:#selector(addObject) onTarget:self], waitAction]];
SKAction* repeatAction = [SKAction repeatActionForever:sequence];
[self runAction:repeatAction withKey:#"fallingObjectsAction"];
}
- (void)addObject
{
SKSpriteNode* newObject = [SKSpriteNode spriteNodeWithColor:[UIColor whiteColor] size:CGSizeMake(88, 88)];
newObject.name = #"Object";
newObject.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:newObject.size];
newObject.position = CGPointMake(CGRectGetMidX(self.frame), self.frame.size.height + newObject.size.height);
newObject.physicsBody.categoryBitMask = objectCategory;
newObject.physicsBody.contactTestBitMask = groundCategory;
newObject.physicsBody.collisionBitMask = 0;
[self addChild:newObject];
}
- (void)didBeginContact:(SKPhysicsContact *)contact
{
if ([contact.bodyA.node.name isEqualToString:#"Object"])
[contact.bodyA.node removeFromParent];
else if ([contact.bodyB.node.name isEqualToString:#"Object"])
[contact.bodyB.node removeFromParent];
}
#end
I generate one object each second and let it fall down from the top of the screen. When it hits the ground, it is removed.
Am I using the physics wrong or is it SpriteKit's fault that it is lagging? It seems strange, because I'm running a very simple project using an iPhone 5s with iOS 8.
Okay I've been having the same issue with physics. The issue was objects are jittery despite low CPU usage and a consistent FPS. I've finally figured out why. The solution I found is do not set physicsWorld.speed to 1.0; set it to a .9999. Now everything runs smoothly.
I hope this helps.

Cocos2d synchronise a sprite with another sprite's animation

I am trying to create a sprite animation of a character firing a gun in Cocos2d. I am struggling to find a way to instantiating and start moving the bullet sprite at the appropriate frame of the load/aim/fire animation of my character.
For example, my character goes through an 12 frame animation to fire his gun and the bullet should be released at frame 7.
Can anyone help?
Thanks in advance
Al
Here is one way of doing this, a similar scenario in a game i am developping. I want , on frame 10 of 16, to schedule an 'attackTurnAround' method (to animate damage animations, hurt on the victim, etc...). Dont waste time on 'frame notifications', sometimes a frame will be skipped, and you will end up with no notifications.
caveat : timing is key. Before getting at this point, i preload EVERYTHING that will happen in 'attackTurnAround' , namely the hurt (or dead) animation, the damage animation, the soundFx that will be heard when the sword hits on frame 10, etc .....
BattleAnimation *ba = [self attackerAttackAnination];
_attackerAttackSprite = ba.firstSprite;
[_attackerNode addChild:_attackerAttackSprite];
CCActionAnimate *anim = [CCActionAnimate actionWithAnimation:ba.animation];
id endAttack = [CCActionCallBlock actionWithBlock:^{
[self endAttack];
}];
id seq = [CCActionSequence actions:anim, endAttack, nil];
_attackerAttackSprite.visible = YES;
_attackerIdleSprite.visible = NO;
[_attackerAttackSprite runAction:seq];
[self scheduleOnce:#selector(attackTurnAround) delay:9. * ba.duration / 16.];

SpriteKit Texture Atlas vs Image xcassets

I am making a game and I noticed that during some scenes, my FPS kept dropping around the 55-60FPS area (using texture atlas). This drove me nuts so I decided to put all my assets to the Images.xcassets folder and voila, steady 60FPS.
I thought this was a fluke or that I was doing something wrong, so I decided to start a new project and perform some benchmarks...
Apple's documentation says that using texture atlas's will improve app performance. Basically, allowing your app to take advantage of batch rendering. However...
The Test (https://github.com/JRam13/JSGlitch):
- (void)runTest
{
SKAction *spawn = [SKAction runBlock:^{
for (int i=0; i<10; i++) {
SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithImageNamed:#"Spaceship"];
sprite.xScale = 0.5;
sprite.yScale = 0.5;
sprite.position = CGPointMake(0, [self randomNumberBetweenMin:0 andMax:768]);
SKAction *action = [SKAction rotateByAngle:M_PI duration:1];
[sprite runAction:[SKAction repeatActionForever:action]];
SKAction *move = [SKAction moveByX:1200 y:0 duration:2];
[sprite runAction:move];
//notice I don't remove from parent (see test2 below)
[self addChild:sprite];
}
}];
SKAction *wait = [SKAction waitForDuration:.1];
SKAction *sequence = [SKAction sequence:#[spawn,wait]];
SKAction *repeat = [SKAction repeatActionForever:sequence];
[self runAction:repeat];
}
Results:
Tests repeatedly show that using the xcassets performed way better than the atlas counterpart in FPS. The atlas does seem to manage memory marginally better than the xcassets though.
Anybody know why these results show that images.xcassets has better performance than the atlas?
Some hypotheses I've come up with:
xcassets is just better optimized than atlasas.
atlasas are good at drawing lots of images in one draw pass, but have bigger overhead with repeated sprites. If true, this means that if your sprite appears multiple times on screen (which was the case in my original game), it is better to remove it from the atlas.
atlas sheets must be filled in order to optimize performance
Update
For this next test I went ahead and removed the sprite from parent after it goes offscreen. I also used 7 different images. We should see a huge performance gain using atlas due to the draw count but...
First, revise your test to match this :
for (int i=0; i<10; i++) {
SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithImageNamed:#"Spaceship"];
sprite.xScale = 0.5;
sprite.yScale = 0.5;
float spawnY = arc4random() % 768;
sprite.position = CGPointMake(0, spawnY);
SKAction *action = [SKAction rotateByAngle:M_PI duration:1];
[sprite runAction:[SKAction repeatActionForever:action]];
SKAction *move = [SKAction moveByX:1200 y:0 duration:2];
// next three lines replace the runAction line for move
SKAction *remove = [SKAction removeFromParent];
SKAction *sequence = [SKAction sequence:#[move, remove]];
[sprite runAction:sequence];
[self addChild:sprite];
}
Rerun your tests and you should notice that your framerate NEVER deteriorates as in your tests. Your tests were basically illustrating what happens when you never remove nodes, but keep creating new ones.
Next, add the following line to your ViewController when you set up your skview :
skView.showsDrawCount = YES;
This will allow you to see the draw count and properly understand where you are getting your performance boost with SKTextureAtlas.
Now, instead of having just one image , gather 3 images and modify your test by choosing a random one of those images each time it creates a node, you can do it something like this :
NSArray *imageNames = #[#"image-0", #"image-1", #"image-2"];
NSString *imageName = imageNames[arc4random() % imageNames.count];
In your code, create your sprite with that imageName each time through the loop. ie :
SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithImageNamed:imageName];
In your SKTextureAtlas test, use that same imageName obviously to create each sprite.
Now... rerun your tests and take note of the draw count in each test.
This should give you a tangible example of what batch rendering is about with SKTextureAtlas.
It's no about rendering the same sprite image thousands of times.
It's about drawing many different sprites images in the same draw pass.
There is likely some overhead in getting this rendering optimization, but I think the draw count should be self explanatory as to why that overhead is moot when all things are considered.
Now, you can hypothesize some more :)
UPDATE
As mentioned in the comments, my post was to expose why your test was not a good test for the benefits of SKTextureAtlas and was flawed if looking to analyze it in a meaningful way. Your test was like testing for measles with a mumps test.
Below is a github project that I put together to pinpoint where an SKTextureAtlas is appropriate and indeed superior to xcassets.
atlas-comparison
Just run the project on your device and then tap to toggle between tests. You can tell when it's testing with SKTextureAtlas because the draw count will be 1 and the framerate will be 60fps.
I isolated what will be optimized with a SKTextureAtlas. It's a 60 frame animation and 1600 nodes playing that animation. I offset their start frames so that they all aren't on the same frame at the same time. I also kept everything uniform for both tests, so that it's a direct comparison.
It's not accurate for someone to think that using SKTextureAtlas will just optimize all rendering. It's optimization comes by reducing draw count via batch rendering. So, if your framerate slowdown is not something that can be improved via batch rendering, SKTexture atlas is the wrong tool for the job. right ?
Similar to pooling of objects, where you can gain optimization via not creating and killing your game objects constantly, but instead reusing them from a pool of objects. However if you are not constantly creating and killing objects in your game, pooling ain't gonna optimize your game.
Based on what I saw you describe as your game's issue in the discussion log , pooling is probably the right tool for the job in your case.

Resources