Bezier Curve Movement - iphone

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

Related

how to make Sprite follow bezier curve

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/

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.

Updating emitter.gravity on a CCParticleSystemQuad on touch event

I've created a simple cocos2d iPhone game that has a CCParticleSystemQuad emitter that emits particles across the scene that are shaped like leaves, to give the illusion of wind.
Right now, the wind (gravity) is blowing across the scene from right to left. I am currently stuck trying to figure out how to update the emitter.gravity to switch from (-500, 80) to (500, 80), hopefully without removing the particles that have already been drawn.
In this example, I'd like the switch to occur on a touch event that happens anywhere on the screen.
What should my touch event look like?
How do I detect a touch that happens any where on the screen?
I've also never implemented a scheduled update loop. Is this the direction I should be thinking? And I suppose a more basic question is, am I going about this the right way?
Here's the code I've got so far:
My init:
-(id) init
{
if( (self=[super init])) {
CCSprite * sky = [CCSprite spriteWithFile:#"sky.png"];
[self addChild:sky z:0 tag:1];
windDirection = -200;
[self leaveEmitters];
}
return self;
}
My leaveEmiiters function
-(void) leaveEmitters{
NSLog(#"The wind is :%i", windDirection);
CCParticleSystemQuad * emitter;
emitter = [[CCParticleSystemQuad alloc] initWithTotalParticles:100];
emitter.texture = [[CCTextureCache sharedTextureCache] addImage: #"particlesLeaves.png"];
emitter.emitterMode = kCCParticleModeGravity;
emitter.duration = -1;
emitter.gravity = ccp(windDirection, -80);
emitter.angle = 0;
emitter.angleVar = 360;
emitter.speed = 10;
emitter.speedVar = 100;
emitter.radialAccelVar = 0;
emitter.tangentialAccel = 0;
emitter.tangentialAccelVar = 0;
emitter.life = 10;
emitter.lifeVar = 0;
emitter.startSpin = 0;
emitter.startSpinVar = 360;
emitter.endSpin = 0;
emitter.endSpinVar = 720;
ccColor4F startColorVar = {255, 100, 0, 0};
ccColor4F startColor = {0, 240,0, 255};
emitter.startColor = startColor;
emitter.startColorVar = startColorVar;
emitter.endSize = emitter.startSize;
emitter.startSize = 60.0f;
emitter.emissionRate = 3;
emitter.blendAdditive = NO;
emitter.position = ccp(500,250);
[self addChild: emitter z:10];
emitter.autoRemoveOnFinish = YES;
CCParticleSystemQuad * emitter2;
emitter2 = [[CCParticleSystemQuad alloc] initWithTotalParticles:100];
emitter2.texture = [[CCTextureCache sharedTextureCache] addImage: #"particlesLeaves.png"];
emitter2.emitterMode = kCCParticleModeGravity;
emitter2.duration = -1;
emitter2.gravity = ccp(windDirection, 0);
emitter2.angle = 0;
emitter2.angleVar = 360;
emitter2.speed = 10;
emitter2.speedVar = 100;
emitter2.radialAccelVar = 0;
emitter2.tangentialAccel = 0;
emitter2.tangentialAccelVar = 0;
emitter2.life = 10;
emitter2.lifeVar = 0;
emitter2.startSpin = 0;
emitter2.startSpinVar = 360;
emitter2.endSpin = 0;
emitter2.endSpinVar = 720;
emitter2.startColor = startColor;
emitter2.endSize = emitter.startSize;
emitter2.startSize = 60.0f;
emitter2.emissionRate = 3;
emitter2.blendAdditive = NO;
emitter2.position = ccp(-500,250);
[self addChild: emitter2 z:10];
emitter2.autoRemoveOnFinish = YES;
}
And finally, my ccTouchesBegan function, which isn't working at all. Why?
-(void) ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView:[touch view]];
location = [[CCDirector sharedDirector] convertToGL:location];
int x = location.x;
int y= location.y;
CCSprite * sky = (CCSprite *) [self getChildByTag:1];
sky.anchorPoint = ccp(0, 0);
CGRect skyHitBox = CGRectMake(sky.position.x, sky.position.y, 500, 500);
if (CGRectContainsPoint(skyHitBox, location)) {
NSLog(#"touch accepted: x: %i y:%i", x, y);
}
}
Any help, feedback, or suggested learning direction would be GREATLY appreciated. Thanks so much!
EDIT: I answered my own questions like 20 seconds after I posted this...
If someone wants to post their own answers, I'll leave this up for 7 more hours.
you just can keep a CCParticleSystemQuad * emitter as private instance member so you can modify emitter.gravity inside the ccTouchesBegan
i didnt get exactly what kind of wind effect you'r trying to achieve. If you want to simply switch from one 500,80 to -500,80 its pretty easy
emitter.gravity = ccp(-emitter.gravity.x, emitter.gravity.y);
If you want to change the wind direction based on witch side of the skybox you hit, with hitting the center equal no wind, and more your touch is far from the center the stronger the wind will get.
I didnt try this but should be a good starting point.
You first need to get get the touched point in skybox coordinates
CGPoint skyTouchedPoint = ccp( x - sky.position.x, y - sky.position.y)
then you can define the center point of you sky box as
CGPoint skyCenterPoint = ccp(sky.size.width/2, sky.size.height/2);
and get a vector pointing to the right side, if you touch the right half side of the skybox and pointing left if you touch the left half side of the skybox
CGPoint windDirection = ccpSub(skyTouchedPoint, skyCenterPoint);
this vector module is higher the more far from the center you touch happened, you should now increase this vector module by a factor that suites for you with
windDirection = ccpMult(windDirection, 10.f); //try different values here
now you can use only the x component of this vector if you want the wind to use always the same y (as you posted the value of 80)

Applying Zoom Effect In cocos2D gaming environment?

I'm working on a game with cocos2D game engine and make load all the sprites while it load the level, now as because some of sprites (obstacles) are taller than 320 pixel, thus it seems difficult to check them out. So for the convenience sake I want to apply ZOOM IN and ZOOM out effect, which minimizes entire level's all sprites at once, and in zoom out case these will resided to there old position.
Can I achieve this?
If yes, then how?
Please tell about pinch zoom also.
Zooming, is fairly simple, simply set the scale property of your main game layer... but there are a few catches.
When you scale the layer, it will shift the position of the layer. It won't automatically zoom towards the center of what you're currently looking at. If you have any type of scrolling in your game, you'll need to account for this.
To do this, set the anchorPoint of your layer to ccp(0.0f, 0.0f), and then calculate how much your layer has shifted, and reposition it accordingly.
- (void) scale:(CGFloat) newScale scaleCenter:(CGPoint) scaleCenter {
// scaleCenter is the point to zoom to..
// If you are doing a pinch zoom, this should be the center of your pinch.
// Get the original center point.
CGPoint oldCenterPoint = ccp(scaleCenter.x * yourLayer.scale, scaleCenter.y * yourLayer.scale);
// Set the scale.
yourLayer.scale = newScale;
// Get the new center point.
CGPoint newCenterPoint = ccp(scaleCenter.x * yourLayer.scale, scaleCenter.y * yourLayer.scale);
// Then calculate the delta.
CGPoint centerPointDelta = ccpSub(oldCenterPoint, newCenterPoint);
// Now adjust your layer by the delta.
yourLayer.position = ccpAdd(yourLayer.position, centerPointDelta);
}
Pinch zoom is easier... just detect the touchesMoved, and then call your scaling routine.
- (void) ccTouchesMoved:(NSSet*)touches withEvent:(UIEvent*)event {
// Examine allTouches instead of just touches. Touches tracks only the touch that is currently moving...
// But stationary touches still trigger a multi-touch gesture.
NSArray* allTouches = [[event allTouches] allObjects];
if ([allTouches count] == 2) {
// Get two of the touches to handle the zoom
UITouch* touchOne = [allTouches objectAtIndex:0];
UITouch* touchTwo = [allTouches objectAtIndex:1];
// Get the touches and previous touches.
CGPoint touchLocationOne = [touchOne locationInView: [touchOne view]];
CGPoint touchLocationTwo = [touchTwo locationInView: [touchTwo view]];
CGPoint previousLocationOne = [touchOne previousLocationInView: [touchOne view]];
CGPoint previousLocationTwo = [touchTwo previousLocationInView: [touchTwo view]];
// Get the distance for the current and previous touches.
CGFloat currentDistance = sqrt(
pow(touchLocationOne.x - touchLocationTwo.x, 2.0f) +
pow(touchLocationOne.y - touchLocationTwo.y, 2.0f));
CGFloat previousDistance = sqrt(
pow(previousLocationOne.x - previousLocationTwo.x, 2.0f) +
pow(previousLocationOne.y - previousLocationTwo.y, 2.0f));
// Get the delta of the distances.
CGFloat distanceDelta = currentDistance - previousDistance;
// Next, position the camera to the middle of the pinch.
// Get the middle position of the pinch.
CGPoint pinchCenter = ccpMidpoint(touchLocationOne, touchLocationTwo);
// Then, convert the screen position to node space... use your game layer to do this.
pinchCenter = [yourLayer convertToNodeSpace:pinchCenter];
// Finally, call the scale method to scale by the distanceDelta, pass in the pinch center as well.
// Also, multiply the delta by PINCH_ZOOM_MULTIPLIER to slow down the scale speed.
// A PINCH_ZOOM_MULTIPLIER of 0.005f works for me, but experiment to find one that you like.
[self scale:yourlayer.scale - (distanceDelta * PINCH_ZOOM_MULTIPLIER)
scaleCenter:pinchCenter];
}
}
If all the sprites have the same parent you can just scale their parent and they will be scaled with it, keeping their coordinates relative to the parent.
this code scale my Layer by 2 to specific location
[layer setScale:2];
layer.position=ccp(240/2+40,160*1.5);
double dx=(touchLocation.x*2-240);
double dy=(touchLocation.y*2-160);
layer.position=ccp(inGamePlay.position.x-dx,inGamePlay.position.y-dy);
My code and it works better than other ones:
- (void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
NSArray* allTouches = [[event allTouches] allObjects];
CCLayer *gameField = (CCLayer *)[self getChildByTag:TAG_GAMEFIELD];
if (allTouches.count == 2) {
UIView *v = [[CCDirector sharedDirector] view];
UITouch *tOne = [allTouches objectAtIndex:0];
UITouch *tTwo = [allTouches objectAtIndex:1];
CGPoint firstTouch = [tOne locationInView:v];
CGPoint secondTouch = [tTwo locationInView:v];
CGPoint oldFirstTouch = [tOne previousLocationInView:v];
CGPoint oldSecondTouch = [tTwo previousLocationInView:v];
float oldPinchDistance = ccpDistance(oldFirstTouch, oldSecondTouch);
float newPinchDistance = ccpDistance(firstTouch, secondTouch);
float distanceDelta = newPinchDistance - oldPinchDistance;
NSLog(#"%f", distanceDelta);
CGPoint pinchCenter = ccpMidpoint(firstTouch, secondTouch);
pinchCenter = [gameField convertToNodeSpace:pinchCenter];
gameField.scale = gameField.scale - distanceDelta / 100;
if (gameField.scale < 0) {
gameField.scale = 0;
}
}
}

Set center point of UIView after CGAffineTransformMakeRotation

I have a UIView that I want the user to be able to drag around the screen.
-(void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
CGPoint pt = [[touches anyObject] locationInView:self];
CGPoint center = self.center;
CGFloat adjustmentX = pt.x - startLocation.x;
CGFloat adjustmentY = pt.y - startLocation.y;
center.x += adjustmentX;
center.y += adjustmentY;
[self setCenter:center];
}
works perfectly until I apply a transform to the view as follows:
self.transform = CGAffineTransformMakeRotation(....);
However, once the transform has been applied, dragging the UIView results in the view being moved in a exponential spiral effect.
I'm assuming I have to make an correction for the rotation in the touchesMoved method, but so far all attempts have eluded me.
You need to translate your transform to your new coordinates because a transform other than the identity is assigned to your view and moving its center alters the transform's results.
Try this instead of altering your view's center
self.transform = CGAffineTransformTranslate(self.transform, adjustmentX, adjustmentY);