Extremely slow framerate in SpriteKit game - ios

I have a game built in SpriteKit that I occasionally work on. I started working on it when iOS 8 was still the latest version of iOS. Back then, it always ran at 60fps or almost (on a physical device). However, since iOS 9, and now on iOS 10, the game runs at a measly 12fps on my iPhone 5. It actually usually runs faster on the simulator (13 - 14fps), which is unheard of.
I did an analysis with Instruments and it appears that some of the slowness comes from the app doing two enumerations every frame. Here they are:
-(void)moveBackground
{
[self enumerateChildNodesWithName:#"stars" usingBlock:^(SKNode *node, BOOL *stop)
{
SKSpriteNode *bg = (SKSpriteNode *)node;
CGPoint bgVelocity = CGPointMake(0, -150); //The speed at which the background image will move
CGPoint amountToMove = CGPointMultiplyScalar (bgVelocity,0.08);
bg.position = CGPointAdd(bg.position,amountToMove);
if (bg.position.y <= 0)
{
bg.position = CGPointMake(bg.position.x, bg.position.y + bg.size.height/2);
}
}];
[self enumerateChildNodesWithName:#"opbg" usingBlock:^(SKNode *node, BOOL *stop)
{
SKSpriteNode *opbg = (SKSpriteNode *)node;
CGPoint bgVelocity = CGPointMake(0, -150); //The speed at which the background image will move
CGPoint amountToMove = CGPointMultiplyScalar (bgVelocity,0.08);
opbg.position = CGPointAdd(opbg.position,amountToMove);
if (opbg.position.y <= 0)
{
[self.opbg removeFromParent];
}
}];
}
These enumerations move the starting background until it is offscreen, then remove it from the parent, and cycle the regular background once that one is done. Is there a more efficient way of doing this? Additionally, is there another way of optimizing the app without losing any quality? My images are relatively large (background is 1080x3840), but I'm not sure if I can use smaller ones and then upscale them without losing quality.
Any help improving my framerate is appreciated, and I can show more code if needed.
Thanks

The biggest change between iOS 8 and 9 was the addition of a cameranode:
https://developer.apple.com/reference/spritekit/skcameranode
And an infamous amount of performance degradation.
There's a litany of Apple forum complaints about performance on iOS 9. There was no communication from Apple in a timely and satisfactory manner. Many SpriteKit users and potential users were lost as a result of the lack of meaningful fixes and explanations.
In your case, switching to use of the camera to do your parallaxes might regain some performance.
Here's some of the issues regarding performance being discussed, so you might see if anyone was doing something similar:
https://forums.developer.apple.com/thread/14487
https://forums.developer.apple.com/thread/30651

Related

Strange Wobble effect on iPhone 6 Plus simulator - UIDynamics

Video link here: (low quality) where you can see everything wobbling around. https://drive.google.com/file/d/0B6SrhxQY65faS3pfaGF0bXBiVXFncWU0aFdsVWFpUXEwaXJ3/edit?usp=sharing
Fix mentioned below doesn't work when in native (non scaling mode), so not really fix.
[Update] So setting a one pixel section inset on the collection view (I had zero left and right before), makes the weirdness go away, it's really strange, why does this only happen on the iPhone 6 Plus? I don't like how it looks with the single pixel inset so will leave the question open in case someone knows what might be going on.
[Original Question] I am using UIAttachmentBehaviors in my UICollectionView that provide a stretchy feel when browsing the collection. This works fine everywhere, except in the iPhone 6 Plus simulator (on XCode 6.0.1). On iPhone 6 Plus, the collection items rotate and wobble around (when they should only be moving on the Y axis. They continue to move for about a minute and then very slowly settle down, while the actual animation is only supposed to last for a fraction of a second. They show clear X axis movement. Has anyone else noticed similar weirdness with the iPhone 6 plus? I'm wondering if this is a simulator bug or a real issue but don't have an iPhone 6 Plus to test on. It works fine on the iPhone 6 simulator.
My code looks like this, and I can't see how this could cause X coordinate changes:
-(BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{
UIScrollView *scrollView = self.collectionView;
CGFloat delta = newBounds.origin.y - scrollView.bounds.origin.y;
CGPoint touchLocation = [self.collectionView.panGestureRecognizer locationInView:self.collectionView];
[self.dynamicAnimator.behaviors enumerateObjectsUsingBlock:^(UIAttachmentBehavior *springBehaviour, NSUInteger idx, BOOL *stop) {
CGFloat yDistanceFromTouch = fabsf(touchLocation.y - springBehaviour.anchorPoint.y);
CGFloat xDistanceFromTouch = fabsf(touchLocation.x - springBehaviour.anchorPoint.x);
CGFloat scrollResistance = (yDistanceFromTouch + xDistanceFromTouch) / 1500.0f;
UICollectionViewLayoutAttributes *item = springBehaviour.items.firstObject;
CGPoint center = item.center;
if (delta < 0) {
center.y += MAX(delta, delta*scrollResistance);
}
else {
center.y += MIN(delta, delta*scrollResistance);
}
item.center = center;
[self.dynamicAnimator updateItemUsingCurrentState:item];
}];
return NO;
}
I have the same problem when trying to use UICollectionView with UIKit Dynamics.
Problem seems to appears when you compute size of items programmatically to fit width of screen, so in this case items interfere with each other while dynamics applies and cannot get to equilibrium. (so this is like corner case when items actually overlaps)
In my case solution is simple - I just decrease size of items to be around 90% of computed value. It will impose some gap between items, but this is doesn't matter in my case.

How to keep fps rate constant in sprite kit?

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.

Cocos2D, animated sprites blur

I've returned to using Cocos2d after an 18 month gap. My initial source for guidance has been the learn Cocos2d book by Steffen Itterheim and Andreas Low.
I have to say I've found the guide rather intuitive to follow but have hit a couple of issues.
The first is the animation of the sprites. I am without a doubt using the HD sprites correctly (this has been proved by renaming the HD sprite and getting warnings that the HD sprite atlas isn't available).
When the sprites animate, they blur. At first I thought this was a simulator issue, however, while testing on my iPhone 5 and my partners iPhone 4, the same blurring is experienced. The method I have used for updating the graphics is as follows: -
-(void) update:(ccTime)delta
{
if (self.parent.visible)
{
CGSize screenSize = [CCDirector sharedDirector].winSize;
if (self.parent.position.x > - 50)//screenSize.width * 0.5f)
{
self.parent.position = ccpAdd(self.parent.position, ccpMult(velocity, delta));
}
else
{
self.parent.visible = NO; // We've gone off screen so set to invisible.
}
}
}
This is invoked with the following method : -
-(id) initWithRate:(int)rate
{
if ((self = [super init]))
{
velocity = CGPointMake(rate, 0); //-100
[self scheduleUpdate];
}
return self;
}
This is used within a "StandardMoveComponent" class, which is added to the sprite node. I should add, the graphics all come from 2 sprite sheets. They're in separate batch notes, but added to the same frameCache.
If anyone out there has any suggestions what I can do to prevent the blurring, I'd be very grateful.

cocos2d 2.0+ Scale Bug || Performance drop || Endless memory allocation

Has someone of you ever noticed that scaling a sprite up to more than 100% can cause the render time per frame to increase and never go down again?
I've set up a test project based on a cocos2d 2.0 template (I also tested it with 2.1 and it also does happen). When touching the screen (testing with an iPad 3) it creates 100 sprites that are scaled by 1.5
When there are 5000 it removes them all. When they get removed the render time very often stays at 0.016. By double tapping the home button (causing some interrupt to happen) you can make it go back to 0.001 (when there are no sprites).
I did a lot of testing when and how to cause this and I came to the conclusion that it only happens when scaling something.
While in 0.016 "mode" there is a constant memory increase (look at the allocations tool) and as soon as you double tap the home button it goes down again slowly. These allocations come from the gyroscope / accelerometer code that I
put in there. When in 0.001 "mode" it works fine but as soon as the render time shows 0.016 memory gets allocated endlessly and it is never freed.
Especially with really big scalings this thing can be achieved easily. Does anyone have an idea how to fix this?
This does happen when touching the screen in my test project. You can download it here:
https://dl.dropboxusercontent.com/u/40859730/RenderTimeIssue.zip
static int spriteCount = 0;
- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
for (int i = 0; i < 100; i++) {
CCSprite *icon = [CCSprite spriteWithFile: #"Icon.png"];
[icon setPosition: ccp(arc4random() % 1000, arc4random() % 1000)];
[self addChild: icon];
icon.tag = spriteCount;
// this line causes the "0.016 bug", comment it out and the frame rate does go back to 0.001 when 5000 sprites are reached
icon.scale = 1.5f; // while scaling up more than 1.0 causes the problem, scaling down does not
spriteCount++;
}
if (spriteCount >= 5000) {
for (int i = 0; i < spriteCount; i++) {
[self removeChildByTag: i cleanup: YES];
}
spriteCount = 0;
}
return YES;
}
EDIT: Here are some images that show the memory increase:
http://imgur.com/a/Jy70a
I've experienced this problem on cocos2d 2.0.
But when i've upgraded to 2.1, this dont happen anymore.
Try to upgrade cocos2d to 2.1 version. There's a lot of performance improvements on rendering images.

Line drawing with Cocos2D for iPhone

I'm making a simple game using Cocos2D. The user can touch and trace a path for small moving objects. The objects then follow the path. This is similar to the Flight Control game mechanic.
My code works well almost all the time. However sometimes I attempt to touch and drag an object but the touch callbacks don't fire. Here's how my code works in general:
small moving objects inherit CCLayer
I'm using the touch callbacks that have a single touch parameter (ccTouchMoved:withEvent: etc)
all callbacks like ccTouchBegan:withEvent: are on the objects (instead of the main game layer)
How can I improve the touch handling such that the error noted above is avoided?
Here's my thoughts so far: Perhaps the touch callbacks are not firing because the objects (which are always moving) move slightly just around the time the user attempts to touch the object. This could be fixed by handling the touches on the main game layer using callbacks that have collections of touches as parameters. Since the total object count is relatively small (say less than 50) I can just compare each touch location with each object.
I also tried increasing the touchable area for the objects (note the 1.5 multiplication). This seemed to help but did not eliminate the problem.
- (BOOL) ccTouchBegan:(UITouch *) touch withEvent:(UIEvent *) event {
CGSize size = self.contentSize;
CGRect rect = CGRectMake(-size.width / 2.0,
-size.height / 2.0,
size.width * 1.5,
size.height * 1.5);
return CGRectContainsPoint(rect, [self convertTouchToNodeSpace:touch]);
}
Another thing to try would be increasing the speed of the objects and see if the callbacks don't fire more often. At the moment the objects move slowly.
Any help is much appreciated!
IIRC, node's origin is in its bottom left corner. Try
CGRect rect = CGRectMake(0, 0, size.width, size.height);
return CGRectContainsPoint(rect, [self convertTouchToNodeSpace:touch]);

Resources