2011-09-21 64 views
3

我有一個箭頭,隨着觸摸旋轉。我想知道是否可以在曲線上旋轉箭頭?我做了一些研究,我認爲它被稱爲貝塞爾路徑?甚至可以使用這段代碼在貝塞爾路徑上旋轉一個精靈,如果可以的話,我怎樣才能將它融合呢?用觸摸在貝塞爾路徑上旋轉雪碧 - Cocos2D/Box2D

UITouch *touch = [touches anyObject]; 

    //acquire the previous touch location 
    CGPoint firstLocation = [touch previousLocationInView:[touch view]]; 
    CGPoint location = [touch locationInView:[touch view]]; 

    //preform all the same basic rig on both the current touch and previous touch 
    CGPoint touchingPoint = [[CCDirector sharedDirector] convertToGL:location]; 
    CGPoint firstTouchingPoint = [[CCDirector sharedDirector] convertToGL:firstLocation]; 

    CGPoint firstVector = ccpSub(firstTouchingPoint, _arrow.position); 
    CGFloat firstRotateAngle = -ccpToAngle(firstVector); 
    CGFloat previousTouch = CC_RADIANS_TO_DEGREES(firstRotateAngle); 

    CGPoint vector = ccpSub(touchingPoint, _arrow.position); 
    CGFloat rotateAngle = -ccpToAngle(vector); 
    CGFloat currentTouch = CC_RADIANS_TO_DEGREES(rotateAngle); 

    //keep adding the difference of the two angles to the dial rotation 
    arrowRotation += currentTouch - previousTouch; 

例如,

我有個球坐在地面上和正上方的箭頭。當您觸摸屏幕並移動箭頭時,箭頭將在半圓軸上移動。

曲線看起來像這樣Half Circle,箭頭將在軸上旋轉。

請讓我知道,如果我需要更清楚。我真的需要一些幫助。

+1

+1這是一個很好的問題我有麻煩的答案 – rptwsthi

+1

謝謝,我希望有人可以幫助 – Jonathan

回答

2

是否面臨着同樣的問題幾天。在這個答案中的大多數鏈接被打破,所以我找到了材料herehere,並提出了這個代碼。像魔術一樣工作。希望它能幫助別人。 (自我)女巫通過手指圍繞另一個對象(self.target)旋轉,並且我有一些像self.starget通過貝塞爾函數旋轉的自我運動向導的動畫精靈。算法相當快,我有100多個指南的永久初始化,它可以在沒有CPU過載的情況下工作。

/** 
Each bezier curve is an array with 8 floats, x1, y1, x2, y2, x3, y3, x4, y4., where x1,y1 and x4,y4 are the arc's end points and x2,y2 and x3,y3 are the cubic bezier's control points. 
@note adapted for xCode by Valentine Konov valentine\@konov.su 2013 

@return a array of objects that represent bezier curves which approximate the circular arc centered at the origin. 
@param startAngle to endAngle (radians) with the specified radius. 
*/ 

-(NSArray*)createArcWithRadius:(float)radius_ withStartAngle:(float)startAngle_ withEndAngle:(float)endAngle_; 
{ 
// OMLog(@"radius:%.2f startAngle:%.2f endAngle:%.2f",radius_,startAngle_,endAngle_); 
    // normalize startAngle, endAngle to [-2PI, 2PI] 

    float twoPI = M_PI * 2; 
    float startAngle = startAngle_; 
    float endAngle = endAngle_; 
    // float startAngle = fmodf(startAngle_,twoPI); 
    // float endAngle = fmodf(endAngle_,twoPI); 

    // Compute the sequence of arc curves, up to PI/2 at a time. Total arc angle 
    // is less than 2PI. 

    NSMutableArray* curves = [NSMutableArray array]; 
    float piOverTwo = M_PI/2.0; 
    float sgn = (startAngle < endAngle) ? 1 : -1; 

    float a1 = startAngle; 
    for (float totalAngle = fminf(twoPI, fabsf(endAngle - startAngle)); totalAngle > 0.00001f /*FLT_EPSILON*/; nil) { 
     float a2 = a1 + sgn * min(totalAngle, piOverTwo); 
     [curves addObject: [self createSmallArc:radius_ a1:a1 a2:a2]]; 
     totalAngle -= fabsf(a2 - a1); 
     a1 = a2; 
    } 
    return curves; 
} 

/** 
Cubic bezier approximation of a circular arc centered at the origin, 

This algorithm is based on the approach described in: 
A. Riškus, "Approximation of a Cubic Bezier Curve by Circular Arcs and Vice Versa," 
Information Technology and Control, 35(4), 2006 pp. 371-378. 
@note adapted for xCode by Valentine Konov valentine\@konov.su 2013 

@param from (radians) a1 to a2, where a2-a1 < pi/2 

@return an array with 8 floats, x1, y1, x2, y2, x3, y3, x4, y4. where x1,y1 and x4,y4 are the arc's end points and x2,y2 and x3,y3 are the cubic bezier's control points. 

*/ 
-(NSArray*)createSmallArc:(float)r a1:(float)a1 a2:(float)a2 
{ 
    // Compute all four points for an arc that subtends the same total angle 
    // but is centered on the X-axis 

    float a = (a2 - a1)/2.0; // 

    float x4 = r * cosf(a); 
    float y4 = r * sinf(a); 
    float x1 = x4; 
    float y1 = -y4; 

    float k = 0.5522847498; 
    float f = k * tan(a); 

    float x2 = x1 + f * y4; 
    float y2 = y1 + f * x4; 
    float x3 = x2; 
    float y3 = -y2; 

    // Find the arc points actual locations by computing x1,y1 and x4,y4 
    // and rotating the control points by a + a1 

    float ar = a + a1; 
    float cos_ar = cosf(ar); 
    float sin_ar = sinf(ar); 


    return [NSArray arrayWithObjects:       // 
     [NSNumber numberWithFloat:(r * cosf(a1))],    //startPoint.x 
     [NSNumber numberWithFloat:(r * sinf(a1))],    //startPoint.y 
     [NSNumber numberWithFloat:(x2 * cos_ar - y2 * sin_ar)], //ctrlPoint1.x 
     [NSNumber numberWithFloat:(x2 * sin_ar + y2 * cos_ar)], //ctrlPoint1.y 
     [NSNumber numberWithFloat:(x3 * cos_ar - y3 * sin_ar)], //ctrlPoint2.x 
     [NSNumber numberWithFloat:(x3 * sin_ar + y3 * cos_ar)], //ctrlPoint2.y 
     [NSNumber numberWithFloat:(r * cosf(a2))],    //endPoint.x 
     [NSNumber numberWithFloat:(r * sinf(a2))],    //endPoint.y 
     nil]; 
} 

/** 
Bezier approximation example 

@note adapted for xCode by Valentine Konov valentine\@konov.su 2013 

@param inSprite_ is sprite, angle_ signed angle radiants 

@return CCSequence of [CCSpawns of (CCBezierTo and CCRotateBy)] 

*/ 

-(id)calcBezierCircle:(CCSprite*)inSprite_ withAngle:(float)angle_ 
{ 
    double speed = 100; //points per second 

    CGPoint positionOffset = ccpSub(((CCNode*)self.target).position, self.position); 
    //((CCNode*)self.target).position is circle center 
    double startAngle = [self calcAngle:inSprite_.position ownerRelated:false]; 
    while (startAngle<0) startAngle += 2*M_PI; 
    while (startAngle>=2*M_PI) startAngle -= 2*M_PI; 
    double endAngle = startAngle + angle_; 
    float radius = [self calcRadius]; 

    NSArray* curves = [self createArcWithRadius:radius withStartAngle:startAngle withEndAngle:endAngle]; 
    NSMutableArray* bezierActions = [NSMutableArray array]; 
    for (NSArray* curve in curves) { 
     CGPoint startPoint = ccpAdd(ccp([[curve objectAtIndex:0] floatValue], [[curve objectAtIndex:1] floatValue]), positionOffset); 
     CGPoint controlPoint1 = ccpAdd(ccp([[curve objectAtIndex:2] floatValue], [[curve objectAtIndex:3] floatValue]), positionOffset); 
     CGPoint controlPoint2 = ccpAdd(ccp([[curve objectAtIndex:4] floatValue], [[curve objectAtIndex:5] floatValue]), positionOffset); 
     CGPoint endPoint =  ccpAdd(ccp([[curve objectAtIndex:6] floatValue], [[curve objectAtIndex:7] floatValue]), positionOffset); 

     ccBezierConfig bezier; 
     bezier.controlPoint_1 = controlPoint1; 
     bezier.controlPoint_2 = controlPoint2; 
     bezier.endPosition =endPoint; 

     float bezierAngle = ccpAngleSigned(ccpSub(startPoint, positionOffset), ccpSub(endPoint, positionOffset)); 
     float bezierDuration = radius*fabsf(bezierAngle)/speed; 
     id bezierTo = [CCBezierTo actionWithDuration:bezierDuration bezier:bezier]; 
     id rotateBy = [CCRotateBy actionWithDuration:bezierDuration angle:CC_RADIANS_TO_DEGREES(-bezierAngle)]; 
     CCAction * bezierToAndRotateBy = [CCSpawn actions:bezierTo, rotateBy, nil]; 

     [bezierActions addObject:bezierToAndRotateBy]; 
    } 
    if ([bezierActions count]<1) { 
     return nil; 
    } 
    return [CCSequence actionWithArray:bezierActions]; 
} 


/** 
Calculates angle 
@param position_ current position of sprite on sircle, ownerRelated boolean, wich is startPoint is {1,0} or owner.position 
@return angle (radiant) 
*/ 
-(float)calcAngle:(CGPoint)position_ ownerRelated:(bool)ownerRelated { 
    if (ownerRelated) { 
     CGPoint v1 = ccpSub(((CCNode*)self.target).position, self.position); 
     CGPoint v2 = ccpSub(ccpSub(((CCNode*)self.target).position, self.position),position_); 
     return ccpAngleSigned(v1, v2); 
    } 
    else { 
     CGPoint v1 = ccp([self calcRadius], 0.0f); 
     CGPoint v2 = ccpSub(position_,ccpSub(((CCNode*)self.target).position, self.position)); 
     return ccpAngleSigned(v1, v2); 
    } 
} 

/** 
Calculates radius 
@return radius 
*/ 
-(float)calcRadius; 
{ 
    return sqrt(pow(self.position.x-((CCSprite*)self.target).position.x, 2)+pow(self.position.y-((CCSprite*)self.target).position.y, 2)); 
} 
0

從未嘗試,但我認爲這是可能的,基本上你想要做的是,當用戶觸摸屏幕時,箭頭移動到曲線上的觸摸位置,箭頭在曲線上旋轉?

首先你需要做的箭頭旋轉,然後執行貝塞爾動作

//on touch 
CGSize s = [[CCDirector sharedDirector] winSize]; 

//rotate action we make the arrow rotate forever 
id actionBy = [CCRotateBy actionWithDuration:2 angle: 360]; 
[arrow runAction: [CCRepeatForever actionWithAction:actionBy]]; 

//bezier action 
ccBezierConfig bezier; 
bezier.controlPoint_1 = ccp(0, s.height/2); 
bezier.controlPoint_2 = ccp(300, -s.height/2); 
bezier.endPosition = ccp(300,100); 

id bezierForward = [CCBezierBy actionWithDuration:3 bezier:bezier]; 
id action = [CCCallFunc actionWithTarget:self selector:@selector(endAction)]; 
[arrow runAction:[CCSequence actions:bezierForward, action]]; 

//write a method endAction 
[arrow stopAllActions];