I have an iphone version of an App that draws lines on the screen from CGPoints that are stored in a Core Data. all of the drawings are line based without any fill, so basically it draws a line from given point to the next point etc.
Now i am making an iPad version and i want to use the same points (The points were collected with a function I build for tracking the screen and it was a lot of work so I wish to reuse the same points i have).
Does any body has an idea, algorithm or function for drawing the same lines' from the same points but X2 size ?
That is the draw method:(took it from the GLPaint example of apple)
- (void) playback:(NSNumber*)index
{
if (p==0) {
pointsCount=[[localpoints objectAtIndex:[index intValue]] count]-1;
}
isPlayBackOn = YES;
LetterPoint *point1 = (LetterPoint*)[[localpoints objectAtIndex:[index intValue]] objectAtIndex:p];
CGPoint p1 = CGPointFromString(point1.float_point);
LetterPoint *point2 = (LetterPoint*)[[localpoints objectAtIndex:[index intValue]] objectAtIndex:p+1];
CGPoint p2 = CGPointFromString(point2.float_point);
[self renderLineFromPoint:p1 toPoint:p2];
p++;
if(p<pointsCount){
[self performSelector:#selector(playback:) withObject:index afterDelay:0.03];
}else {
p=0;
isPlayBackOn = NO;
}
}
thanks
shani
Create an affine transform matrix with a scale factor of 2.0 (and possibly a translation if you want to move the origin of the drawing). Then apply that transform to every point with CGPointApplyAffineTransform() and use the resulting points for drawing.
well, just double p1.x and p1.y...
transform it just with this:
instead of:
CGPoint p1 = CGPointFromString(point1.float_point);
do this:
CGPoint p1Temp = CGPointFromString(point1.float_point);
CGPoint p1 = CGPointMake(p1Temp.x * 2, p1Temp.y * 2);
and the same for p2...
Related
I have a simple oval shape (comprised of CGMutablePaths) from which I'd like the user to be able to drag an object around it. Just wondering how complicated it is to do this, do I need to know a ton of math and physics, or is there some simple built in way that will allow me to do this? IE the user drags this object around the oval, and it orbits it.
This is an interesting problem. We want to drag an object, but constrain it to lie on a CGPath. You said you have “a simple oval shape”, but that's boring. Let's do it with a figure 8. It'll look like this when we're done:
So how do we do this? Given an arbitrary point, finding the nearest point on a Bezier spline is rather complicated. Let's do it by brute force. We'll just make an array of points closely spaced along the path. The object starts out on one of those points. As we try to drag the object, we'll look at the neighboring points. If either is nearer, we'll move the object to that neighbor point.
Even getting an array of closely-spaced points along a Bezier curve is not trivial, but there is a way to get Core Graphics to do it for us. We can use CGPathCreateCopyByDashingPath with a short dash pattern. This creates a new path with many short segments. We'll take the endpoints of each segment as our array of points.
That means we need to iterate over the elements of a CGPath. The only way to iterate over the elements of a CGPath is with the CGPathApply function, which takes a callback. It would be much nicer to iterate over path elements with a block, so let's add a category to UIBezierPath. We start by creating a new project using the “Single View Application” template, with ARC enabled. We add a category:
#interface UIBezierPath (forEachElement)
- (void)forEachElement:(void (^)(CGPathElement const *element))block;
#end
The implementation is very simple. We just pass the block as the info argument of the path applier function.
#import "UIBezierPath+forEachElement.h"
typedef void (^UIBezierPath_forEachElement_Block)(CGPathElement const *element);
#implementation UIBezierPath (forEachElement)
static void applyBlockToPathElement(void *info, CGPathElement const *element) {
__unsafe_unretained UIBezierPath_forEachElement_Block block = (__bridge UIBezierPath_forEachElement_Block)info;
block(element);
}
- (void)forEachElement:(void (^)(const CGPathElement *))block {
CGPathApply(self.CGPath, (__bridge void *)block, applyBlockToPathElement);
}
#end
For this toy project, we'll do everything else in the view controller. We'll need some instance variables:
#implementation ViewController {
We need an ivar to hold the path that the object follows.
UIBezierPath *path_;
It would be nice to see the path, so we'll use a CAShapeLayer to display it. (We need to add the QuartzCore framework to our target for this to work.)
CAShapeLayer *pathLayer_;
We'll need to store the array of points-along-the-path somewhere. Let's use an NSMutableData:
NSMutableData *pathPointsData_;
We'll want a pointer to the array of points, typed as a CGPoint pointer:
CGPoint const *pathPoints_;
And we need to know how many of those points there are:
NSInteger pathPointsCount_;
For the “object”, we'll have a draggable view on the screen. I'm calling it the “handle”:
UIView *handleView_;
We need to know which of the path points the handle is currently on:
NSInteger handlePathPointIndex_;
And while the pan gesture is active, we need to keep track of where the user has tried to drag the handle:
CGPoint desiredHandleCenter_;
}
Now we have to get to work initializing all those ivars! We can create our views and layers in viewDidLoad:
- (void)viewDidLoad {
[super viewDidLoad];
[self initPathLayer];
[self initHandleView];
[self initHandlePanGestureRecognizer];
}
We create the path-displaying layer like this:
- (void)initPathLayer {
pathLayer_ = [CAShapeLayer layer];
pathLayer_.lineWidth = 1;
pathLayer_.fillColor = nil;
pathLayer_.strokeColor = [UIColor blackColor].CGColor;
pathLayer_.lineCap = kCALineCapButt;
pathLayer_.lineJoin = kCALineJoinRound;
[self.view.layer addSublayer:pathLayer_];
}
Note that we haven't set the path layer's path yet! It's too soon to know the path at this time, because my view hasn't been laid out at its final size yet.
We'll draw a red circle for the handle:
- (void)initHandleView {
handlePathPointIndex_ = 0;
CGRect rect = CGRectMake(0, 0, 30, 30);
CAShapeLayer *circleLayer = [CAShapeLayer layer];
circleLayer.fillColor = nil;
circleLayer.strokeColor = [UIColor redColor].CGColor;
circleLayer.lineWidth = 2;
circleLayer.path = [UIBezierPath bezierPathWithOvalInRect:CGRectInset(rect, circleLayer.lineWidth, circleLayer.lineWidth)].CGPath;
circleLayer.frame = rect;
handleView_ = [[UIView alloc] initWithFrame:rect];
[handleView_.layer addSublayer:circleLayer];
[self.view addSubview:handleView_];
}
Again, it's too soon to know exactly where we'll need to put the handle on screen. We'll take care of that at view layout time.
We also need to attach a pan gesture recognizer to the handle:
- (void)initHandlePanGestureRecognizer {
UIPanGestureRecognizer *recognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handleWasPanned:)];
[handleView_ addGestureRecognizer:recognizer];
}
At view layout time, we need to create the path based on the size of the view, compute the points along the path, make the path layer show the path, and make sure the handle is on the path:
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
[self createPath];
[self createPathPoints];
[self layoutPathLayer];
[self layoutHandleView];
}
In your question, you said you're using a “simple oval shape”, but that's boring. Let's draw a nice figure 8. Figuring out what I'm doing is left as an exercise for the reader:
- (void)createPath {
CGRect bounds = self.view.bounds;
CGFloat const radius = bounds.size.height / 6;
CGFloat const offset = 2 * radius * M_SQRT1_2;
CGPoint const topCenter = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds) - offset);
CGPoint const bottomCenter = { topCenter.x, CGRectGetMidY(bounds) + offset };
path_ = [UIBezierPath bezierPath];
[path_ addArcWithCenter:topCenter radius:radius startAngle:M_PI_4 endAngle:-M_PI - M_PI_4 clockwise:NO];
[path_ addArcWithCenter:bottomCenter radius:radius startAngle:-M_PI_4 endAngle:M_PI + M_PI_4 clockwise:YES];
[path_ closePath];
}
Next we're going to want to compute the array of points along that path. We'll need a helper routine to pick out the endpoint of each path element:
static CGPoint *lastPointOfPathElement(CGPathElement const *element) {
int index;
switch (element->type) {
case kCGPathElementMoveToPoint: index = 0; break;
case kCGPathElementAddCurveToPoint: index = 2; break;
case kCGPathElementAddLineToPoint: index = 0; break;
case kCGPathElementAddQuadCurveToPoint: index = 1; break;
case kCGPathElementCloseSubpath: index = NSNotFound; break;
}
return index == NSNotFound ? 0 : &element->points[index];
}
To find the points, we need to ask Core Graphics to “dash” the path:
- (void)createPathPoints {
CGPathRef cgDashedPath = CGPathCreateCopyByDashingPath(path_.CGPath, NULL, 0, (CGFloat[]){ 1.0f, 1.0f }, 2);
UIBezierPath *dashedPath = [UIBezierPath bezierPathWithCGPath:cgDashedPath];
CGPathRelease(cgDashedPath);
It turns out that when Core Graphics dashes the path, it can create segments that slightly overlap. We'll want to eliminate those by filtering out each point that's too close to its predecessor, so we'll define a minimum inter-point distance:
static CGFloat const kMinimumDistance = 0.1f;
To do the filtering, we'll need to keep track of that predecessor:
__block CGPoint priorPoint = { HUGE_VALF, HUGE_VALF };
We need to create the NSMutableData that will hold the CGPoints:
pathPointsData_ = [[NSMutableData alloc] init];
At last we're ready to iterate over the elements of the dashed path:
[dashedPath forEachElement:^(const CGPathElement *element) {
Each path element can be a “move-to”, a “line-to”, a “quadratic-curve-to”, a “curve-to” (which is a cubic curve), or a “close-path”. All of those except close-path define a segment endpoint, which we pick up with our helper function from earlier:
CGPoint *p = lastPointOfPathElement(element);
if (!p)
return;
If the endpoint is too close to the prior point, we discard it:
if (hypotf(p->x - priorPoint.x, p->y - priorPoint.y) < kMinimumDistance)
return;
Otherwise, we append it to the data and save it as the predecessor of the next endpoint:
[pathPointsData_ appendBytes:p length:sizeof *p];
priorPoint = *p;
}];
Now we can initialize our pathPoints_ and pathPointsCount_ ivars:
pathPoints_ = (CGPoint const *)pathPointsData_.bytes;
pathPointsCount_ = pathPointsData_.length / sizeof *pathPoints_;
But we have one more point we need to filter. The very first point along the path might be too close to the very last point. If so, we'll just discard the last point by decrementing the count:
if (pathPointsCount_ > 1 && hypotf(pathPoints_[0].x - priorPoint.x, pathPoints_[0].y - priorPoint.y) < kMinimumDistance) {
pathPointsCount_ -= 1;
}
}
Blammo. Point array created. Oh yeah, we also need to update the path layer. Brace yourself:
- (void)layoutPathLayer {
pathLayer_.path = path_.CGPath;
pathLayer_.frame = self.view.bounds;
}
Now we can worry about dragging the handle around and making sure it stays on the path. The pan gesture recognizer sends this action:
- (void)handleWasPanned:(UIPanGestureRecognizer *)recognizer {
switch (recognizer.state) {
If this is the start of the pan (drag), we just want to save the starting location of the handle as its desired location:
case UIGestureRecognizerStateBegan: {
desiredHandleCenter_ = handleView_.center;
break;
}
Otherwise, we need to update the desired location based on the drag, and then slide the handle along the path toward the new desired location:
case UIGestureRecognizerStateChanged:
case UIGestureRecognizerStateEnded:
case UIGestureRecognizerStateCancelled: {
CGPoint translation = [recognizer translationInView:self.view];
desiredHandleCenter_.x += translation.x;
desiredHandleCenter_.y += translation.y;
[self moveHandleTowardPoint:desiredHandleCenter_];
break;
}
We put in a default clause so clang won't warn us about the other states that we don't care about:
default:
break;
}
Finally we reset the translation of the gesture recognizer:
[recognizer setTranslation:CGPointZero inView:self.view];
}
So how do we move the handle toward a point? We want to slide it along the path. First, we have to figure out which direction to slide it:
- (void)moveHandleTowardPoint:(CGPoint)point {
CGFloat earlierDistance = [self distanceToPoint:point ifHandleMovesByOffset:-1];
CGFloat currentDistance = [self distanceToPoint:point ifHandleMovesByOffset:0];
CGFloat laterDistance = [self distanceToPoint:point ifHandleMovesByOffset:1];
It's possible that both directions would move the handle further from the desired point, so let's bail out in that case:
if (currentDistance <= earlierDistance && currentDistance <= laterDistance)
return;
OK, so at least one of the directions will move the handle closer. Let's figure out which one:
NSInteger direction;
CGFloat distance;
if (earlierDistance < laterDistance) {
direction = -1;
distance = earlierDistance;
} else {
direction = 1;
distance = laterDistance;
}
But we've only checked the nearest neighbors of the handle's starting point. We want to slide as far as we can along the path in that direction, as long as the handle is getting closer to the desired point:
NSInteger offset = direction;
while (true) {
NSInteger nextOffset = offset + direction;
CGFloat nextDistance = [self distanceToPoint:point ifHandleMovesByOffset:nextOffset];
if (nextDistance >= distance)
break;
distance = nextDistance;
offset = nextOffset;
}
Finally, update the handle's position to our newly-discovered point:
handlePathPointIndex_ += offset;
[self layoutHandleView];
}
That just leaves the small matter of computing the distance from the handle to a point, should the handle be moved along the path by some offset. Your old buddy hypotf computes the Euclidean distance so you don't have to:
- (CGFloat)distanceToPoint:(CGPoint)point ifHandleMovesByOffset:(NSInteger)offset {
int index = [self handlePathPointIndexWithOffset:offset];
CGPoint proposedHandlePoint = pathPoints_[index];
return hypotf(point.x - proposedHandlePoint.x, point.y - proposedHandlePoint.y);
}
(You could speed things up by using squared distances to avoid the square roots that hypotf is computing.)
One more tiny detail: the index into the points array needs to wrap around in both directions. That's what we've been relying on the mysterious handlePathPointIndexWithOffset: method to do:
- (NSInteger)handlePathPointIndexWithOffset:(NSInteger)offset {
NSInteger index = handlePathPointIndex_ + offset;
while (index < 0) {
index += pathPointsCount_;
}
while (index >= pathPointsCount_) {
index -= pathPointsCount_;
}
return index;
}
#end
Fin. I've put all of the code in a gist for easy downloading. Enjoy.
Can you let me know what is the best way to draw a line or rectangle on a scene layer using Cocos2d ios4 iphone.
So far have tried Texture2d, but it is more like a paint brush and is not so good. Tried drawing a line using draw method, but previous line disappears on drawing another line.
Basically want to draw multiple horizontal ,vertical, oblique beams. Please suggest. Any code would help a lot .
The code to draw using texture is below:
CGPoint start = edge.start;
CGPoint end = edge.end;
// begin drawing to the render texture
[target begin];
// for extra points, we'll draw this smoothly from the last position and vary the sprite's
// scale/rotation/offset
float distance = ccpDistance(start, end);
if (distance > 1)
{
int d = (int)distance;
for (int i = 0; i < d; i++)
{
float difx = end.x - start.x;
float dify = end.y - start.y;
float delta = (float)i / distance;
[brush setPosition:ccp(start.x + (difx * delta), start.y + (dify * delta))];
[brush setScale:0.3];
// Call visit to draw the brush, don't call draw..
[brush visit];
}
}
// finish drawing and return context back to the screen
[target end];
The rendering is not good esp. with oblique lines as the scaling affects the quality.
Cheers
You could create a separate layer and call the draw method like this:
-(void) draw
{
CGSize s = [[Director sharedDirector] winSize];
drawCircle( ccp(s.width/2, s.height/2), circleSize, 0, 50, NO);
It's for a circle but the principle is the same. This is from a project I made a while back and it worked then. Don't know if anything has changed since.
You need to add draw method to your layer:
-(void) draw {
// ...
}
Inside it you can use some openGL like functions and cocos2d wrapper methods for openGL.
Hint: other methods can be called inside draw method.
But keep in mind that using other name for method
containing openGL instructions, that's not called inside draw mentioned above simply won't work.
Even when called from update method or other method used by scheduleUpdate selector.
So you will end up with something like this:
-(void) draw {
glEnable(GL_LINE_SMOOTH);
glColor4ub(255, 0, 100, 255);
glLineWidth(4);
CGPoint verts[] = { ccp(0,200), ccp(300,200) };
ccDrawLine(verts[0], verts[1]);
[self drawSomething];
[self drawSomeOtherStuffFrom:ccp(a,b) to:ccp(c,d)];
[someObject doSomeDrawingAsWell];
}
For more information check out cocos2d-iphone programming guide :
http://www.cocos2d-iphone.org/wiki/doku.php/prog_guide:draw_update?s[]=schedule#draw
I'm creating a drawing app ( text ) for the iPad using OpenGL. I've already had a look at Apple's example GLPaint, and my app is now based on that code. My app should be just for drawing text, not for painting pictures.
Well, my App works, I can write some text. But the writing isn't really good, it doesn't make fun to write. The drawing path isn't smooth, it's angularly because I'm drawing a line from one point to another. And the path has everywhere the same width. My idea is: when you're writing fast the line is thinner than when you're writing slow. It should be the same experience like writing with a real pen.
How can I make the path look much smoother? How can I vary the width of the line depending on the speed of writing?
Here you can see what I mean:
The best way to smooth the drawing is use a bezeir curve. Here is my code. It is a modified version I found on apple's dev site, but I don't remember the original link:
CGPoint drawBezier(CGPoint origin, CGPoint control, CGPoint destination, int segments)
{
CGPoint vertices[segments/2];
CGPoint midPoint;
glDisable(GL_TEXTURE_2D);
float x, y;
float t = 0.0;
for(int i = 0; i < (segments/2); i++)
{
x = pow(1 - t, 2) * origin.x + 2.0 * (1 - t) * t * control.x + t * t * destination.x;
y = pow(1 - t, 2) * origin.y + 2.0 * (1 - t) * t * control.y + t * t * destination.y;
vertices[i] = CGPointMake(x, y);
t += 1.0 / (segments);
}
//windowHeight is the height of you drawing canvas.
midPoint = CGPointMake(x, windowHeight - y);
glVertexPointer(2, GL_FLOAT, 0, vertices);
glDrawArrays(GL_POINTS, 0, segments/2);
return midPoint;
}
That will draw based on three points. The control is the midpoint, which you need to return. The new midpoint will be different than the previous. Also, if you go through the above code, it will only draw half the line. The next stroke will fill it in. This is required. my code for calling this function (the above is in C, this is in Obj-C):
//Invert the Y axis to conform the iPhone top-down approach
invertedYBegCoord = self.bounds.size.height - [[currentStroke objectAtIndex:i] CGPointValue].y;
invertedYEndCoord = self.bounds.size.height - [[currentStroke objectAtIndex:i+1] CGPointValue].y;
invertedYThirdCoord = self.bounds.size.height - [[currentStroke objectAtIndex:i+2] CGPointValue].y;
//Figure our how many dots you need
count = MAX(ceilf(sqrtf(([[currentStroke objectAtIndex:i+2] CGPointValue].x - [[currentStroke objectAtIndex:i] CGPointValue].x)
* ([[currentStroke objectAtIndex:i+2] CGPointValue].x - [[currentStroke objectAtIndex:i] CGPointValue].x)
+ ((invertedYThirdCoord - invertedYBegCoord) * (invertedYThirdCoord - invertedYBegCoord))) / pointCount), 1);
newMidPoint = drawBezier(CGPointMake([[currentStroke objectAtIndex:i] CGPointValue].x, invertedYBegCoord), CGPointMake([[currentStroke objectAtIndex:i+1] CGPointValue].x, invertedYEndCoord), CGPointMake([[currentStroke objectAtIndex:i+2] CGPointValue].x, invertedYThirdCoord), count);
int loc = [currentStroke count]-1;
[currentStroke insertObject:[NSValue valueWithCGPoint:newMidPoint] atIndex:loc];
[currentStroke removeObjectAtIndex:loc-1];
That code will get the mid point based on inverted iPad points, and set the 'control' as the current point.
That will smooth out the edges. Now regarding the line width, you just need to find the speed of that drawing. It is easiest just to find the length of your line. This is easily done using component mathematics. I don't have any code for it, but here is a primer for component mathmatics from a physics site. Or you can simply divide (above) count by some number to find out how thick you need the line to be (count uses component mathematics).
I store point data in an array called currentStroke, in case it wasn't obvious.
That should be all you need.
EDIT:
To store points, you should use touchesBegin and touchesEnd:
- (void) touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event
{
self.currentStroke = [NSMutableArray array];
CGPoint point = [ [touches anyObject] locationInView:self];
[currentStroke addObject:[NSValue valueWithCGPoint:point]];
[self draw];
}
- (void) touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
CGPoint point = [ [touches anyObject] locationInView:self];
[currentStroke addObject:[NSValue valueWithCGPoint:point]];
[self draw];
}
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
CGPoint point = [ [touches anyObject] locationInView:self];
[currentStroke addObject:[NSValue valueWithCGPoint:point]];
[self draw];
}
That is pretty much an entire drawing application there. If you are using GL_Paint, then you are already using point sprites, which this system is build on.
With regards to the second part of your question (how to vary the width of the line depending on the speed of writing), you should be able to achieve this by taking advantage of UITouch's timestamp property in your touchesBegan:withEvent: and touchesMoved:withEvent: method.
You can calculate the time difference between two subsequent touch events by storing the timestamp of the most recent UITouch object and comparing it to the new one. Dividing the distance of the swiping motion by the time difference should give you some measurement of the movement speed.
Now all you need to do is to come up with a way to convert speed of writing into line width, which will probably come down to picking an arbitrary value and adjusting it until you're happy with the result.
I have a buttonsthat I add on a UIImageView. With a method when the user touch the screen
the UIImageView will rotate, I want to know if there is a way to get the new position of the button after the rotation is done.
Right now I'm getting all the time the original position with this method :
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
NSLog(#"Xposition : %f", myButton.frame.origin.x);
NSLog(#"Yposition : %f", myButton.frame.origin.y);
}
Thanks,
This is a tricky question. Referring to the UIView documentation on the frame property it states:
Warning: If the transform property is not the identity transform, the value of this property is undefined and therefore should be ignored.
So the trick is finding a workaround, and it depends on what exactly you need. If you just need an approximation, or if your rotation is always a multiple of 90 degrees, the CGRectApplyAffineTransform() function might work well enough. Pass it the (untransformed) frame of the UIButton of interest, along with the button's current transform and it will give you a transformed rect. Note that since a rect is defined as an origin, width and height, it can't define a rectangle with sides not parallel to the screen edges. In the case that it isn't parallel, it will return the smallest possible bounding rectangle for the rotated rect.
Now if you need to know the exact coordinates of one or all of the transformed points, I've written code to compute them before, but it's a bit more involved:
- (void)computeCornersOfTransformedView:(UIView*)transformedView relativeToView:(UIView*)parentView {
/* Computes the coordinates of each corner of transformedView in the coordinate system
* of parentView. Each is corner represented by an independent CGPoint. Doesn't do anything
* with the transformed points because this is, after all, just an example.
*/
// Cache the current transform, and restore the view to a normal position and size.
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];
// Do something with the newly acquired points.
}
Please forgive any minor errors in the code, I wrote it in the browser. Not the most helpful IDE...
I'm developing a line drawing game, similar to Flight Control, Harbor Master, and others in the appstore, using Cocos2D.
For this game, I need a CCSprite to follow a line that the user has drawn. I'm storing a sequence of CGPoint structs in an NSArray, based on the points I get in the touchesBegin and touchesMoved messages. I now have the problem of how to make my sprite follow them.
I have a tick method, that is called at the framerate speed. In that tick method, based on the speed and current position of the sprite, I need to calculate its next position. Is there any standard way to achieve this?
My current approach is calculate the line between the last "reference point" and the next one, and calculate the next point in that line. The problem I have is when the sprite "turns" (moves from one segment of the line to another).
Any hint will be greatly appreciated.
Why do you code your own tick method? Why don't you just use the built-in CCMoveTo method?
(void) gotoNextWayPoint {
// You would need to code these functions:
CGPoint point1 = [self popCurrentWayPoint];
CGPoint point2 = [self getCurrentWayPoint];
// Calculate distance from last way point to next way point
CGFloat dx = point2.x - point1.x;
CGFloat dy = point2.y - point1.y;
float distance = sqrt( dx*dx + dy*dy );
// Calculate angle of segment
float angle = atan2(dy, dx);
// Rotate sprite to angle of next segment
// You could also do this as part of the sequence (or CCSpawn actually) below
// gradually as it approaches the next way point, but you would need the
// angle of the line between the next and next next way point
[mySprite setRotation: angle];
CCTime segmentDuration = distance/speed;
// Animate this segment, and afterward, call this function again
CCAction *myAction = [CCSequence actions:
[CCMoveTo actionWithDuration: segmentDuration position: nextWayPoint],
[CCCallFunc actionWithTarget: self selector: #selector(gotoNextWayPoint)],
nil];
[mySprite runAction: myAction];
}