Is batching animation sprites more efficient with a single sprite? (cocos2d) - animation

In the cocos2d programming guide there is the following code:
CGSize s = [[CCDirector sharedDirector] winSize];
CCSprite *sprite = [CCSprite spriteWithSpriteFrameName:#"grossini_dance_01.png"];
sprite.position = ccp( s.width/2-80, s.height/2);
CCSpriteBatchNode *batchNode = [CCSpriteBatchNode batchNodeWithFile:#"animations/grossini.png"];
[batchNode addChild:sprite];
[self addChild:batchNode];
NSMutableArray *animFrames = [NSMutableArray array];
for(int i = 1; i < 15; i++) {
CCSpriteFrame *frame = [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:[NSString stringWithFormat:#"grossini_dance_%02d.png",i]];
[animFrames addObject:frame];
}
CCAnimation *animation = [CCAnimation animationWithName:#"dance" delay:0.2f frames:animFrames];
[sprite runAction:[CCRepeatForever actionWithAction: [CCAnimate actionWithAnimation:animation restoreOriginalFrame:NO] ]];
It adds a simple animation in the form of an array of frames and adds the sprite which will animate these frames into a CCSpriteBatchNode. My question is: will batch drawing a single animated sprite be any more efficient than not using batching at all? Since there is only one frame drawn at each draw and only one object, I would think not. The only benefit I think would be if you added more than one object - so that they could be drawn at their frame coordinates from the same texture in a single draw. Is my reasoning correct?

Thanks to replies here:
http://www.cocos2d-iphone.org/forum/topic/29354?replies=3#post-144515
At least one person has confirmed it has no benefit whatsoever with one object, but may reduce performance slightly from the added complexity.

Related

UIImageView frame origin after applying CAKeyframeAnimation

I have an UIImageView that I am moving around a circle with CGAffineTransformRotate. Works great! But when the user press a stop bottom I would like to the the actual x- / y- position of the UIImageView. So far I am always getting the original x- / y- values from when the UIImageView was created.
Is there a way to get the actual position, when the user stopped the rotation?
I have found the solution and share it in case someone is running a similar case:
From UIBezierPath I use the bounds information and this give me the position where the UIImageView stopped. Here the code:
UIBezierPath *path = [[UIBezierPath alloc] init];
[path addArcWithCenter:CGPointMake(iMiddleX, iMiddleY) radius:flR startAngle:degreesToRadians(flDegrees-0.01) endAngle:degreesToRadians(flDegrees) clockwise:YES];
CAKeyframeAnimation *pathAnimation = [CAKeyframeAnimation animationWithKeyPath:#"position"];
pathAnimation.calculationMode = kCAAnimationPaced;
pathAnimation.fillMode = kCAFillModeForwards;
pathAnimation.removedOnCompletion = NO;
pathAnimation.repeatCount = 1;
pathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault];
pathAnimation.duration = 1.0;
pathAnimation.path = path.CGPath;
NSInteger iX = path.bounds.origin.x;
NSInteger iY = path.bounds.origin.y;

rotate a sublayer in xcode

It's been a while, as I was hospitalized for 3 months after a motorcycle accident.
So I just got to renew my apple programming subscription :-)
I have another question that has been on my mind for quite some time.
In my iPad application I draw a triangle in the center of an iPad like this:
- (void)initTriangle
{
CGRect screenBound = [[UIScreen mainScreen] bounds];
CGSize screenSize = screenBound.size;
CGFloat screenWidth = screenSize.width;
CGFloat screenHeight = screenSize.height;
// draw triangle (TRIANGLE)
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path,NULL, 0.5*screenWidth, 0.5*screenHeight-25);
CGPathAddLineToPoint(path, NULL, 0.5*screenWidth-25, 0.5*screenHeight+25);
CGPathAddLineToPoint(path, NULL, 0.5*screenWidth+25, 0.5*screenHeight+25);
CGPathAddLineToPoint(path, NULL, 0.5*screenWidth, 0.5*screenHeight-25);
CAShapeLayer *triangle = [CAShapeLayer layer];
[triangle setPath:path];
[triangle setFillColor:[[UIColor blackColor] CGColor]];
[[[self view] layer] addSublayer:triangle];
CGPathRelease(path);
}
And I call this from my viewDidLoad like this:
[self initTriangle];
Now I'm trying to rotate this triangle with the rotation of my iPad around Z-Axis while laying flat on the table. I have a function that gives me the yaw readings in float and I'm calling my
-(void)updateTriangleWithYaw:(float)yaw
method, but I don't know what to exactly put in there to make it rotate.
here is what my method looks like so far:
-(void)updateTriangleWithYaw:(float)yaw
{
CGRect screenBound = [[UIScreen mainScreen] bounds];
CGSize screenSize = screenBound.size;
CGFloat screenWidth = screenSize.width;
CGFloat screenHeight = screenSize.height;
NSLog(#"YAW: %f", yaw);
Z += 2 * yaw;
Z *= 0.8;
CGFloat newR = R + 10 * yaw;
self.triangle.frame = CGRectMake(0.5*screenWidth, 0.5*screenHeight, newR, newR);
}
Any help will be greatly appreciated!
Thanks and be safe guys!!
You should set the layer's affineTransform. You can apply a rotation transform like:
[self.triangle setAffineTransform:CGAffineTransformMakeRotation(yaw)];
This method, setAffineTransform is a convenience to set the transform property of the layer, which is a more general type of transform CATransform3D. You can also set the transform of the layer directly, and if you want to do that you can make a rotation about the z-axis like:
self.triangle.transform = CATransform3DMakeRotation(yaw, 0, 0, 1);
In this case the first argument is the angle (in radians) and the last three arguments specify the axis of rotation.
Note that you should not assign or depend on the value of the frame property of a layer whose transform is not the identity (CGAffineTransformIdentity). When you use the transform property you should set the size and position of your layer by assigning the layer's center and bounds properties, and similarly you should read the center and bounds when you want to find out information about the layer's position and size.

how to get NSWindow size from kCGWindowNumber?

- (void) GetClientRect
NSArray *screenArray = [NSScreen screens];
NSScreen *mainScreen = [NSScreen mainScreen];
unsigned screenCount = [screenArray count];
unsigned index = 0;
for (index; index < screenCount; index++)
{
NSScreen *screen = [screenArray objectAtIndex: index];
NSRect screenRect = [screen visibleFrame];
NSString *mString = ((mainScreen == screen) ? #"Main" : #"not-main");
NSLog(#"Screen #%d (%#) Frame: %#", index, mString, NSStringFromRect(screenRect));
}
}
Above method is use to get frame for mainscreen.
But i want method which return Screen size of NSWindow whose kCGWindowNumber have been passed.
Any idea ?!
Why are you working with a CGWindowID (which is what I assume you meant by kCGWindowNumber)? That's a very unnatural thing to do in most circumstances.
Anyway, you can call CGWindowListCopyWindowInfo(0, theWindowID) and examine the dictionary in the first element of the returned array. Use the key kCGWindowBounds to get a dictionary representation of the bounds rectangle and then CGRectMakeWithDictionaryRepresentation() to convert that to a CGRect.
Are you looking for how to convert the NSWindow -frame, which is in Cocoa's coordinate system, to the Core Graphics coordinate system? The Cocoa coordinate system has its origin at the bottom-left of the primary display, with Y increasing in the up direction. The Core Graphics coordinate system has its origin at the top-left of the primary display, with Y increasing in the down direction.
So, the conversion looks like this:
NSWindow* window = /* ... comes from wherever ... */;
NSRect frame = window.frame;
frame.origin.y = NSMaxY([NSScreen.screens.firstObject frame]) - NSMaxY(frame);
CGRect cgbounds = NSRectToCGRect(frame);

drawRect performance

I need to draw lots of polygons 500k to a million on the iPad. After experimenting, I can only get only get 1 fps if that. This is just an example my real code has some good sized polygons.
Here are a few question:
Why don't I have to add the Quartz Framework to my project?
If many of the polygons repeat can I leverage that with views or are they too heavy etc?
Any alternatives, QTPaint can handle this but dips into the gpu. Is there is anything like QT or ios?
Can Opengl increase 2d performance of this type?
Example drawrect:
//X Y Array of boxes
- (void)drawRect:(CGRect)rect
{
int reset = [self pan].x;
int markX = reset;
int markY = [self pan].y;
CGContextRef context = UIGraphicsGetCurrentContext();
for(int i = 0; i < 1000; i++)//1,000,000
{
for(int j = 0; j < 1000; j++)
{
CGContextMoveToPoint(context, markX, markY);
CGContextAddLineToPoint(context, markX, markY + 10);
CGContextAddLineToPoint(context, markX + 10, markY + 10);
CGContextAddLineToPoint(context, markX + 10, markY);
CGContextAddLineToPoint(context, markX, markY);
CGContextStrokePath(context);
markX+=12;
}
markY += 12;
markX = reset;
}
}
The pan just move the array of boxes around on screen with pan gesture. Any help or hints would greatly appreciated.
The key issue with your example is that it is not optimized. Whenever drawRect: is called, the device is rendering all 1,000,000 squares. Worse still, it's making 6,000,000 calls to those APIs in the loop. If you want to refresh this view at even a modest 30fps, that is 180,000,000 calls / second.
With your 'simple' example, the size of the draw area is 12,000px × 12,000px; the maximum area you can display on the iPad's display is 768×1024 (assuming full-screen portrait). Therefore, the code is wasting a lot of CPU resources drawing outside the visible area. UIKit has ways of handling this scenario with relative ease.
When managing content that is significantly larger than the visible area, you should limit drawing to only that which is visible. UIKit has a couple of ways of handing this; UIScrollView in combination with a view backed by a CATiledLayer is your best bet.
Steps:
Disclaimer: This is specifically an optimization of your example code above
Create a new View Based Application iPad project
Add a reference to the QuartzCore.framework
Create a new class, say MyLargeView, subclassed from UIView and add the following code:
:
#import <QuartzCore/QuartzCore.h>
#implementation MyLargeView
- (void)awakeFromNib {
CATiledLayer *tiledLayer = (CATiledLayer *)[self layer];
tiledLayer.tileSize = CGSizeMake(512.0f, 512.0f);
}
// Set the layer's class to be CATiledLayer.
+ (Class)layerClass {
return [CATiledLayer class];
}
- (void)drawRect:(CGRect)rect {
// Drawing code
// only draws what is specified by the rect parameter
CGContextRef context = UIGraphicsGetCurrentContext();
// set up some constants for the objects being drawn
const CGFloat width = 10.0f; // width of rect
const CGFloat height = 10.0f; // height of rect
const CGFloat xSpace = 4.0f; // space between cells (horizontal)
const CGFloat ySpace = 4.0f; // space between cells (vertical)
const CGFloat tWidth = width + xSpace; // total width of cell
const CGFloat tHeight = height + ySpace;// total height of cell
CGFloat xStart = floorf(rect.origin.x / tWidth); // first visible cell (column)
CGFloat yStart = floorf(rect.origin.y / tHeight); // first visible cell (row)
CGFloat xCells = rect.size.width / tWidth + 1; // number of horizontal visible cells
CGFloat yCells = rect.size.height / tHeight + 1; // number of vertical visible cells
for(int x = xStart; x < (xStart + xCells); x++) {
for(int y = yStart; y < (yStart + yCells); y++) {
CGFloat xpos = x*tWidth;
CGFloat ypos = y*tHeight;
CGContextMoveToPoint(context, xpos, ypos);
CGContextAddLineToPoint(context, xpos, ypos + height);
CGContextAddLineToPoint(context, xpos + width, ypos + height);
CGContextAddLineToPoint(context, xpos + width, ypos);
CGContextAddLineToPoint(context, xpos, ypos);
CGContextStrokePath(context);
}
}
}
#end
Edit the view controller nib and add a UIScrollView to the view
Add a UIView to the UIScrollView and make sure it fills the UIScrollView
Change the class to MyLargeView
Set frame size of MyLargeView to 12,000×12,000
Finally, open up the view controller .m file and add the following override:
:
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
UIScrollView *scrollView = [self.view.subviews objectAtIndex:0];
scrollView.contentSize = CGSizeMake(12000, 12000);
}
If you look at the drawRect: call, it is only drawing into the area specified by the rect parameter, which will correspond to the tile size (512×512) for the CATiledLayer we configured in the awakeFromNib method. This will scale to a 1,000,000×1,000,000 pixel canvas.
Alternatives to look at are the ScrollViewSuite example, specifically 3_Tiling.
OpenGL is GPU hardware accelerated on iOS devices. Core Graphics drawing is not, and can be many many times slower when dealing with a large number of small graphics primitives (lines).
For lots of small squares, just writing them into a bitmap in C code is faster than Core Graphics line drawing. Then just draw the bitmap to the view once when done. But Open GL would be even faster.
point 4. OpenGL should do that fine. Check if you could reuse those objects and whether you could move some of the logic to GLSL code.
OpenGL performance optimization (in context of WebGL but most of it should apply): http://www.youtube.com/watch?v=rfQ8rKGTVlg
I don't know the details of iOS history so this may not have been an option when the question was first posted. However, I wanted to call out CAShapeLayer as a simple option when dealing with path performance problems. "iOS Core Animation: Advanced Techniques" (find it on Google Books) says CAShapeLayer "uses hardware-accelerated drawing" which I'm taking to mean that it's a GPU-based implementation. The same book has a good usage example in chapter 6, which boils down to this:
Create a CAShapeLayer
Configure its lineWidth, fillColor, strokeColor, etc.
Add the layer as a sublayer of your view's containerView.layer
To draw a path, just set it to the layer's "path" property
This made a gigantic performance difference in my app, as measured by Instruments. If your performance problem is path-based, don't wade into OpenGL before you've tried CAShapeLayer.
I encountered the same problem. After endless searching on google,CAShapeLayer saved me finally! Here is the detail steps you need to do:
Create a view with CAShapeLayer as it's layer type by override UIView's + (Class)layerClass method
Configure the layer's lineWidth, fillColor, strokeColor, etc.
Create an UIBezierPath instance
To draw a path,use UIBezierPath instance to add lines,curve,or acr etc, after you finished drawing, just set bezierPath.CGPath to the
layer's "path" property
Here is a simple demo to draw a simple curve when you touch the demo view:
//Simple ShapelayerView.m
-(instancetype)init {
self = [super init];
if (self) {
_bezierPath = [UIBezierPath bezierPath];
CAShapeLayer *shapeLayer = (CAShapeLayer *)self.layer;
shapeLayer.lineWidth = 5;
shapeLayer.lineJoin = kCALineJoinRound;
shapeLayer.lineCap = kCALineCapRound;
shapeLayer.strokeColor = [UIColor yellowColor].CGColor;
shapeLayer.fillColor = [UIColor blueColor].CGColor;
}
return self;
}
+ (Class)layerClass {
return [CAShapeLayer class];
}
- (void) customDrawShape {
CAShapeLayer *shapeLayer = (CAShapeLayer *)self.layer;
[_bezierPath removeAllPoints];
[_bezierPath moveToPoint:CGPointMake(10, 10)];
[_bezierPath addQuadCurveToPoint:CGPointMake(2, 2) controlPoint:CGPointMake(50, 50)];
shapeLayer.path = _bezierPath.CGPath;
}
- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
[super touchesBegan:touches withEvent:event];
[self customDrawShape];
}

cocos2d screen size problem in landscape mode

I have such a problem in my cocos2d application in landscape mode:
When I'm adding a new object inherited from CCNode and add it to layer and after that layer to screen. The maximum position must be 480 on X and 320 on Y in landscape mode. But my object is in position 480 on X when it coordinates are on (220, 0). Anybody know how to solve this problem?? Thanx!
- (void) applicationDidFinishLaunching:(UIApplication*)application
{
CCScene *scene = [CCScene node];
CCLayer *layer = [CCLayer node];
//layer.anchorPoint = ccp(1, 1);
//layer.contentSize = CGSizeMake(480, 320);
CCSprite *sp = [CCSprite spriteWithFile:#"fon.png"];
[layer addChild: sp];
[scene addChild: layer];
[[CCDirector sharedDirector] runWithScene: scene];
}
I think you are misunderstanding the co-ordinates.
x and y are the same on the phone, regardless of orientation.
When the phone is on its side, x = y and y = x.
Try this
player1 = [[Player alloc] initWithPosition: CGPointMake(20, 200) )];

Resources