SKAction followPath, creating the path - ios

I am using a simple A* Pathfinding algorithm to create a path between two points (the green and red circles below), each square is an SKSpriteNode (with a CGPoint [x,y] position). I want to animate another SKSpriteNode along a smooth path that passes through each point.
My thinking was that I could use the SKAction -followPath:duration: to do this but I am having trouble working out how to create/specify the CGPath. Any help would be much appreciated.

You can create a simple CGPath like this
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, 0, 0);
CGPathAddLineToPoint(path, NULL, 100, 0);
CGPathAddLineToPoint(path, NULL, 100, 100);
Depending on what you want to achieve you can add different elements to your path like arcs, curves, rects...
You can find more about it here.

Related

How to copy a region of an image enclosed by cubic curves

I have a Java program that works very similar to the Bezier tool in Inkscape. The purpose of my program is to allow the user to use the curves to draw a path around an object (like the head of a person) and then extract (copy) the pixels inside the enclosed curves. In the attached picture, you can see that there are 3 blue curves that form an enclosed area. I'd like to know how to copy the area enclosed by these 3 curves?
The code I use to draw the curves (I omit the red tangent lines and the red control points for simplicity):
cubicCurve1 = new CubicCurve2D.Double(
p1.x, p1.y,
p1Control1.x, p1Control1.y,
p2Control1.x, p2Control1.y,
p2.x, p2.y);
cubicCurve2 = new CubicCurve2D.Double(
p2.x, p2.y,
p2Control2.x, p2Control2.y,
p3Control1.x, p3Control1.y,
p3.x, p3.y);
cubicCurve3 = new CubicCurve2D.Double(
p3.x, p3.y,
p3Control2.x, p3Control2.y,
p1Control1.x, p1Control1.y,
p1.x, p1.y);
g2D.setPaint(Color.BLUE);
g2D.draw(cubicCurve1);
g2D.draw(cubicCurve2);
g2D.draw(cubicCurve3);
Here's how I solved my problem:
GeneralPath shape = new GeneralPath();
shape.moveTo(cubicCurve1.x1, cubicCurve1.y1);
shape.curveTo(cubicCurve1.ctrlx1, cubicCurve1.ctrly1, cubicCurve1.ctrlx2, cubicCurve1.ctrly2, cubicCurve1.x2, cubicCurve1.y2);
shape.curveTo(cubicCurve2.ctrlx1, cubicCurve2.ctrly1, cubicCurve2.ctrlx2, cubicCurve2.ctrly2, cubicCurve2.x2, cubicCurve2.y2);
shape.curveTo(cubicCurve3.ctrlx1, cubicCurve3.ctrly1, cubicCurve3.ctrlx2, cubicCurve3.ctrly2, cubicCurve1.x1, cubicCurve1.y1);
g2D.draw(shape);
g2D.setClip(shape);
// Draw an image
g2D.drawImage(image, 0, 0, this);

Drawing line with SpriteKit and detecting collision

I have managed to draw a line with array of my random CGPoints.
-(void)drawLine
{
SKShapeNode *mainLine = [SKShapeNode node];
CGMutablePathRef pathToDraw = CGPathCreateMutable();
CGPathMoveToPoint(pathToDraw, NULL, 0, 0);
for(int i;i<pointsInMyView.count;i++)
{
CGPoint myPoint = [pointsInMyView[i] CGPointValue];
CGPathAddLineToPoint(pathToDraw, NULL, myPoint.x, myPoint.y);
mainLine.path = pathToDraw;
}
[mainLine setLineWidth:40];
[mainLine setStrokeColor:[SKColor whiteColor]];
mainLine.name = #"mainLine";
[self addChild:mainLine];
}
As you can see I am drawing a SKShapeNode. My goal is to check collision of my SKSpriteNode with my line. But of course, this shape node makes a frame that contains all points of my line, and in this case my ShapeNode is all over my view. My SpriteNode detects collision with this ShapeNode all the time.
I should draw multiple different ShapeNodes I guess, so every node would have its own frame. But if i do it this way, my line is not connected.
Is there some solution to draw this node by node and still get nice line.
Unfortunately nodes in SpriteKit cannot be concave (http://mathworld.wolfram.com/ConcavePolygon.html), with lines that cross over each other.
You will have to create separate nodes for each lines between adjacent points and then possibly join them together. you could do this either by:
Create the 1st segment and then add subsequent segments as children
of that 1st segment.
Create a non-visible node for the whole line and add wll the line
segments as children of that node.
Create physicsbodies for each segment and join them with
SKPhysicsJointFixed.

How to create egg shape physics body with CGPathRef?

I have to create a CGPathRef to simulate the physic property in Sprite Kit.
I am trying to create an egg shape path by an half of circle plus half of oval.
the egg's ratio is 0.4(bottom of half circle) to 0.6 (upper of half oval);
However, I don't know why nothing is happen.
following code is the creation of the path:
self.egg = [SKSpriteNode spriteNodeWithImageNamed:IMAGE_NAME_EGG];
[self.egg setScale:0.2];
self.egg.position = CGPointMake(self.size.width/2,self.size.height - self.egg.size.height/2);
self.egg.name = IMAGE_NAME_EGG;
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddArc(path, NULL, 0, self.egg.size.height*0.4, self.egg.size.width/2, M_PI, M_PI_2, NO);
CGPathAddEllipseInRect(path, NULL, self.egg.frame);
self.egg.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromPath:path];
self.egg.physicsBody.dynamic = YES;
self.egg.physicsBody.categoryBitMask = eggCategory;
self.egg.physicsBody.contactTestBitMask = floorCategory;
self.egg.physicsBody.collisionBitMask = floorCategory;
//self.eggCanMove = NO;
self.egg.physicsBody.allowsRotation = YES;
[self addChild:self.egg];
There is a better way to do this. Make a texture of your egg with p.e. Adobe Illustrator and use this method:
init(texture texture: SKTexture!,alphaThreshold alphaThreshold: CFloat,size size: CGSize) -> SKPhysicsBody
The SKTexture is your egg image. The body is defined by the colored pixels.
The alphaThresHold: The minimum alpha value for texels that should be part of the new physics body.
The Size is clear I think.
The texture is analyzed and all invisible pixels around the egg are ignored and only the color pixels are interpreted as the body of the SKPhysicsNode.
You shouldn't use too many of these because they are very expensive to calculate for the Physics Engine.
Variations of this method are found in the SpriteKit Class Reference.

Error attempting to create polygon with CGPathAddEllipseInRect

I am trying to create an ellipses. I used bodyWithEdgeLoopFromPath and it worked but there seems to be something wrong with it because sometimes other objects get caught in the middle of it.
But I want the ellipses to be solid, So I tried bodyWithPolygonFromPath (I want it static)
horizontalOval = [[SKShapeNode alloc] init];
theRect = CGRectMake(0, 0, self.frame.size.width/6 , 15);
CGMutablePathRef ovalPath = CGPathCreateMutable();
CGPathAddEllipseInRect(ovalPath, NULL, theRect);
horizontalOval.path = ovalPath;
horizontalOval.fillColor = [UIColor blueColor];
horizontalOval.physicsBody.dynamic = NO;
horizontalOval.physicsBody = [SKPhysicsBody bodyWithPolygonFromPath:ovalPath];
But got the error
SKPhysicsBody: Error attempting to create polygon with 17 vertices, maximum is 12
How do I create complex paths and make them solid?
Also when I pud it in position self.frame.size.width/2 and self.frame.size.height/2 It doesn't stay center, it goes a little to the right.
I had to theRect = CGRectMake(-40, 0........) to make it center but why is that?
UIBezierPath* ovalPath = [UIBezierPath bezierPathWithOvalInRect: _paddleRect];
But has 13 vertices. Trying to use PaintCode.
You can think of an edge body as one that has no volume, just the edges, a body with 'negative space' - that's why your objects got caught in the middle of it. As the Sprite Kit Programming Guide says:
The main difference between a edge and a volume is that an edge permits movement inside its own boundaries, while a volume is considered a solid object.
Since you want your oval to be a solid object, you do need a volume-based body. For these bodies, you have three options to create their shape: a circle (bodyWithCircleOfRadius:), a rectangle (bodyWithRectangleOfSize:), or a polygon (bodyWithPolygonFromPath:).
For an oval shape, you probably have to draw a polygon - however, the Sprite Kit physics engine will only accept those with a maximum of 12 vertices (that's why you were getting an error when drawing an actual ellipse). Your best bet is drawing a polygon using a helper tool, such as this one: http://dazchong.com/spritekit/ - just drag and drop your sprite and draw the path. Remember that the polygon must be convex (no angles over 180 degrees inside it) and that it can have a maximum of 12 vertices.
Also check out this answer for a similar issue: Ellipse SKPhysicsBody

how to get buffer zone around a uibezierpath

i have some uibezierpaths. as paths, they don't really have thickness.
but, I am hoping to find a way to define an area around a path like the grayish areas around the lines in this picture
basically, i want to test whether drawn lines fall within the buffer zone around the lines.
i thought this would be simple, but it's turning out to be much more complex than i thought. I can use the CGPathApply function to examine the points along my path, and then getting a range +or- each point, but it's more complicated than that with angles and curves. any ideas?
Expanding the width of a path is actually quite difficult. However, you could just stroke it with a thicker width and get pretty much the same effect. Something like...
CGContextSetRGBStrokeColor(context, 0.4, 0.4, 0.4, 1.0);
[path setLineWidth:15];
[path stroke];
CGContextSetRGBStrokeColor(context, 0.0, 0.0, 0.0, 1.0);
[path setLineWidth:3];
[path stroke];
...would produce a picture like the one in your question. But I doubt that's news to you.
The real trick is the test of "whether drawn lines fall within the buffer zone." That problem is very similar to one which I just answered for myself in another question. Take a look at the LineSample.zip code I shared there. This implements a bitmap/bitwise data comparison to detect hits on lines much like you need. You could just draw the thicker "buffer" paths into the bitmap for testing and show the thinner lines in your view.
Basically, you want to check if any point falls inside a region of specified size around your path.
It is actually very simple to do. First, you need a value which will define the amount of space around path you want to test. Let's say 20 points. So what you need to do is start a FOR loop, starting from -20 to 20, and at each iteration, create a copy of your path, translate the path's x and y co-odrinates, check each of them.
All of this is more clear in this code sample.
CGPoint touchPoint = /*get the point*/;
NSInteger space = 20;
for (NSInteger i = -space; i < space; i++) {
UIBezierPath *pathX = [UIBezierPath bezierPathWithCGPath:originalPath.CGPath];
[pathX applyTransform:CGAffineTransformMakeTranslation(i, 0)];
if ([pathX containsPoint:touchPoint]) {
/*YEAH!*/
}
else {
UIBezierPath *pathY = [UIBezierPath bezierPathWithCGPath:originalPath.CGPath];
[pathY applyTransform:CGAffineTransformMakeTranslation(0, i)];
if ([pathY containsPoint:touchPoint]) {
/*YEAH!*/
}
}
}

Resources