cocos2d create a circle shape and use it as a sprite - xcode

so I would like to create a circle shape with primitives on cocos2d and then use it as a sprite, How can I do it please ?
I know that I have to use something like this :
glLineWidth(16);
glColor4ub(0, 255, 0, 255);
drawCircle( ccp(s.width/2, s.height/2), 100, 0, 10, NO);
But it's hard for me to understand how it works and How to use it as a sprite

do you really need CCSprite instance? you can create a subclass of CCNode, then in its
- (void) draw
method put your code there. your circle will have it's center position (0.f, 0.f)
#implementation MyScene
- (void) onEnter
{
[super onEnter];
CCNode* myNode = [MyNodeSubclass node];
[node setPosition: someRandomPosition ];
[self addChild: node];
}
#end
#implementation MyNodeSubclass
- (void) draw
{
glColor4f(255, 255, 255, 255);
CCPoint center = ccp(0.f, 0.f);
CGFloat radius = 10.f;
CGFloat angle = 0.f;
NSInteger segments = 10;
BOOL drawLineToCenter = YES;
ccDrawCircle(center, radius, angle, segments, drawLineToCenter);
}
#end
wrote this piece of code right here, didn't copy from xcode, but it should work as you want. ccDrawCircle is a cocos2d function, declared in CCDrawingPrimitives.h

Related

CoreGraphics drawRect in a cocos2d project EXEC BAD ACCESS

I got a interesting riddle here. My cocos2d project uses a UIView to display nicer popups than the regular alert view. To be more flexible in the size of the popup, I draw the background in the drawRect method.
But first the hierarchy how I implement cocos2d and UIKit:
Every UIKit element is added to the RootViewController. Every CCNode to the EAGLView. (If someone has a better solution to mix those world, don't wait to tell me!) Unfortunately is every UIKit view in front of cocos nodes.
Everything works fine when I add the first popup to the RootViewController. But if I remove the first popup and add a new one to the RootViewController occurs a bad access.
It crashes only in combination with cocos2d.
I use the code from Ray Wenderlichs CoreGraphics 101 tutorial.
context and strokeColor are not nil.
Another important infos: I use ARC and am supporting iOS 4.2 and above.
The complete code can be found at raywenderlich.com or below
void drawLinearGradient(CGContextRef context, CGRect rect, CGColorRef startColor, CGColorRef endColor)
{
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGFloat locations[] = { 0.0, 1.0 };
NSArray *colors = [NSArray arrayWithObjects:(__bridge id)startColor, (__bridge id)endColor, nil];
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace,
(__bridge CFArrayRef) colors, locations);
CGPoint startPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect));
CGPoint endPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMaxY(rect));
CGContextSaveGState(context);
CGContextAddRect(context, rect);
CGContextClip(context);
CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);
CGContextRestoreGState(context);
CGGradientRelease(gradient);
CGColorSpaceRelease(colorSpace);
}
CGRect rectFor1PxStroke(CGRect rect)
{
return CGRectMake(rect.origin.x + 0.5, rect.origin.y + 0.5, rect.size.width - 1, rect.size.height -1);
}
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGRect frame = CGRectInset(self.bounds, 2, 2);
CGContextSetShadowWithColor(context, CGSizeMake(0, 0), 3.0, shadowColor);
CGRect strokeRect = CGRectInset(frame, -2.0, -2.0);
CGContextSetStrokeColorWithColor(context, strokeColor);
CGContextSetLineWidth(context, 2.0);
CGContextStrokeRect(context, rectFor1PxStroke(strokeRect));
drawLinearGradient(context, frame, gradient1, gradient2);
}
ARC releases the CGColorRef named strokeColor. One fix is to replace it with an CGFloat array and use CGContextSetStrokeColor instead CGContextSetStrokeColorWithColor
This answer solves this issue:
App crashes when using __bridge for CoreGraphics gradient on ARC
I agree with zeiteisen ,ARC releases CGColorRef and the easiest way to solve this by
replacing CGContextSetStrokeColorWithColor with
CGContextSetStrokeColor(context, components)
Change UIColor of strokeColor to 'RGB CGFloat components array' as following:
static CGFloat red, green, blue, alpha;
- (void)getRGBComponents:(CGFloat [4])components forColor:(UIColor *)color {
[color getRed:&red green:&green blue:&blue alpha:&alpha];//To fetch CGFloat RGB components
for (int component = 0; component < 4; component++) {
switch (component) {
case 0:
components[component] = red;
break;
case 1:
components[component] = green;
break;
case 2:
components[component] = blue;
break;
case 3:
components[component] = alpha;
break;
default:
break;
}
}
}
Use this method like this:
CGFloat components[4];
UIColor *strokeColor=[UIColor greyColor];//My color
[self getRGBComponents:components forColor:strokeColor];//'components' will be substituted in CGContextSetStrokeColor
NSLog(#"%f %f %f %f", components[0], components[1], components[2],components[3]);

Creating an animatable translucent overlay with Core Animation layers

I'm creating a spotlight that moves over content in my app, like so:
In the sample app (shown above), the background layer is blue, and I have a layer over it that darkens all of it, except a circle that shows it normally. I've got this working (you can see how in the code below). In my real app, there is actual content in other CALayers, rather than just blue.
Here's my problem: it doesn't animate. I'm using CGContext drawing to create the circle (which is an empty spot in an otherwise black layer). When you click the button in my sample app, I draw the circle at a different size in a different location.
I would like that to smoothly translate and scale, instead of jumping, as it currently does. It may require a different method of creating the spotlight effect, or there might be a way I don't know of to implicitly animate the -drawLayer:inContext: call.
It's easy to create the sample app:
Make a new Cocoa app (using ARC)
Add the Quartz framework
Drop a custom view and a button onto the XIB
Link the custom view to a new class (SpotlightView), with code provided below
Delete SpotlightView.h, since I included its contents in SpotlightView.m
Set the button's outlet to the -moveSpotlight: action
Update (the mask property)
I like David Rönnqvist's suggestion in comments to use the mask property of the darkened layer to cut out a hole, which I could then move independently. The problem is that for some reason, the mask property works the opposite of how I expect a mask to work. When I specify a circular mask, all that shows up is the circle. I expected the mask to work in the opposite manner, masking out the area with 0 alpha.
Masking feels like the right way to go about this, but if I have to fill in the entire layer and cut out a hole, then I may as well do it the way I originally posted. Does anyone know how to invert the -[CALayer mask] property, so that the area drawn in gets cut out from the layer's image?
/Update
Here's the code for SpotlightView:
//
// SpotlightView.m
//
#import <Quartz/Quartz.h>
#interface SpotlightView : NSView
- (IBAction)moveSpotlight:(id)sender;
#end
#interface SpotlightView ()
#property (strong) CALayer *spotlightLayer;
#property (assign) CGRect highlightRect;
#end
#implementation SpotlightView
#synthesize spotlightLayer;
#synthesize highlightRect;
- (id)initWithFrame:(NSRect)frame {
if ((self = [super initWithFrame:frame])) {
self.wantsLayer = YES;
self.highlightRect = CGRectNull;
self.spotlightLayer = [CALayer layer];
self.spotlightLayer.frame = CGRectInset(self.layer.bounds, -50, -50);
self.spotlightLayer.autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable;
self.spotlightLayer.opacity = 0.60;
self.spotlightLayer.delegate = self;
CIFilter *blurFilter = [CIFilter filterWithName:#"CIGaussianBlur"];
[blurFilter setValue:[NSNumber numberWithFloat:5.0]
forKey:#"inputRadius"];
self.spotlightLayer.filters = [NSArray arrayWithObject:blurFilter];
[self.layer addSublayer:self.spotlightLayer];
}
return self;
}
- (void)drawRect:(NSRect)dirtyRect {}
- (void)moveSpotlight:(id)sender {
[self.spotlightLayer setNeedsDisplay];
}
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx {
if (layer == self.spotlightLayer) {
CGContextSaveGState(ctx);
CGColorRef blackColor = CGColorCreateGenericGray(0.0, 1.0);
CGContextSetFillColorWithColor(ctx, blackColor);
CGColorRelease(blackColor);
CGContextClearRect(ctx, layer.bounds);
CGContextFillRect(ctx, layer.bounds);
// Causes the toggling
if (CGRectIsNull(self.highlightRect) || self.highlightRect.origin.x != 25) {
self.highlightRect = CGRectMake(25, 25, 100, 100);
} else {
self.highlightRect = CGRectMake(NSMaxX(self.layer.bounds) - 50,
NSMaxY(self.layer.bounds) - 50,
25, 25);
}
CGRect drawnRect = [layer convertRect:self.highlightRect
fromLayer:self.layer];
CGMutablePathRef highlightPath = CGPathCreateMutable();
CGPathAddEllipseInRect(highlightPath, NULL, drawnRect);
CGContextAddPath(ctx, highlightPath);
CGContextSetBlendMode(ctx, kCGBlendModeClear);
CGContextFillPath(ctx);
CGPathRelease(highlightPath);
CGContextRestoreGState(ctx);
}
else {
CGColorRef blueColor = CGColorCreateGenericRGB(0, 0, 1.0, 1.0);
CGContextSetFillColorWithColor(ctx, blueColor);
CGContextFillRect(ctx, layer.bounds);
CGColorRelease(blueColor);
}
}
#end
I finally got it. What prodded me to the answer was hearing of the CAShapeLayer class. At first, I thought it would be a simpler way to draw the layer's contents, rather than drawing and clearing the contents of a standard CALayer. But I read the documentation of the path property of CAShapeLayer, which stated it could be animated, but not implicitly.
While a layer mask might have been more intuitive and elegant, it doesn't seem to be possible to use the mask to hide a portion of the owner's layer, rather than showing a portion, and so I couldn't use it. I'm happy with this solution, as it's pretty clear what's going on. I wish it used implicit animation, but the animation code is only a few lines.
Below, I've modified the sample code from the question to add smooth animation. (I removed the CIFilter code, because it was extraneous. The solution does still work with filters.)
//
// SpotlightView.m
//
#import <Quartz/Quartz.h>
#interface SpotlightView : NSView
- (IBAction)moveSpotlight:(id)sender;
#end
#interface SpotlightView ()
#property (strong) CAShapeLayer *spotlightLayer;
#property (assign) CGRect highlightRect;
#end
#implementation SpotlightView
#synthesize spotlightLayer;
#synthesize highlightRect;
- (id)initWithFrame:(NSRect)frame {
if ((self = [super initWithFrame:frame])) {
self.wantsLayer = YES;
self.highlightRect = CGRectNull;
self.spotlightLayer = [CAShapeLayer layer];
self.spotlightLayer.frame = self.layer.bounds;
self.spotlightLayer.autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable;
self.spotlightLayer.fillRule = kCAFillRuleEvenOdd;
CGColorRef blackoutColor = CGColorCreateGenericGray(0.0, 0.60);
self.spotlightLayer.fillColor = blackoutColor;
CGColorRelease(blackoutColor);
[self.layer addSublayer:self.spotlightLayer];
}
return self;
}
- (void)drawRect:(NSRect)dirtyRect {}
- (CGPathRef)newSpotlightPathInRect:(CGRect)containerRect
withHighlight:(CGRect)spotlightRect {
CGMutablePathRef shape = CGPathCreateMutable();
CGPathAddRect(shape, NULL, containerRect);
if (!CGRectIsNull(spotlightRect)) {
CGPathAddEllipseInRect(shape, NULL, spotlightRect);
}
return shape;
}
- (void)moveSpotlight {
CGPathRef toShape = [self newSpotlightPathInRect:self.spotlightLayer.bounds
withHighlight:self.highlightRect];
CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:#"path"];
pathAnimation.fromValue = (__bridge id)self.spotlightLayer.path;
pathAnimation.toValue = (__bridge id)toShape;
[self.spotlightLayer addAnimation:pathAnimation forKey:#"path"];
self.spotlightLayer.path = toShape;
CGPathRelease(toShape);
}
- (void)moveSpotlight:(id)sender {
if (CGRectIsNull(self.highlightRect) || self.highlightRect.origin.x != 25) {
self.highlightRect = CGRectMake(25, 25, 100, 100);
} else {
self.highlightRect = CGRectMake(NSMaxX(self.layer.bounds) - 50,
NSMaxY(self.layer.bounds) - 50,
25, 25);
}
[self moveSpotlight];
}
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx {
CGColorRef blueColor = CGColorCreateGenericRGB(0, 0, 1.0, 1.0);
CGContextSetFillColorWithColor(ctx, blueColor);
CGContextFillRect(ctx, layer.bounds);
CGColorRelease(blueColor);
}
#end

How would I get a UIImageView to always face up based off the accelerometer?

I would like to make it so when the user rotates the device (to any angle, not just landscape/portrait) the UIImageView would always be facing upwards. How would I do this?
Thanks in advance
#interface FirstViewController : UIViewController <UIAccelerometerDelegate> {
IBOutlet UIImageView *imageView;
CGPoint delta;
CGPoint translation;
float ballRadius;
}
//implementation
- (void)viewDidLoad
{
[super viewDidLoad];
// for the line in the middle of the camera
UIAccelerometer *accel = [UIAccelerometer sharedAccelerometer];
accel.delegate = self;
accel.updateInterval = 1.0f/60.0f;
}
- (void)accelerometer:(UIAccelerometer *)acel
didAccelerate:(UIAcceleration *)acceleration {
// Get the current device angle
float xx = -[acceleration x];
float yy = [acceleration y];
float angle = atan2(yy, xx);
// Add 1.5 to the angle to keep the image constantly horizontal to the viewer.
[imageView setTransform:CGAffineTransformMakeRotation(angle+1.5)];
}

Cocoa NSView: Making circles but they are getting cropped

the outer edges of my circle at each point of the compass are getting cropped (presumably by the rect frame). How do I get the circle to display within the frame? (This is getting created from a button click):
In my AppController.m
#import "AppController.h"
#import "MakeCircle.h"
#implementation AppController
- (IBAction)makeCircle:(id)sender {
MakeCircle* newCircle = [[MakeCircle alloc] initWithFrame:NSMakeRect(100.0, 100.0, 30.0, 30.0)];
[[[[NSApplication sharedApplication] mainWindow] contentView] addSubview:newCircle];
[newCircle release];
}
#end
In my MakeCircle.m
- (void)drawRect:(NSRect)rect {
[self setNeedsDisplay:YES];
[[NSColor blackColor] setStroke];
// Create our circle path
NSBezierPath* circlePath = [NSBezierPath bezierPath];
[circlePath appendBezierPathWithOvalInRect: rect];
//give the line some thickness
[circlePath setLineWidth:4];
// Outline and fill the path
[circlePath stroke];
}
Thanks.
I think you see only half of the edge, right? You can calculate the half of the thickness of the edge and subtract that from the rectangle:
#define STROKE_COLOR ([NSColor blackColor])
#define STROKE_WIDTH (4.0)
- (void)drawRect:(NSRect)dirtyRect {
NSBezierPath *path;
NSRect rectangle;
/* Calculate rectangle */
rectangle = [self bounds];
rectangle.origin.x += STROKE_WIDTH / 2.0;
rectangle.origin.y += STROKE_WIDTH / 2.0;
rectangle.size.width -= STROKE_WIDTH / 2.0;
rectangle.size.height -= STROKE_WIDTH / 2.0;
path = [NSBezierPath path];
[path appendBezierPathWithOvalInRect:rectangle];
[path setLineWidth:STROKE_WIDTH];
[STROKE_COLOR setStroke];
[path stroke];
}
I have no Mac at the moment, so I can't test it, but I think it should solve your problem.
Als don't call [self setNeedsDisplay:YES]. The method is used when you want to redraw your whole NSView, and calling it from the drawing method is a little bit recursive. That's why I'm surprised your code actually draws something.
And I have another tip: [[NSApplication sharedApplication] mainWindow] is actually the same as [NSApp mainWindow]. NSApp is a global variable containing the main application.
Hope it helps,
ief2

Cocoa draw image with rounded corners and shadow

I am trying to draw an image using core graphics such that it has rounded corners and a drop shadow. Here is a snippet of my code:
CGContextSetShadowWithColor(context, CGSizeMake(0, 1), 2, shadowColor);
CGContextAddPath(context, path);
CGContextClip(context);
CGContextDrawImage(context, rect, image);
The problem I am having is that the clipping to create the rounded corners is also clipping the shadow. Since the image may be transparent in areas, I cannot simply draw the rounded rectangle with a shadow under the image. I guess I need to apply the rounded shape to the image first, and then draw the resulting image to the screen and add the shadow. Does anyone know how to do this?
Thanks!
Okay, so assuming that you have a UIView subclass, which has an instance variable, image, which is a UIImage, then you can do your drawRect: function like so...
- (void)drawRect:(CGRect)rect {
[super drawRect:rect];
CGRect _bounds = [self bounds];
CGColorRef aColor;
CGContextRef context = UIGraphicsGetCurrentContext();
// Create a path
CGRect insetRect = CGRectInset(_bounds, kBSImageButtonBorder, kBSImageButtonBorder);
CGRect offsetRect = insetRect; offsetRect.origin = CGPointZero;
UIGraphicsBeginImageContext(insetRect.size);
CGContextRef imgContext = UIGraphicsGetCurrentContext();
CGPathRef clippingPath = [UIBezierPath bezierPathWithRoundedRect:offsetRect cornerRadius:CORNER_RADIUS].CGPath;
CGContextAddPath(imgContext, clippingPath);
CGContextClip(imgContext);
// Draw the image
[image drawInRect:offsetRect];
// Get the image
UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
// Setup the shadow
aColor = [UIColor colorWithRed:0.0f green:0.0f blue:0.0f alpha:0.5f].CGColor;
CGContextSetShadowWithColor(context, CGSizeMake(0.0f, 2.0f), 2.0f, aColor);
// Draw the clipped image in the context
[img drawInRect:insetRect];
}
I'm a little new to Quartz programming myself, but that should give you your image, centered in the rectangle, minus a border, with a corner radius, and a 2.f point shadow 2.f points below it. Hope that helps.
Here is a function to round the corners of an image using Daniel Thorpe's answer, in case you came here, like me, just looking for a way to do this.
+ (UIImage *) roundCornersOfImage:(UIImage *)image toRadius:(float)radius {
// create image sized context
UIGraphicsBeginImageContext(image.size);
CGContextRef context = UIGraphicsGetCurrentContext();
// add rounded rect clipping path to context using radius
CGRect imageBounds = CGRectMake(0, 0, image.size.width, image.size.height);
CGPathRef clippingPath = [UIBezierPath bezierPathWithRoundedRect:imageBounds cornerRadius:radius].CGPath;
CGContextAddPath(context, clippingPath);
CGContextClip(context);
// draw the image
[image drawInRect:imageBounds];
// get the image
UIImage *outImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return outImage;
}
You can use an imageView with a layer, the layer has properties for setting shadows and borders, this is how:
self.imageView = [[NSImageView alloc] initWithFrame:NSMakeRect(0,0,60,60)];
self.imageView.image = [NSImage imageNamed:#"yourImageName"];
self.imageView.wantsLayer = YES;
self.imageView.layer.cornerRadius = 10;
//make the shadow and set it
NSShadow* shadow = [[NSShadow alloc] init];
shadow.shadowBlurRadius = 2;
shadow.shadowOffset = NSMakeSize(2, -2);
shadow.shadowColor = [NSColor blackColor];
self.imageView.shadow = shadow;
Hope this helps, this is also much faster to draw then using drawRect overrides

Resources