Drawing line with Touch iOS Sprite Kit
Asked Answered
F

2

0

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];
Flee answered 25/9, 2014 at 16:21 Comment(7)
This should help : #24553745Perrotta
doesn't explain why the line is not showing though? @PerrottaFlee
Added an answer. But I think both current answers, will experience the issue defined in the link. So if you are going to be drawing long lines, you should just go there and skip this approach. imoPerrotta
are you testing using a simulator or actual device? @PerrottaFlee
I tested on an iPad mini.Perrotta
Set the zPosition of lineNode to a value greater than the zPosition of the background node. The default value is zero.Flagstad
That worked fantastic! Everything works great now!Flee
P
2

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.

Perrotta answered 25/9, 2014 at 19:23 Comment(7)
this does not work for me.. what could be the problem? I still continue to have the same problem node count goes up but no visible line to be seenFlee
Works fine for me, I just copied your code into a empty project, added the iVars and one line and it works just fine? Perhaps your ViewController is set up incorrectly ? You can github the project if you want an I can look, but this definitely works and the problem exists outside this code above.Perrotta
I figured out the problem its the background node I'm adding. Ill add the code above to show you what how I do it. Am I not allowed to draw over nodes? Whats the work around? @PerrottaFlee
If you have a layering problem, set your zPosition accordingly.Perrotta
Hi @Perrotta I could use help with one more question.. Im adding a physical body to this line after( as you can see in the code commented out). What happens with my original code the line doesn't show but the physical body shape is correct. I know this when objects are interacting with this line. But with your code modification you see the line but the physical body shape is incorrect it takes the shape of the first place you touched ignoring the rest of the line. I know its taking the point of where we closed the subpath but how can I fix this issue.Flee
New question. Not good to go about answering questions in the comments as answers get hidden.Perrotta
#26089315Flee
F
2

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];
}
Flagstad answered 25/9, 2014 at 18:35 Comment(10)
this does not work for me.. what could be the problem? I still continue to have the same problem node count goes up but no visible line to be seenFlee
What happens when you run it?Flagstad
it runs fine just no line is drawn when you swipe with your finger..node count goes up though when touches endFlee
xCode 6.1 since im working with a lot of particle files and 6.0 crashes when you open themFlee
i run in 6.0.1 just to test and still same problemFlee
Try running it on a device. The simulator has issues drawing shape nodes.Flagstad
Try creating a new project, copy my code into it, and add the ivars.Flagstad
Let us continue this discussion in chat.Flee
Did you want to draw lines as the title of your question suggested or draw freehand as your code indicated?Flagstad
sorry i wanted free hand i can see how i made that confusing I apologizeFlee
P
2

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.

Perrotta answered 25/9, 2014 at 19:23 Comment(7)
this does not work for me.. what could be the problem? I still continue to have the same problem node count goes up but no visible line to be seenFlee
Works fine for me, I just copied your code into a empty project, added the iVars and one line and it works just fine? Perhaps your ViewController is set up incorrectly ? You can github the project if you want an I can look, but this definitely works and the problem exists outside this code above.Perrotta
I figured out the problem its the background node I'm adding. Ill add the code above to show you what how I do it. Am I not allowed to draw over nodes? Whats the work around? @PerrottaFlee
If you have a layering problem, set your zPosition accordingly.Perrotta
Hi @Perrotta I could use help with one more question.. Im adding a physical body to this line after( as you can see in the code commented out). What happens with my original code the line doesn't show but the physical body shape is correct. I know this when objects are interacting with this line. But with your code modification you see the line but the physical body shape is incorrect it takes the shape of the first place you touched ignoring the rest of the line. I know its taking the point of where we closed the subpath but how can I fix this issue.Flee
New question. Not good to go about answering questions in the comments as answers get hidden.Perrotta
#26089315Flee

© 2022 - 2024 — McMap. All rights reserved.