My question is about iOS development. I am writing a simple game in order to test the low level Core Graphics functions (contexts, layers, etc) and how to mix it with actual simple views... I explain it:
I have a simple sprite in the screen, it is animated with the UIImageView
properties, so the frame rate works perfectly.
After that, I added an infinite background that is being generated on the fly with bezier curves
. To animate this, I use a CADisplayLink
, calling a method "animate"
on the background, that moves my points on the bezier curve
, and then I call setNeedsDisplay
. It works "more or less" well, but if I remove the background (or the setNeedsDisplay
calling, so it does not get regenerated every frame) the frame rate increases a lot, so "anything" is not working correctly in this part...
I have also tried a double buffering system, but it works worse than my current code. I though about using a thread too, but I really don't find anything hard for CPU to do on background.
Here is the code:
//This is called automatically with CADDisplayLink.
- (void) animate:(CADisplayLink *)sender
{
//Check for deleting the first point.
float x = points[firstPoint].x;
if ( x < -2*DIST ) {
CGFloat lastX = points[lastPoint].x;
lastPoint = firstPoint;
firstPoint = ((firstPoint + 1) % (TOTAL_POINTS));
points[lastPoint] = CGPointMake(lastX + DIST, [self getNextY]);
}
//Move points
for(int i = 0; i < TOTAL_POINTS; i++) {
points[i].x -= 7;
}
//Mark as "needed to redraw"
[self setNeedsDisplay];
}
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 3.0);
CGContextSetRGBStrokeColor(context, 0, 0, 0, 1.0);
CGContextSetRGBFillColor(context, 1.0, 0, 0, 1.0);
CGContextSetLineCap(context, kCGLineCapRound);
//Create path
CGMutablePathRef path = CGPathCreateMutable();
//Draw the curve
[self drawBezierCurveInPath:path];
//Close and add path
CGPathAddLineToPoint(path, NULL, 485, -5);
CGPathAddLineToPoint(path, NULL, -5, -5);
CGPathCloseSubpath(path);
CGContextAddPath(context, path);
CGContextSetFillColorWithColor(context, bg2);
// [[UIColor redColor] setFill];
[[UIColor blackColor] setStroke];
CGContextDrawPath(context, kCGPathFillStroke);
//Release
CGPathRelease(path);
//Paint time.
playintIntervalView.text = [NSString stringWithFormat:@"%f", playingInterval];
}
- (void) drawBezierCurveInPath: (CGMutablePathRef) path
{
CGPoint fp = midPoint(points[firstPoint], points[(firstPoint+1)%TOTAL_POINTS]);
CGPathMoveToPoint(path, NULL, fp.x, fp.y);
int i = firstPoint;
for (int k = 0; k < TOTAL_POINTS-2; k++) {
previousPoint2 = points[i];
previousPoint1 = points[(i+1)%TOTAL_POINTS];
currentPoint = points[(i+2)%TOTAL_POINTS];
// calculate mid point
CGPoint mid2 = midPoint(currentPoint, previousPoint1);
CGPathAddQuadCurveToPoint(path, nil, previousPoint1.x, previousPoint1.y, mid2.x, mid2.y);
i = (i + 1) % TOTAL_POINTS;
}
}
I would prefer to store a UIBezierPath
(so I can then make some collision tests) but it doesn't allow you to just remove ONE point (when it leaves the screen on the left), so I would have to initialize it every frame too... Am I missing anything?
This is probably a bit late, but using CADisplayLink in this situation to animate is a bad idea. You should look into CoreAnimation for efficient animations of paths.
The issue here is that you're trying to redraw 60 times a second and the code in drawRect probably doesn't complete in the milliseconds that you have for that. By removing setNeedsDisplay, you're basically telling it not to update.