SKshapenode is not responding to Physicsbody

2019-05-29 01:16发布

问题:

I have created a SKShapeNode and I have assigned a physicsBody to it. However, it is not being triggered when there is contact.

Creation of SKShapeNode code:

-(SKShapeNode*)gravityline{
    //SKSpriteNode *lolo=[[SKSpriteNode alloc]init];
    SKShapeNode *lolo = [[SKShapeNode alloc] init];
    CGPoint fff=CGPointMake(ray1.position.x, ray1.position.y);
    CGMutablePathRef path = CGPathCreateMutable();
    CGPathMoveToPoint(path, NULL, fff.x, fff.y);
    CGPathAddLineToPoint(path, 0,rayoriginpoint.x,rayoriginpoint.y );
    CGPathCloseSubpath(path);
    lolo.path = path;
    lolo.name=@"gravityline";
    lolo.strokeColor=[SKColor greenColor];
    lolo.glowWidth=.1;
    CGPathRelease(path);
    lolo.physicsBody=[SKPhysicsBody bodyWithEdgeFromPoint:fff toPoint:rayoriginpoint];
    //lolo.physicsBody=[SKPhysicsBody bodyWithEdgeLoopFromPath:path];
    //lolo.physicsBody=[SKPhysicsBody bodyWithPolygonFromPath:path];
    lolo.physicsBody.categoryBitMask=raylightCategory;
    lolo.physicsBody.collisionBitMask=batCategory;
    lolo.physicsBody.contactTestBitMask=batCategory;
    lolo.physicsBody.usesPreciseCollisionDetection=YES;
    lolo.physicsBody.linearDamping=0;
    lolo.physicsBody.restitution=1.0;
    lolo.physicsBody.dynamic=NO;

    return lolo;
}

Here is the trigering 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 == raylightCategory && secondBody.categoryBitMask==batCategory)
    {
        NSLog(@"Contact with bat have been made");
        [secondBody.node removeFromParent];
    }
}

If anybody has a clue what I did wrong, why the SKShapeNode is not activating the physicsBody, please let me know.

回答1:

This certainly won't work:

lolo.physicsBody=[SKPhysicsBody bodyWithEdgeFromPoint:fff toPoint:rayoriginpoint];

If anything this will return a body already assigned to a different node. But I guess it simply returns nil.

This commented line will not work either:

//lolo.physicsBody=[SKPhysicsBody bodyWithEdgeLoopFromPath:path];

Edge shapes will create static (as in: immovable) bodies. Hence this node won't move through physics and if I'm not mistaken you also won't get contact response from contacts with static bodies, only dynamic bodies.

This one should work:

//lolo.physicsBody=[SKPhysicsBody bodyWithPolygonFromPath:path];

But then you're setting the body to a static body here:

lolo.physicsBody.dynamic=NO;

Hence the same rules apply as if you were creating a body with an edge loop.



回答2:

Your code doesn't quite show what your "gravityLine" is colliding with. I can only speculate from its description that it's not detecting a collision with a round shaped object (a ball and a bat).

The "gravityLine" method seems to be returning a SKShapeNode that is defined by an edge-based shape:

lolo.physicsBody=[SKPhysicsBody bodyWithEdgeFromPoint:fff toPoint:rayoriginpoint];

When it comes to collisions, it is important to read Apple's Sprite Kit Programming Guide, specifically it's explanation of three types of shapes.

1) A dynamic volume simulates a physical object with volume and mass that can be affected by forces and collisions in the system. Use dynamic volumes to represent items in the scene that need to move around and collide with each other.

2) A static volume is similar to a dynamic volume, but its velocity is ignored and it is unaffected by forces or collisions. However, because it still has volume, other objects can bounce off it or interact with it. Use static volumes to represent items that take up space in the scene, but that should not be moved by the simulation. For example, you might use static volumes to represent the walls of a maze. While it is useful to think of static and dynamic volumes as distinct entities, in practice these are two different modes you can apply to any volume-based physics body. This can be useful because you can selectively enable or disable effects for a body.

3) An edge is a static volume-less body. Edges are never moved by the simulation and their mass doesn’t matter. Edges are used to represent negative space within a scene (such as a hollow spot inside another entity) or an uncrossable, invisibly thin boundary. For example, edges are frequently used to represent the boundaries of your scene. 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. If edges are moved through other means, they only interact with volumes, not with other edges.

Based on the above info, if you read the documentation for the method that you used bodyWithEdgeFromPoint:toPoint:, you will see that you are creating an "Edge-based" physics body.

Return Value

A new edge-based physics body.

Discussion

An edge has no volume or mass and is always treated as if the dynamic property is equal to NO. Edges may only collide with volume-based physics bodies.

To make your collision work, you have to make sure that your edge is colliding with a volume-based physics body. Every physics body shape creation method documents what type if shape it's creating.

If you are using a volume-based physics body that is colliding with your edge, then another possibility may be due to the size or speed of the involved objects. Again, reading Apple's docs makes it clear.

Specify High Precision Collisions for Small or Fast-Moving Objects: When Sprite Kit performs collision detection, it first determines the locations of all of the physics bodies in the scene. Then it determines whether collisions or contacts occurred. This computational method is fast, but can sometimes result in missed collisions. A small body might move so fast that it completely passes through another physics body without ever having a frame of animation where the two touch each other.

If you have physics bodies that must collide, you can hint to Sprite Kit to use a more precise collision model to check for interactions. This model is more expensive, so it should be used sparingly. When either body uses precise collisions, multiple movement positions are contacted and tested to ensure that all contacts are detected

ship.physicsBody.usesPreciseCollisionDetection = YES;

Other possibilities might mess up collisions as well, such as wrong position info for the path that you're assigning to the physics body. It's important to understand that when you set a path to a node, that the shape is being set using the local coordinate system of the node. It's important to remember that the origin in Sprite Kit is located bottom left corner (not UIKit's top left corner), and when you assign a path to a physics body of a node, that the path is placed relative to the anchor point of the node.

For example:

SKShapeNode *ball = [[SKShapeNode alloc] init];
CGRect ballFrame = CGRectMake(-25.0, -25.0, 50.0, 50.0);
[ball setPath:[UIBezierPath bezierPathWithOvalInRect:ballFrame].CGPath];
[ball setPosition:CGPointMake(100.0, 450.0)];
[ball setFillColor:[UIColor redColor]];
[ball setPhysicsBody:[SKPhysicsBody bodyWithCircleOfRadius:25.0]];

If I had set the origin of "ballFrame" at (0,0), then the circle of the physics body with radius of 25.0, would not coincide with the shape of the ball, as the bottom left corner of the physics body will be placed at the anchor point of the ball, which is at points x = 0 and y = 0, in the local coordinate system of the ball.