1

In my simple archery game, I have defined an arrow node and a target node with associated physics bodies.

When I attempt to join them together using an SKPhysicsJointFixed (I have also tried other types), the behaviour is inaccurate with the joint seemingly created at random points before actually hitting the target node.

I have played with friction and restitution values and also SKShapeNode (with a CGPath) and SKSpriteNode (with a rectangle around the sprite) to define the target but the problem occurs with both.

When just using collisions, the arrows bounce off the correct locations of the target, which seems OK. It is only when the join occurs that the results become random on-screen, usually 10-20 points away from the target node "surface".

static const uint32_t arrowCategory = 0x1 << 1;
static const uint32_t targetCategory = 0x1 << 2;

- (SKSpriteNode *)createArrowNode
{
    SKSpriteNode *arrow = [[SKSpriteNode alloc] initWithImageNamed:@"Arrow.png"];

    arrow.position = CGPointMake(165, 110);
    arrow.name = @"arrowNode";

    arrow.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:arrow.frame.size];
    arrow.physicsBody.angularVelocity = -0.25;
    arrow.physicsBody.usesPreciseCollisionDetection = YES;

    arrow.physicsBody.restitution = 0.0;
    arrow.physicsBody.friction = 0.0;

    arrow.physicsBody.categoryBitMask = arrowCategory;
    arrow.physicsBody.collisionBitMask = targetCategory;
    arrow.physicsBody.contactTestBitMask = /*arrowCategory | */targetCategory | bullseyeCategory;

    return arrow;
}


-void(createTargetNode)
{
    SKSpriteNode *sprite = [[SKSpriteNode alloc] initWithImageNamed:@"TargetOutline.png"];
    sprite.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:sprite.size];

    sprite.position = CGPointMake(610, 100);
    sprite.name = @"targetNode";

    sprite.physicsBody.usesPreciseCollisionDetection = NO;
//    sprite.physicsBody.affectedByGravity = NO;
//    sprite.physicsBody.mass = 20000;
    sprite.physicsBody.dynamic = NO;
    sprite.physicsBody.friction = 0.0;
    sprite.physicsBody.restitution = 0.0;

    sprite.physicsBody.categoryBitMask = targetCategory;
    sprite.physicsBody.contactTestBitMask = targetCategory | arrowCategory;

    [self addChild:sprite];
}


- (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 & arrowCategory) != 0 &&
        (secondBody.categoryBitMask & targetCategory) != 0)
    {
        CGPoint contactPoint = contact.contactPoint;

        NSLog(@"contactPoint is %@", NSStringFromCGPoint(contactPoint));

        float contact_x = contactPoint.x;
        float contact_y = contactPoint.y;

        SKPhysicsJoint *joint = [SKPhysicsJointFixed jointWithBodyA:firstBody bodyB:secondBody anchor:(CGPoint)contactPoint ];

        [self.physicsWorld addJoint:joint];

        CGPoint bullseye = CGPointMake(590, 102.5);
        NSLog(@"Center is %@", NSStringFromCGPoint(bullseye));

        CGFloat distance = SDistanceBetweenPoints(contactPoint, bullseye);
        NSLog(@"Distance to bullseye is %f", distance);
}
Astirian
  • 63
  • 1
  • 10
  • Which version of iOS are you running? – 0x141E Jul 15 '14 at 06:11
  • Hi Code Monkey, it's version 10.9.3 of OSX and XCode 5.1.1 with the latest iOS version (7.1.2) as of this writing. Could it be because I'm using a resolution of 640x400 perhaps? I've observed the same behaviour on physical devices now (I thought it could be a simulator thing)... – Astirian Jul 30 '14 at 06:49
  • iOS 7.0 had a problem with physics joint. The issue was fixed in 7.1. Are you still having problems with joints? – 0x141E Jul 30 '14 at 06:52
  • Yeah, it's so close but yet so far. The arrows appear to be up to 12 pixels or so off at times. Sometimes they are accurate. It's a frustrating issue. I've limited the FPS to 30 based on this blog Steffen wrote http://www.learn-cocos2d.com/2013/10/game-engine-multiply-delta-time-or-not/ - (largely because I'm a noob and very impressionable). – Astirian Jul 30 '14 at 07:02
  • 1
    I suggest you try the following: 1) refine your physics body to better reflect the shape of your arrow (perhaps a triangle), 2) Set usesPreciseCollisionDetection to YES for your target, 3) instead of using a physics joint (which has its own issues), set the arrow's dynamic property to NO when it makes contact with the target. – 0x141E Jul 30 '14 at 07:45
  • Excellent ideas! I tried removing the body's dynamism and set precise to YES on the target but no joy, same results. I suspected that the physics engine is trying to guess, as best as it can, the node's pre-contact position before it tries to draws it. This might explain why the problem magnifies the lower the fps... As far as I can tell when the arrows just bounce, they seem to be hitting the correct line just fine. – Astirian Aug 01 '14 at 13:27
  • I found this... http://codea.io/talk/discussion/2838/inaccuracy-of-the-physics-engine/p1 – Astirian Aug 01 '14 at 13:35
  • 2
    Try adding self.view.showsPhysics = YES; to your scene initialization code. With that set, you will see an outline of the physic bodies and maybe you'll see the issue. Also, try setting the dynamic property in didSimulatePhysics instead by keeping a pointer to the arrow that made contact. I was thinking that arrow may need to be updated for the current frame before stopping its motion. – 0x141E Aug 01 '14 at 19:10

0 Answers0