how to make Sprite follow bezier curve - sprite-kit

Im fairly new to objective-c and sprite kit but i've done games development for a while. Im currently working on a 2d game and i have enemy ships moving from right to left on the screen. Ive been following tutorials for different parts of my game and then adding to it where necessary. I found a tutorial where the in game enemies follow a bezier path and i've managed to implement this in my game however as i'm new to bezier curves i do not fully understand them and the algorithm makes my sprites move from top to bottom but i need them to go left to right.
I have included the code snippet that i have used to add the bezier curve to my sprites any suggestions on how i can have them move right to left instead of top to bottom.
CGMutablePathRef cgpath = CGPathCreateMutable();
float xStart = [self getRandomNumberBetween:0+asteroid.size.width to:self.frame.size.width-asteroid.size.width];
float xEnd = [self getRandomNumberBetween:0+asteroid.size.width to:self.frame.size.width-asteroid.size.width];
float cp1X =[self getRandomNumberBetween:0+asteroid.size.width to:self.frame.size.width-asteroid.size.width];
float cp1y = [self getRandomNumberBetween:0+asteroid.size.width to:self.frame.size.width-asteroid.size.height];
float cp2x = [self getRandomNumberBetween:0+asteroid.size.width to:self.frame.size.width-asteroid.size.width];
float cp2Y = [self getRandomNumberBetween:0 to:cp1y];
CGPoint s = CGPointMake(xStart, 1024.0);
CGPoint e = CGPointMake(xEnd, -100);
CGPoint cp1 = CGPointMake(cp1X, cp1y);
CGPoint cp2 = CGPointMake(cp2x, cp2Y);
CGPathMoveToPoint(cgpath, NULL, s.x, s.y);
CGPathAddCurveToPoint(cgpath, NULL, cp1.x, cp1.y, cp2.x, cp2.y, e.x, e.y);
SKAction *enemyCurve = [SKAction followPath:cgpath asOffset:NO orientToPath:YES duration:5];
CGPoint location = CGPointMake(-self.frame.size.width-asteroid.size.width, randY);
SKAction *moveAction = [SKAction moveTo:location duration:randDuration];
SKAction *doneAction = [SKAction runBlock:(dispatch_block_t)^(){
asteroid.hidden = YES;
}];
SKAction *moveAsteroidActionWithDone = [SKAction sequence:#[enemyCurve,moveAction, doneAction]];
Thank you for any help and suggestions.

Bezier curves are used to generate a smooth curve between two points.
To move the path from left to right, choose a starting point on the left and choosing an ending point on the right. The two control points determine the shape of the path to take from left to right. Varying the startpoint and endpoint in the following code will control where the bezier curve starts and where it ends. Varying the control points vary the shape of the curve. You can see that with the attached GIF.
CGMutablePathRef cgpath = CGPathCreateMutable();
CGPoint startingPoint = CGPointMake(50, 100);
CGPoint controlPoint1 = CGPointMake(160, 250);
CGPoint controlPoint2 = CGPointMake(200, 140);
CGPoint endingPoint = CGPointMake(303, 100);
CGPathMoveToPoint(cgpath, NULL, startingPoint.x, startingPoint.y);
CGPathAddCurveToPoint(cgpath, NULL, controlPoint1.x, controlPoint1.y,
controlPoint2.x, controlPoint2.y,
endingPoint.x, endingPoint.y);
SKAction *enemyCurve = [SKAction followPath:cgpath asOffset:NO orientToPath:YES duration:5];
[enemy runAction:[SKAction sequence:#[[SKAction waitForDuration:1],enemyCurve]]];
P0 and P3 are the starting and ending points and P1 and P2 are the control points.
Check out this page to play more with bezier curves.
http://www.jasondavies.com/animated-bezier/

Related

Draw the Path of a Moving SKSpritenode

I am Trying to use the new SKSpritenode , i have managed to create a Spritenode move it around the screen , although i want the Spritenode leaves a trace( color ) where it moves.
the code to create the Sprite node & my attempts to create a shapenode as a child for the spritenode ( which did not work ) .
-(void)movetherayoflight{
ray1=[[lightray1 alloc]initWithColor:[SKColor redColor] size:CGSizeMake(6,6)];
[ray1 setPosition:CGPointMake(50, 50)];
ray1.physicsBody=[SKPhysicsBody bodyWithRectangleOfSize:ray1.size];
ray1.physicsBody.restitution=1;
ray1.physicsBody.linearDamping=0;
ray1.physicsBody.friction=0;
ray1.physicsBody.allowsRotation=NO;
ray1.physicsBody.velocity=CGVectorMake(0.0f, 300.0f);
[self addChild:ray1];
SKShapeNode *raayyy=[[SKShapeNode alloc]init];
CGMutablePathRef rayPath = CGPathCreateMutable();
CGPoint fff=CGPointMake(ray1.position.x, ray1.position.y);
CGPoint startpoint=CGPointMake(50, 100);
//CGPathAddLines(rayPath, NULL, &fff, 2);
CGPathAddLines(rayPath, NULL, &startpoint, 5);
//CGPathAddLineToPoint(rayPath, NULL, ray1.position.x, ray1.position.y);
raayyy.path=rayPath;
raayyy.lineWidth = 1.0;
//raayyy.fillColor = [SKColor whiteColor];
raayyy.strokeColor = [SKColor greenColor];
raayyy.glowWidth = 0.5;
[ray1 addChild:raayyy];
}
If you have better Solution , Please let me know !
Instead of making the SKShapeNode as the child of the SKSpriteNode, declare it as its sibling in the SKScene.
First, declare the SKShapeNode and the CGPath as instance variables.
In your scene's -initWithSize: method,
raayyy=[[SKShapeNode alloc]init];
//Additional initialization here.
rayPath = CGPathCreateMutable();
CGPathMoveToPoint(pathToDraw, NULL, ray1.position.x, ray1.position.y);
rayyy.path = rayPath;
[self addChild:rayyy];
Then in your -update: method,
CGPathAddLineToPoint(rayPath, NULL, yar1.position.x, ray1.position.y);
rayyy.path = rayPath;
This is just a suggestion, I haven't tried anything of the like myself.
well , I have managed it with a solution but i really dont like it so far
-(SKShapeNode*)gravityline{
SKShapeNode *lolo = [[SKShapeNode alloc] init];
CGPoint fff=CGPointMake(ray1.position.x, ray1.position.y);
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, fff.x, fff.y);
CGPathAddLineToPoint(path, 0,rayoriginpoint.x,rayoriginpoint.y );
CGPathCloseSubpath(path);
lolo.path = path;
lolo.name=#"gravityline";
lolo.strokeColor=[SKColor greenColor];
lolo.glowWidth=.1;
lolo.physicsBody=[SKPhysicsBody bodyWithPolygonFromPath:path];
lolo.physicsBody.categoryBitMask=raylightCategory;
lolo.physicsBody.collisionBitMask=batCategory;
lolo.physicsBody.contactTestBitMask=batCategory;
lolo.physicsBody.dynamic=NO;
CGPathRelease(path);
return lolo;
}
The rayoriginpointchanges its value once the ray point hits something
if (firstBody.categoryBitMask == rayCategory && secondBody.categoryBitMask==mirrorCategory )
{
[self addChild:[self gravityline]];
CGPoint p = contact.contactPoint;
rayoriginpoint=p;
NSLog(#"Contact have been made");
}
What i want to do is very basic :
1- Either to change the Color of every CGPOINT that the SKSpritenode Passes by
2- Either to create Sprite node that scales up in certain direction till it hits something then changes direction

Bezier Curve Movement

I want to move a cubic bezier curve from source point to destination point like line draw using touches but in my case not by touches, or u can say just by draw method or update method when a scene called(not a sprite movement along bezier curve).
My Code is-> Here i just take one sprite(one small image line) and with the help of curve points i just make curve path.(when i called scene then direct curved path showing)
but I want proper curve movement animation from source point to destination point.
curvePoints=[[NSMutableArray alloc]init];
for (CGFloat t = 0.0; t <= 1.00001; t += 0.005)
{
CGPoint point = CGPointMake(
_bezierPoint(t,237, 320,423,609),
_bezierPoint(t, 319, 461,529,534));
CCSprite*path=[CCSprite spriteWithFile:#"path.png"];
[self addChild:path];
[path setPosition:[[CCDirector sharedDirector]convertToGL:point]];
[curvePoints addObject:[NSValue valueWithCGPoint:[[CCDirector sharedDirector]convertToGL:point]]];
}
I have also tried using the draw method like this:
-(void)draw{
CGSize s = [[CCDirector sharedDirector] winSize];
CGPoint point1 = CGPointMake(237, 705);
CGPoint point2 = CGPointMake(320, 563);
CGPoint point3 = CGPointMake(423, 495);
CGPoint point4 = CGPointMake(609, 490);
[curvePoints addObject:NSStringFromCGPoint(point1)];
[curvePoints addObject:NSStringFromCGPoint(point2)];
[curvePoints addObject:NSStringFromCGPoint(point3)];
[curvePoints addObject:NSStringFromCGPoint(point4)];
glEnable(GL_LINE_SMOOTH);
for(int i = 0; i < [curvePoints count]; i+=2)
{
CGPoint origin = CGPointFromString([curvePoints objectAtIndex:i]);
CGPoint control1 = CGPointFromString([curvePoints objectAtIndex:i+1]);
CGPoint control2 = CGPointFromString([curvePoints objectAtIndex:i+1]);
CGPoint destination = CGPointFromString([curvePoints objectAtIndex:i+1]);
ccDrawCubicBezier(origin, control1, control2, destination, 100);
}
}
but here also same problem,when I called scene,direct cubic curve showing.
Please help me out of this problem,sorry for my bad English,
any help will be appreciated
Thanks

CGPoint rotation changes distance from origin

I want to rotate a CGPoint(red rect) around another CGPoint(blue rect) but it changes distance from the origin(blue rect)...when i give 270 in angle it creates the point right above the origin but when i give 90 as angle value it comes down the origin BUT CHANGES THE DISTANCE ALSO almost three times more....I want to keep the distance same and want to rotate CGPoint around another. Please guide any approach for rotation of cgpoints...
distance = 100;
angle = 270*M_PI/180;
rotatedPoint.x = initialPoint.x+distance*cos(angle);
rotatedPoint.y = initialPoint.y+distance*sin(angle);
//rotatedPoint.x = initialPoint.x+tan(angle);
[test setCenter:rotatedPoint];
[test setBackgroundColor:[UIColor redColor]];
Thanks
CGAffineTransform is a handy tool when it comes to rotation, translation, and scaling. To make sure a point is rotated properly, you must translate it to the origin, rotate it, and then translate it back.
To complete your transformation, something like the following should do the trick:
CGPoint pointToRotate = CGPointMake(30, 30);
float angleInRadians = DEGREES_TO_RADIANS(90);
CGPoint distanceFromOrigin = CGPointMake(0 - pointToRotate.x, 0 - pointToRotate.y);
CGAffineTransform translateToOrigin = CGAffineTransformMakeTranslation(distanceFromOrigin.x, distanceFromOrigin.y);
CGAffineTransform rotationTransform = CGAffineTransformMakeRotation(angleInRadians);
CGAffineTransform translateBackFromOrigin = CGAffineTransformInvert(translateToOrigin);
CGAffineTransform totalTransform = CGAffineTransformConcat(translateToOrigin, rotationTransform);
totalTransform = CGAffineTransformConcat(totalTransform, translateBackFromOrigin);
pointToRotate = CGPointApplyAffineTransform(pointToRotate, totalTransform);
And here is the documentation on CGAffineTransform, if you'd like to review it further: http://developer.apple.com/library/mac/#documentation/GraphicsImaging/Reference/CGAffineTransform/Reference/reference.html
Please let me know if you need anything else if this doesn't solve your problem!

How to give effect like throwing a ball in a Bucket?

I am new to Cocos2d and hence not aware of most of the classes and also the functions. In my application, I want to implement a module like touching and dragging the ball in a direction of a barrel will pass the ball and will put the ball in the barrel. I am able to pass the ball in the direction but then it just would go out of the screen following the coordinates(x.y).
- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
// Choose one of the touches to work with
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView:[touch view]];
location = [[CCDirector sharedDirector] convertToGL:location];
// Determine offset of location to projectile
int offX = location.x - ball.position.x;
int offY = location.y - ball.position.y;
// Bail out if we are shooting down or backwards
if (offX <= 0)
return;
// Determine where we wish to shoot the projectile to
int realX = winSize.width + (ball.contentSize.height/2);
float ratio = (float) offY / (float) offX;
int realY = (realX * ratio) + ball.position.y;
CGPoint realDest = ccp(realX, realY);
if(realX>=320)
realX = 320;
if(realY>=480)
realY = 480;
CGPoint realDest = ccp(realX, realY);
int good = goodBarrel.position.x;
int bad = badBarrel.position.x;
int destY = realDest.x;
if(destY<=good)
destY = good;
if(destY>=bad)
destY = bad;
realDest.x = destY+10;
// Determine the length of how far we're shooting
int offRealX = realX - ball.position.x;
int offRealY = realY - ball.position.y;
float length = sqrtf((offRealX*offRealX)+(offRealY*offRealY));
float velocity = 480/1; // 480pixels/1sec
float realMoveDuration = length/velocity;
// Move projectile to actual endpoint
[ball runAction:[CCSequence actions:
[CCMoveTo actionWithDuration:realMoveDuration position:realDest],
[CCCallFuncN actionWithTarget:self selector:#selector(spriteMoveFinished:)],
nil]];
}
I need to do something like it will get the idea of barrels position and finding the top of the barrel and will put the ball in the center of the barrel and also while moving the ball,the size of the ball gets reduced to give effect of throwing the ball to a distant place. GoodBarrel and BadBarrel are the two barrels I have put in the opposite of ball to put the ball in the same. Is there any function or method I can work on?
Immediately after the [ball runAction: .... ]; line, you can run another action on the same object, simultaneously. Add a
[ball runAction:[CCScaleTo actionWithDuration:realMoveDuration scale:0.4f];
both actions will complete at the same time, providing the 'illusion' of distance you are seeking to create.
You can use collision detection, or in other words check to see if the CGRect of ball and barrel intersect
- (void)collisionDetection:(ccTime)dt{
CGRect ballRect = CGRectMake(
ball.position.x,
ball.position.y,
10,
10);
CGRect barrelRect = CGRectMake(
barrel.position.x,
barrel.position.y,
50,
10);
if (CGRectIntersectsRect(ballRect, barrelRect))
{
// Run Animation of Ball Dropping in the Barrel
[self removeChild:ball cleanup: YES];
}
}
hope this is what you need.

Rotate UIButton randomly on screen

I want to rotate a button randomly on a screen. No specific path defined it can move randomly on a view.
I dont want to use CAKeyframeAnimation. It should be clean and simple.
Can anyone guide me ?
CGAffineTransform cachedTransform = transformedView.transform;
transformedView.transform = CGAffineTransformIdentity;
// Note each of the (untransformed) points of interest.
CGPoint topLeft = CGPointMake(0, 0);
CGPoint bottomLeft = CGPointMake(0, transformedView.frame.size.height);
CGPoint bottomRight = CGPointMake(transformedView.frame.size.width, transformedView.frame.size.height);
CGPoint topRight = CGPointMake(transformedView.frame.size.width, 0);
// Re-apply the transform.
transformedView.transform = cachedTransform;
// Use handy built-in UIView methods to convert the points.
topLeft = [transformedView convertPoint:topLeft toView:parentView];
bottomLeft = [transformedView convertPoint:bottomLeft toView:parentView];
bottomRight = [transformedView convertPoint:bottomRight toView:parentView];
topRight = [transformedView convertPoint:topRight toView:parentView];
Also see if this link is useful to you : Moving UIButton
Animate the button's transform property using CGAffineTransformMakeRotation() and combine it with an NSTimer with a random time interval.
#define RADIANS(degrees) ((degrees * M_PI) / 180.0)
CGAffineTransform rotateTransform = CGAffineTransformRotate(CGAffineTransformIdentity,
RADIANS(30.0));
myButton.transform = rotateTransform;
Use NSTimer to carry this process and use arc4random to randomly generate the angle in radians.