Drawing straight line with spritekit and UITouch creates multiple lines - ios

Should be simple.
I'm trying to draw a single, straight line using UITouch and Spritekit. However, when touchesmoved is called, it creates multiple lines instead of just a single line. Code used is below:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
positionInScene1 = [touch locationInNode:self];
pathToDraw = CGPathCreateMutable();
selectorLine = [SKShapeNode node];
selectorLine.strokeColor = [SKColor greenColor];
selectorLine.lineWidth = 5;
[self addChild:selectorLine];
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
pathToDraw2 = CGPathCreateMutable();
UITouch* touch = [touches anyObject];
positionInScene2 = [touch locationInNode:self];
CGPathMoveToPoint(pathToDraw2, NULL, positionInScene1.x, positionInScene1.y);
CGPathAddLineToPoint(pathToDraw2, NULL, positionInScene2.x, positionInScene2.y);
CGPathCloseSubpath(pathToDraw);
selectorLine.path = pathToDraw;
}
If I move
CGPathAddLineToPoint(pathToDraw, NULL, positionInScene2.x, positionInScene2.y);
to touchesEnd, it creates a single line but only after the user ends the touch. I want the user to see the line being drawn as they touch.
Thanks,
Doug

Create a new path in your touchesMoved. You are modifying the same path, and just adding more and more lines to it.

Related

Adding a physics body to a drawn line

I am working on an iOS app with sprite kit where I draw a line in the following way:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch* touch = [touches anyObject];
CGPoint positionInScene = [touch locationInNode:self];
pathToDraw = CGPathCreateMutable();
CGPathMoveToPoint(pathToDraw, NULL, positionInScene.x, positionInScene.y);
lineNode = [SKShapeNode node];
lineNode.path = pathToDraw;
lineNode.strokeColor = [SKColor blackColor];
lineNode.lineWidth = 5.0f;
[self addChild:lineNode];
}
- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
UITouch* touch = [touches anyObject];
CGPoint positionInScene = [touch locationInNode:self];
CGPathAddLineToPoint(pathToDraw, NULL, positionInScene.x, positionInScene.y);
lineNode.path = pathToDraw;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
CGPathRelease(pathToDraw);
}
This is working exactly how I want it to, but I am not sure where to begin with my next step.
I want to create a physics body for the line that I have just drawn.
I would preferable to this in the touchesEnded method.
How can I add a physics body to the line regardless of its width?
My best guess is to track all the points, then do some math to figure out a path along the edge of the line, then add the physics body.
Is there a better way to do this before I dive into my approach?

SKPhysicsBody from a line (not closed polygon)

I am trying to create an SKPhysicsBody from a line drawn in the scene. This line is not a closed polygon, but simply created while tracking the users finger in touchesMoved. I need to create a physics body out of this line so that a ball bounces off of it as if it were a ledge, but I can't seem to figure out how to do that.
The closest thing I've found is bodyWithPolygonFromPath, but it only works when the line has been closed into a polygon.
I'm very new to iOS programming (this is my first project) so please take it easy!
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[lineNode removeFromParent];
CGPathRelease(pathToDraw);
UITouch* touch = [touches anyObject];
CGPoint positionInScene = [touch locationInNode:self];
pathToDraw = CGPathCreateMutable();
CGPathMoveToPoint(pathToDraw, NULL, positionInScene.x, positionInScene.y);
lineNode = [SKShapeNode node];
lineNode.path = pathToDraw;
lineNode.strokeColor = [SKColor redColor];
[self addChild:lineNode];
}
- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
UITouch* touch = [touches anyObject];
CGPoint positionInScene = [touch locationInNode:self];
CGPathAddLineToPoint(pathToDraw, NULL, positionInScene.x, positionInScene.y);
lineNode.path = pathToDraw;
}
- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event
{
lineNode.physicsBody = [SKPhysicsBody bodyWithPolygonFromPath:lineNode.path];
lineNode.physicsBody.categoryBitMask = paddleCategory;
lineNode.physicsBody.collisionBitMask = ballCategory | paddleCategory;
lineNode.physicsBody.contactTestBitMask = ballCategory | paddleCategory;
}
Does the line need to move or react to collisions, or just cause collisions?
If the former, you need a dynamic body — and a dynamic body has to have volume (well, area) so an open path won't work. In this case you could either loop the path back along itself to close it (probably easy to code but hard to make work right) or approximate the path with a collection of narrow rectangular bodies.
If the latter, a static body will do. You can create one from a line segment between two points with bodyWithEdgeFromPoint:toPoint:, or from a series of segments with bodyWithEdgeChainFromPath:.

Drawing line with Touch iOS Sprite Kit

Im using the following code to draw lines in my sprite kit scene but nothing shows up. But the node counts goes up. Can anyone see what could be the problem. Ive been fighting with this code forever
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch* touch = [touches anyObject];
CGPoint positionInScene = [touch locationInNode:self];
pathToDraw = CGPathCreateMutable();
CGPathMoveToPoint(pathToDraw, NULL, positionInScene.x, positionInScene.y);
lineNode = [SKShapeNode node];
lineNode.path = pathToDraw;
lineNode.strokeColor = [SKColor redColor];
//lineNode.lineWidth = 2;
[self addChild:lineNode];
}
- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
UITouch* touch = [touches anyObject];
CGPoint positionInScene = [touch locationInNode:self];
CGPathAddLineToPoint(pathToDraw, NULL, positionInScene.x, positionInScene.y);
lineNode.path = pathToDraw;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
// lineNode.physicsBody = [SKPhysicsBody bodyWithPolygonFromPath:pathToDraw];
// lineNode.physicsBody.dynamic = NO;
// lineNode.physicsBody.categoryBitMask = lines;
// lineNode.physicsBody.contactTestBitMask = 0;
// lineNode.physicsBody.collisionBitMask = blueParticles | redParticles| yellowParticles;
CGPathRelease(pathToDraw);
}
Edit------ Code works when background node removed..How can I set up my background node so that I can daw overtop? Thanks
SKSpriteNode *background;
if(screenHeight == 480){
background = [SKSpriteNode spriteNodeWithImageNamed:#"iPhone4BG.png"];
}
if(screenHeight == 568){
background = [SKSpriteNode spriteNodeWithImageNamed:#"iPhone5BG.png"];
}
if(screenHeight == 667){
background = [SKSpriteNode spriteNodeWithImageNamed:#"iPhone6BG.png"];
}
if(screenHeight == 736){
background = [SKSpriteNode spriteNodeWithImageNamed:#"iPhone6PlusBG.png"];
}
background.position = CGPointMake(screenWidth/2, screenHeight/2);
background.size = CGSizeMake(screenWidth, screenHeight);
// [self addChild:background];
NOTE Core issue was Simulator not rendering a path that isn't closed. On the device the issue doesn't exist. Also a background element was involved that the original poster didn't mention and wasn't represented in code. This answer will solve the issue on the simulator.
To fix your problem, modify your code as follows :
- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
UITouch* touch = [touches anyObject];
CGPoint positionInScene = [touch locationInNode:self];
CGPathAddLineToPoint(pathToDraw, NULL, positionInScene.x, positionInScene.y);
// add this line to fix your issue
CGPathMoveToPoint(pathToDraw, NULL, positionInScene.x, positionInScene.y);
lineNode.path = pathToDraw;
}
The core issue is that when it goes to render the path, it doesn't have a complete path. Your current approach is to be constantly modifying the path on each touchMove.
The way you close a sub path is :
CGPathCloseSubpath(pathToDraw);
However I chose to just use CGPathMoveToPoint each time you added a segment to the path.
From CGPath reference for CGPathMoveToPoint :
This function ends the subpath already in progress (if any) and starts a new subpath, initializing the starting point and the current point to the specified location (x,y) after an optional transformation.
I am closing the current subpath before it gets rendered, and also setting the start location for the next segment.
However, you are going to run into major issues with performance as defined in the link in the the comments if you are going to be drawing a long line or many lines. Draw a really long line, and you will see what I mean. With minimal line drawing, you might get away with the current approach.
Sadly, drawing of this nature with SKShapeNode is not as optimized as it should be.
I think this is what you are trying to implement. It draws a temporary line (white) as you move your finger and draws a final line (red) when you lift your finger. You will need to declare a CGPoint instance variable named startingPoint and delete the CGMutablePathRef ivar named pathToDraw. The code can be modified to draw multiple, connected line segments as well.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch* touch = [touches anyObject];
CGPoint positionInScene = [touch locationInNode:self];
startingPoint = positionInScene;
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch* touch = [touches anyObject];
CGPoint positionInScene = [touch locationInNode:self];
// Remove temporary line if it exist
[lineNode removeFromParent];
CGMutablePathRef pathToDraw = CGPathCreateMutable();
CGPathMoveToPoint(pathToDraw, NULL, startingPoint.x, startingPoint.y);
CGPathAddLineToPoint(pathToDraw, NULL, positionInScene.x, positionInScene.y);
lineNode = [SKShapeNode node];
lineNode.path = pathToDraw;
//CGPathRelease(pathToDraw);
lineNode.strokeColor = [SKColor whiteColor];
lineNode.lineWidth = 1;
[self addChild:lineNode];
}
- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event
{
UITouch* touch = [touches anyObject];
CGPoint positionInScene = [touch locationInNode:self];
// Remove temporary line
[lineNode removeFromParent];
CGMutablePathRef pathToDraw = CGPathCreateMutable();
CGPathMoveToPoint(pathToDraw, NULL, startingPoint.x, startingPoint.y);
CGPathAddLineToPoint(pathToDraw, NULL, positionInScene.x, positionInScene.y);
SKShapeNode *finalLineNode = [SKShapeNode node];
finalLineNode.path = pathToDraw;
//CGPathRelease(pathToDraw);
finalLineNode.strokeColor = [SKColor redColor];
finalLineNode.lineWidth = 1;
[self addChild:finalLineNode];
}

How to detect touches in physicsBody area?

I did create SKSpriteNode using this code and physicsBody is circle. How I could check that user did touch eare in the physicsBody?
self = [super initWithColor:[SKColor clearColor] size:CGSizeMake(200, 200)];
//...
self.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:self.size.width/2.0f];
self.userInteractionEnabled = YES;
I did found solution for my problem using this code:
CGMutablePathRef pathRef;
// Fill pathRef with points and create phisycs body using pathRef.
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint touchLocation = [touch locationInNode:self.scene];
if (CGPathContainsPoint(pathRef, nil, touchLocation, NO)) {
// If we are here it means user did touche physic body.
}
}
The easiest solution would be to create a second invisible sprite, with the size you need, and place it directly over the area you want to record touches on.
The other option is to store ALL the coordinates which trigger a valid touch and compare them against the actual touch coordinate in your touch method, like this:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint touchLocation = [touch locationInNode:self.scene];
NSLog(#"%#", NSStringFromCGPoint(touchLocation));
// Compare the touchLocation coordinate against your list of acceptable coordinates...
}

SKPhysicsBody does not work on SKShapeNode

I am drawing a ShapeNode using UITouch as below:
The line draws correctly. However, my problem is with adding the physicsbody to the shapenode(I want a SKSpriteNode to collide and bounce). But it's as if the physicsbody never attaches to the shapenode. I've used YMCPhysicsDebugger to see where the physicsbody is, relative to the shapenode, but it doesn't detect anything.
Has anyone encountered this before? How do you successfully add a physicsbody to a dynamic shapenode?
`-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
touch = [touches anyObject];
CGPoint positionInScene = [touch locationInNode:self];
pathToDraw = CGPathCreateMutable();
CGPathMoveToPoint(pathToDraw, NULL, positionInScene.x, positionInScene.y);
selectorLine = [SKShapeNode node];
selectorLine.path = pathToDraw;
selectorLine.strokeColor = [SKColor greenColor];
selectorLine.lineWidth = 10;
selectorLine.physicsBody=[SKPhysicsBody bodyWithPolygonFromPath:pathToDraw];
selectorLine.physicsBody.restitution=1.0f;
selectorLine.physicsBody.dynamic=YES;
selectorLine.physicsBody.usesPreciseCollisionDetection=YES;
selectorLine.physicsBody.mass=0.2;
selectorLine.zPosition=1.5;
[self addChild:selectorLine];
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
touch = [touches anyObject];
CGPoint positionInScene = [touch locationInNode:self];
CGPathAddLineToPoint(pathToDraw, NULL, positionInScene.x, positionInScene.y);
selectorLine.path = pathToDraw;
}
Thanks,
Doug
I recall reading that you need to add the SKNode to it's parent and set it's position before adding the physics body...
I found the solution that solved it for me (in Swift). I had exactly the same problem as you're facing. Instead of setting the node path like
selectorLine.path = pathToDraw
try setting it in the constructor instead like
selectorLine = SKPathNode(path: pathToDraw)

Resources