views:

1749

answers:

4

I've got a very simplistic "game" set up on the iPhone. Things move around, rotate, you can interact with them, etc. In the simulator, it runs fine, but on the device it gets around 0.25 FPS, and it's so slow that it doesn't have time to recognize touches, apparently.

This was originally just using a UIView with an array of Items each of which had a Tick and a Draw function. At the beginning of the app, I started a Timer that shoots off at 60 FPS, which calls Tick, and at the end of Tick, it calls [self setNeedsDisplay];

It was working so horribly slow, so I turned the Item class into a subclass of the CALayer class, moving the layer around instead of redrawing the items. However, I was moving the CALayer around manually, frame-by-frame, and this, apparently, is no good. It was still way too slow. So I told it to move to its final destination and gave it a length equal to the number of ticks it would take to get there.

That worked okay, but there are cases when, if you interact with something, it should stop moving that way and do something else instead. So I moved back to moving every tick, and instead of just setting the new position, I also wrapped it in a CATransaction and gave the transaction the same length as the tick.

Works OK, but new problem arises: If I want to animate something, I still have to use [self setNeedsDisplay] every tick, which calls drawInContext:, and this seems to be a major slow down issue.

Isn't there just a way to manually draw everything like I was doing before? I was just making CGContext* calls, and it worked fine in the simulator! It would work fine if I implemented something similar on J2ME or in embedded C++. What am I missing? Do I really need to learn OpenGL to do any sort of interesting manual animation?

+2  A: 

Not sure about the CG animation layer but OpenGLES is the iPhone's answer to high performance graphics. At the end of the day you will get better performance that way. The openGLES can be a pain to work with but if you start with working code (see crashlanding app's texture and view classes) you should be able to abstract enough of what you need out of/around it.

In all honesty I wasnt too happy about having to use openGLES just to draw some 2D sprites but I'm getting a around 50fps with a full physics engine and scripting layer so I guess it was worth it.

Nick
A: 

It sounds like it would be a bit of a re-write unfortunately, but have you looked at the Cocos2d engine? If all you are looking to do is animate a series of sprites around the screen then they have you covered with nice Objective-C wrappers around all of the OpenGL calls.

http://code.google.com/p/cocos2d-iphone/

Lounges
+1  A: 

If all you need to do is animate something moving from point A to point B, UIView and CALayer are more than able to handle this. You really, really don't want to be manually changing the position of the sprites at 60 FPS using Core Graphics, UIViews, or Core Animation on the iPhone.

For simple movement using a CALayer, you should be able to do the following:

[CATransaction begin];
[CATransaction setValue:[NSNumber numberWithFloat:0.5f] forKey:kCATransactionAnimationDuration];

CGSize spriteSize = currentSprite.frame.size;
currentSprite.frame = CGRectMake(newPosition.x, newPosition.y, spriteSize.width, spriteSize.height);

[CATransaction commit];

with currentSprite being your CALayer and newPosition being the end point of the entire linear move. This will smoothly animate from the current location to the new position using a constant velocity.

With Core Animation, you just want to specify the final location of your layer and let it do the work of interpolating the in-between frames. If you interrupt this animation by giving it another animation to perform on the same layer, it will even smoothly transition from moving to the one location to moving to the other. More complex animations can be designed using CAKeyframeAnimations. Again, just give Core Animation the starting and ending states and let it optimize the rest.

With UIViews, which are lightweight wrappers around CALayers, I'm getting at least 30 FPS on the device with dozens of translucent layers moving around at once.

Brad Larson
A: 

I found out the real issue was the necessity to call [self setNeedsDisplay]. Redrawing on a frame-by-frame basis is bad mojo. So I am using images instead for the most part, and I can still get away with setting the position on a frame-by-frame basis by setting the actions on the CALayer to null. Specifically:

NSMutableDictionary *customActions=[NSMutableDictionary dictionaryWithDictionary:[self actions]];
[customActions setObject:[NSNull null] forKey:@"position"];
[customActions setObject:[NSNull null] forKey:@"transform"];
[customActions setObject:[NSNull null] forKey:@"opacity"];
self.actions=customActions;

After that call, everything is much faster when calculating frame-by-frame.

Ed Marty