How do I set a different value for gravity? - physics

I am using Sprite Kit in Xcode and I want to change the default value that's set on the gravity.
This is the code I have:
SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithImageNamed:#"sprite1.png"];
sprite.position = CGPointMake(-100+CGRectGetMidX(self.frame), 50 + CGRectGetMidY(self.frame));
[self addChild:sprite];
sprite.color = [SKColor blueColor];
sprite.colorBlendFactor = 1.0;
sprite.xScale = 0.2;
sprite.yScale = 0.2;
[SKPhysicsBody bodyWithCircleOfRadius:50];
sprite.physicsBody.dynamic = YES;
sprite.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:sprite.size.width * 0.5];
And Now I know that using this line of code should give the default settings but I can't run the project now:
//self.physicsWorld.gravity = CGPointMake(0.0, -9.8);
Help?

self.physicsWorld.gravity property is represented by CGVector.
So you should use CGVectorMake to change gravity:
self.physicsWorld.gravity = CGVectorMake(0.0f, -9.8f);

It's a bug in Apple's documentation - it's CGVectorMake and not CGPoint as the documentation still says.

Related

SpriteKit rolling ball

I am new to SpriteKit in Xcode. A new game I'm making includes a ball that's supposed to roll along a plank using gravity physics. The ball is not just one solid color, so it needs to roll and turn at the same time to look real. However the ball just looks like it's sliding along the plank instead of rolling.
This is the code for the ball:
SKSpriteNode *ball = [SKSpriteNode spriteNodeWithImageNamed:#"Ball"];
ball.xScale = 0.25;
ball.yScale = 0.25;
ball.position = CGPointMake(CGRectGetMidX(self.view.frame), self.view.frame.size.height - 150);
ball.name = #"ball";
ball.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:50/2];
ball.physicsBody.dynamic = YES;
ball.physicsBody.restitution = 0;
ball.physicsBody.friction = 1;
ball.physicsBody.allowsRotation = YES;
[self addChild:ball];
And this is the code for the plank:
SKSpriteNode *plank = [SKSpriteNode spriteNodeWithColor:[UIColor whiteColor] size:CGSizeMake(150, 10)];
plank.position = CGPointMake(CGRectGetMidX(self.view.frame), 50);
plank.name = #"plank";
plank.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(150, 10)];
plank.physicsBody.dynamic = NO;
plank.physicsBody.restitution = 0.5;
plank.physicsBody.friction = 0;
[self addChild:plank];
I included the code ball.physicsBody.allowsRotation = YES; although I'm not sure if that's right, but the ball isn't rolling the way it should. Any help would be appreciated, thanks!

CALayer shadow while scrolling UITableView

I have added a shadow to a UITableView (which covers a third of the screen sfrom the bottom - see attached screenshot) using the following in a UIView Category:
- (void) addShadow {
UIBezierPath *path = [UIBezierPath bezierPathWithRect:self.bounds];
self.layer.masksToBounds = NO;
self.layer.shadowColor = [UIColor blackColor].CGColor;
self.layer.shadowOpacity = 1;
self.layer.shadowOffset = CGSizeMake(-5,-5);
self.layer.shadowRadius = 20;
self.layer.shadowPath = path.CGPath;
self.layer.shouldRasterize = YES;
}
It appears as expected, but when I scroll it up, the shadow scrolls up too. Also, the table scrolls beyond its upper bound. Can you suggest what is wrong here? if I comment self.layer.masksToBounds = NO;, the shadow disappears, but the table scrolling is as expected. Hence, the problem lies somewhere around masksToBounds perhaps.
I solved it by putting an identical view underneath, just for the shadow. Not a clean solution ... hence I am still open to answers. My code is as follows:
- (UIView*) addShadow {
UIView* backView = [[UIView alloc] initWithFrame:self.frame];
UIBezierPath *path = [UIBezierPath bezierPathWithRect:backView.bounds];
backView.layer.masksToBounds = NO;
backView.layer.shadowColor = [UIColor blackColor].CGColor;
backView.layer.shadowOpacity = 1;
backView.layer.shadowOffset = CGSizeMake(-5,-5);
backView.layer.shadowRadius = 20;
backView.layer.shadowPath = path.CGPath;
backView.layer.shouldRasterize = YES;
[self.superview addSubview:backView];
[self.superview bringSubviewToFront:self];
return backView;
}
(void) removeShadow {
self.layer.masksToBounds = YES;
self.layer.shadowColor = nil;
self.layer.shadowOpacity = 0;
self.layer.shadowOffset = CGSizeMake(0,0);
self.layer.shadowRadius = 0;
}

Animating CAShapeLayer size change

I am drawing a circle, with an initial radius of 200
self.circle = [CAShapeLayer layer];
self.circle.fillColor = nil;
self.circle.strokeColor = [UIColor blackColor].CGColor;
self.circle.lineWidth = 7;
self.circle.bounds = CGRectMake(0, 0, 2 * radius, 2 * radius);
self.circle.path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(self.radius, self.radius)
Can anyone please tell me how to animate a change to a radius of 100?
This is how I ended up doing it:
UIBezierPath *newPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(newRadius, newRadius) radius:newRadius startAngle:(-M_PI/2) endAngle:(3*M_PI/2) clockwise:YES];
CGRect newBounds = CGRectMake(0, 0, 2 * newRadius, 2 * newRadius);
CABasicAnimation* pathAnim = [CABasicAnimation animationWithKeyPath: #"path"];
pathAnim.toValue = (id)newPath.CGPath;
CABasicAnimation* boundsAnim = [CABasicAnimation animationWithKeyPath: #"bounds"];
boundsAnim.toValue = [NSValue valueWithCGRect:newBounds];
CAAnimationGroup *anims = [CAAnimationGroup animation];
anims.animations = [NSArray arrayWithObjects:pathAnim, boundsAnim, nil];
anims.removedOnCompletion = NO;
anims.duration = 2.0f;
anims.fillMode = kCAFillModeForwards;
[self.circle addAnimation:anims forKey:nil];
This is, I believe, the simpler and preferred way to do it:
UIBezierPath *newPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(newRadius, newRadius) radius:newRadius startAngle:(-M_PI/2) endAngle:(3*M_PI/2) clockwise:YES];
CABasicAnimation* pathAnim = [CABasicAnimation animationWithKeyPath:#"path"];
pathAnim.fromValue = (id)self.circle.path;
pathAnim.toValue = (id)newPath.CGPath;
pathAnim.duration = 2.0f;
[self.circle addAnimation:pathAnim forKey:#"animateRadius"];
self.circle.path = newPath.CGPath;
I got this approach from the Apple Documentation here (See listing 3-2). The important things here are that you're setting the fromValue and also setting the final value for self.circle's path to newPath.CGPath right after you set up the animation. An animation does not change the underlying value of the model, so in the other approach, you're not actually making a permanent change to the shape layer's path variable.
I've used a solution like the chosen answer in the past (using removedOnCompletion and fillMode etc) but I found that on certain versions of iOS they caused memory leaks, and also sometimes that approach just doesn't work for some reason.
With this approach, you're creating the animation explicitly (both where it starts from and where it ends), and you're specifying the final permanent value for the path at the end (with that last line), so that there's no need to mess with not removing the animation when it finishes (which I believe is what leads to memory leaks if you do this animation a lot of times... the unremoved animations build up in memory).
Also, I removed the animation of the bounds because you don't need it to animate the circle. Even if the path is drawn outside the bounds of the layer, it'll still be fully shown (see the docs for CAShapeLayer about that). If you do need to animate the bounds for some other reason, you can use the same approach (making sure to specify the fromValue and the final value for bounds).
You can use a CAAnimationGroup to animate the bounds and path of your CAShapeLayer:
- (void)animateToRadius:(CGFloat)newRadius {
CAShapeLayer *shapeLayer = ...;
UIBezierPath *newBezierPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(newRadius, newRadius)];
NSValue *newBounds = [NSValue valueWithCGRect:CGRectMake(0,0,2*newRadius,2*newRadius);
CAAnimationGroup *group = [CAAnimationGroup animation];
NSMutableArray *animations = [#[] mutableCopy];
NSDictionary *animationData = #{#"path":newBezierPath.CGPath, #"bounds":newBounds};
for(NSString *keyPath in animationData) {
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:keyPath];
animation.toValue = animationData[keyPath];
[animations addObject:animation];
}
group.animations = [animations copy];
group.duration = 2;
group.removedOnCompletion = NO;
group.fillMode = kCAFillModeForwards;
[shapeLayer addAnimation:group forKey:nil];
}
Here is a direct swift conversion of Jonathan's code should anyone need it.
func scaleShape(shape : CAShapeLayer){
let newRadius : CGFloat = 50;
let newPath: UIBezierPath = UIBezierPath(arcCenter: CGPointMake(newRadius, newRadius), radius: newRadius, startAngle: (CGFloat(-M_PI) / 2.0), endAngle: (3 * CGFloat(M_PI) / 2), clockwise: true);
let newBounds : CGRect = CGRect(x: 0, y: 0, width: 2 * newRadius, height: 2 * newRadius);
let pathAnim : CABasicAnimation = CABasicAnimation(keyPath: "path");
pathAnim.toValue = newPath.CGPath;
let boundsAnim : CABasicAnimation = CABasicAnimation(keyPath: "bounds");
boundsAnim.toValue = NSValue(CGRect: newBounds);
let anims: CAAnimationGroup = CAAnimationGroup();
anims.animations = [pathAnim, boundsAnim];
anims.removedOnCompletion = false;
anims.duration = 2.0;
anims.fillMode = kCAFillModeForwards;
shape.addAnimation(anims, forKey: nil);
}

can not add a shadow to the view, CALayer

I am adding a shadow to my view by following
- (void)viewDidLoad{
[super viewDidLoad];
self.view.layer.shadowColor = [UIColor blackColor].CGColor;
self.view.layer.shadowOffset = CGSizeMake(0.0f, 0.0f);
self.view.layer.shadowOpacity = 1.0f;
self.view.layer.shadowRadius = 4.0f;
self.view.layer.shadowPath = [UIBezierPath bezierPathWithRect:self.view.bounds].CGPath;
}
However, I am getting a view with no shadow at all like below
Did I miss some points in the middle of the way. Please advice me on this issue
You need to set layer.masksToBounds to NO and clipsToBounds to YES.
self.view.layer.masksToBounds = NO;
self.view.clipsToBounds = YES;
You are not offsetting your shadow at all.
Try:
self.view.layer.shadowOffset = CGSizeMake(0.0f, 1.0f);
Sorry all, this is my stupid mistake. The view structure I am having is
view(UIView) ( in white color )
|
|
aView (UIView) ( in orange color )
what I did was to show the shadow of view not aView. Just corrected the code like below
(void)viewDidLoad{
[super viewDidLoad];
self.aView.layer.shadowColor = [UIColor blackColor].CGColor;
self.aView.layer.shadowOffset = CGSizeMake(0.0f, 0.0f);
self.aView.layer.shadowOpacity = 1.0f;
self.aView.layer.shadowRadius = 4.0f;
self.aView.layer.shadowPath = [UIBezierPath bezierPathWithRect:self.view.bounds].CGPath;
}
making the offset 0, will produce a nil effect on the view.
you can try this:
-(void)makeShadow
{
self.layer.shadowOpacity = 0.7f;
self.layer.shadowOffset = CGSizeMake(5.0f,3.0f);
self.layer.shadowColor =[[UIColor blackColor] CGColor];
// UIBezierPath *path = [UIBezierPath bezierPathWithRect:self.bounds];
// layer.shadowPath = path.CGPath;
// layer.shadowRadius = 5.0f;
}
-(void)makeCornerRadius:(CGFloat)_cornerRadius
{
self.cornerRadius = _cornerRadius;
[self.layer setMasksToBounds:NO];
[self.layer setCornerRadius:_cornerRadius];
}
you should make the offset have a certain value not 0.
for example:
self.view.layer.shadowOffset = CGSizeMake(3, -1);
so that there will be a shadow forming in your view.
Make sure that self.clipsToBounds = NO;
Your view looks unrelated to the code you provided. Be sure you are applying the shadow to the correct view, you are not masking the view to its bounds, and the colors are correct. The code below shows a working example:
UIView *pointView = [[UIView alloc] initWithFrame:pointLabel.frame];
pointView.backgroundColor = [UIColor whiteColor];
pointView.layer.shadowColor = [UIColor lightGrayColor].CGColor;
pointView.layer.shadowOffset = CGSizeMake(2.0f, 2.0f);
pointView.layer.shadowOpacity = 1.0f;
pointView.layer.shadowRadius = 5.0f;
pointView.layer.shadowPath = [UIBezierPath bezierPathWithRect:pointView.bounds].CGPath;

Animating CAShapeLayer's strokeColor with CABasicAnimation

I've been unsuccessful at animating a flashing stroke on a CAShapeLayer using the answer from this previous thread, and after many searches I can find no other examples of animating the stroke using CABasicAnimation.
What I want to do is have the stroke of my CAShapeLayer pulse between two colors. Using CABasicAnimation for opacity works fine, but the [CABasicAnimation animationWithKeyPath:#"strokeColor"] eludes me, and I'd appreciate any advice on how to successfully implement.
CABasicAnimation *strokeAnim = [CABasicAnimation animationWithKeyPath:#"strokeColor"];
strokeAnim.fromValue = (id) [UIColor blackColor].CGColor;
strokeAnim.toValue = (id) [UIColor purpleColor].CGColor;
strokeAnim.duration = 1.0;
strokeAnim.repeatCount = 0.0;
strokeAnim.autoreverses = NO;
[shapeLayer addAnimation:strokeAnim forKey:#"animateStrokeColor"];
// CABasicAnimation *opacityAnimation = [CABasicAnimation animationWithKeyPath:#"opacity"];
// opacityAnimation.fromValue = [NSNumber numberWithFloat:0.0];
// opacityAnimation.toValue = [NSNumber numberWithFloat:1.0];
// opacityAnimation.duration = 1.0;
// opacityAnimation.repeatCount = 0.0;
// opacityAnimation.autoreverses = NO;
// [shapeLayer addAnimation:opacityAnimation forKey:#"animateOpacity"];
Uncommenting the opacity animation results in an expected opacity fade. The stroke animation produces no effect. An implicit strokeColor change animates as expected, but I would like documented confirmation that strokeColor can be explicitly animated using CABasicAnimation.
Update: The specific problem was that shapeLayer.path was NULL. Correcting that fixed the problem.
The code below works great for me. What is the lineWidth of your shapeLayer stroke path? Could that be the issue?
- (void)viewDidLoad
{
[super viewDidLoad];
UIBezierPath * circle = [UIBezierPath bezierPathWithOvalInRect:self.view.bounds];
CAShapeLayer * shapeLayer = [CAShapeLayer layer];
shapeLayer.path = circle.CGPath;
shapeLayer.strokeColor =[UIColor blueColor].CGColor;
[shapeLayer setLineWidth:15.0];
[self.view.layer addSublayer:shapeLayer];
CABasicAnimation *strokeAnim = [CABasicAnimation animationWithKeyPath:#"strokeColor"];
strokeAnim.fromValue = (id) [UIColor redColor].CGColor;
strokeAnim.toValue = (id) [UIColor greenColor].CGColor;
strokeAnim.duration = 3.0;
strokeAnim.repeatCount = 0;
strokeAnim.autoreverses = YES;
[shapeLayer addAnimation:strokeAnim forKey:#"animateStrokeColor"];
}
Let me know if it works for you...