3
votes

For my program I need to take a line drawn using touches moved and SKShapeNode and give it collision properties.

I looked at this thread: draw a line in sprite kit in touchesmoved where a line is drawn, but I am having trouble making it a physics body.

What I'm trying to accomplish is to drive a car through obstacles from above and below by a user drawn road. So I'm trying to get the car to touch the line and move along it...technically the car doesn't move because only the background moves.

How would I go about doing this?

Here is the important parts of my code:

I have made 3 more classes (GameHelper, StartGame, and GameOver), but they are irrelevant to my question. Here is where I have declared a CGMutablePathRef called pathToDraw and a SKShapeNode called lineNode.

#import "MyScene.h"
#import "MyScene.h"
#import "StartGameLayer.h"
#import "GameOverLayer.h"

#define TIME 1.5
#define MINIMUM_ROCK_HEIGHT 200.0f
#define GAP_BETWEEN_TOP_AND_BOTTOM_ROCK 50.0f

#define BACKGROUND_Z_POSITION    100
#define START_GAME_LAYER_Z_POSITION     150
#define GAME_OVER_LAYER_Z_POSITION      200

static const uint32_t rockCategory            =  0x1 << 0;
static const uint32_t mineCartCategory        =  0x1 << 1;
static const uint32_t lineCategory            =  0x1 << 1;



static const float BG_VELOCITY = (TIME * 60);

static inline CGPoint CGPointAdd(const CGPoint a, const CGPoint b)
{
    return CGPointMake(a.x + b.x, a.y + b.y);
}

static inline CGPoint CGPointMultiplyScalar(const CGPoint a, const CGFloat b)
{
    return CGPointMake(a.x * b, a.y * b);
}


@interface MyScene() <SKPhysicsContactDelegate, StartGameLayerDelegate, GameOverLayerDelegate>
{
    NSTimeInterval _dt;
    float bottomScrollerHeight;

    BOOL _gameStarted;
    BOOL _gameOver;

    CGMutablePathRef pathToDraw;
    SKShapeNode *lineNode;

    StartGameLayer* _startGameLayer;
    GameOverLayer* _gameOverLayer;

    int _score;
}
@property (nonatomic) SKSpriteNode* backgroundImageNode;
@property (nonatomic) SKSpriteNode* mineCart;
@property (nonatomic) NSTimeInterval lastSpawnTimeInterval;
@property (nonatomic) NSTimeInterval lastUpdateTimeInterval;

@property (nonatomic) NSArray* mineCartFrames;
@end

Then here is my code to draw the line:

#pragma mark - Touches

- (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.zPosition = 125;
    lineNode.physicsBody = [SKPhysicsBody bodyWithPolygonFromPath:lineNode.path];
    lineNode.physicsBody.categoryBitMask = lineCategory;
    lineNode.physicsBody.contactTestBitMask = mineCartCategory;
    lineNode.physicsBody.collisionBitMask = 0;
    lineNode.physicsBody.affectedByGravity = NO;
    [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;
}

Here I have tried to make lineNode a physicsBody but when I run this the collision doesn't work. (It runs with no errors)

Below is the collision code:

- (void)didBeginContact:(SKPhysicsContact *)contact
{
    SKPhysicsBody *firstBody, *secondBody;

    if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask)
    {
        firstBody = contact.bodyA;
        secondBody = contact.bodyB;
    }
    else
    {
        firstBody = contact.bodyB;
        secondBody = contact.bodyA;
    }

    if ((firstBody.categoryBitMask & rockCategory) != 0 &&
        (secondBody.categoryBitMask & mineCartCategory) != 0)
    {
        [self rock:(SKSpriteNode *) firstBody.node didCollideWithCart:(SKSpriteNode *) secondBody.node];
    }
    else if ((firstBody.categoryBitMask & lineCategory) != 0 &&
             (secondBody.categoryBitMask & mineCartCategory) != 0)
    {
        [self line:(SKSpriteNode *) firstBody.node didCollideWithCart:(SKSpriteNode *) secondBody.node];
    }
}
2
This question lacks sufficient information to diagnose the problem. Please include the relevant parts of the code and explain how your results differ from the desired results. Include any error messages you receive. Please read this advice on how to ask good questions and Jon Skeet's blog post Writing the perfect question. Pay special attention to the "Golden Rule", though I highly advise you to read the entire article.Adi Inbar
Your question is a little bit confusing. You want obstacles appearing on the top of the screen, moving down the screen and then disappearing. You want the user to control the x axis of the car by drawling a line on the screen's Y axis? That sounds really complicated if you are just looking to have the player move the car left or right because as you said, the car is not actually moving. So the only control the user has is the X axis. Have you considered using CoreMotion or touch left/right to move the car?sangony
I have now included relevant parts of the code...sorry about that, my mistake. The user can only control the sprites position on the y-axis, and yes I would rather use CoreMotion or touch left/right, BUT this is an assignment where I have to meet requirements (one of which is creating a physics body out of the SKShapeNode)Prathmesh
Create a SKSpriteNode and use bodyWithPolygonFromPath: as its physics body.sangony
Yep that got rid of the error but the two physics bodies aren't recognizing collisions between one another.Prathmesh

2 Answers

0
votes

Try to make a closed path with some width, for example:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    ...
    pathToDraw = CGPathCreateMutable();
    CGPathMoveToPoint(pathToDraw, NULL, 0, 0);
    CGPathAddLineToPoint(pathToDraw, NULL, 1, 0);
    ...
}

- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
    ...
    CGPathAddLineToPoint(pathToDraw, NULL, positionInScene.x, positionInScene.y);
    CGPathAddLineToPoint(pathToDraw, NULL, 0, 0);
    lineNode.path = pathToDraw;
}
0
votes

You should be adding the physics body after in

- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event
{

What your doing is adding a physics body to the first point the users touch began, which is not what you want.