0

Overview: I'm adding an enemy from the top of the scene. The enemy is initially moving right with 150 velocityX. The map has a left wall and a right wall. When then enemy his the right and left wall he should immediately reverse direction.

Problem: When I call the function spawnEnemy from initWithSize the enemy behaves as expected by reversing direction when the enemy hits each wall. When I use an NSTimer to call spawnEnemy every 2 seconds the SKScene does not recognize that the enemy makes contact with the wall and doesn't reverse the enemies direction. The code is provided below.

SPAWN ENEMY METHOD:

-(void)spawnEnemy{

    self.testingEnemy = [SKSpriteNode spriteNodeWithImageNamed:@"normalEnemy.png"];

    self.testingEnemy.position = (CGPoint) {
        CGRectGetMidX(self.scene.frame),
        300
    };

    self.testingEnemy.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:self.testingEnemy.frame.size];

    self.testingEnemy.physicsBody.dynamic = TRUE;

    self.testingEnemy.physicsBody.affectedByGravity = TRUE;

    self.testingEnemy.physicsBody.mass = 0.00150;

    self.testingEnemy.physicsBody.restitution = 0.0;

    self.testingEnemy.physicsBody.allowsRotation = FALSE;

    self.testingEnemy.physicsBody.linearDamping = 0.0;

    [self addChild:self.testingEnemy];
}

initWithSize

-(id)initWithSize:(CGSize)size {
    if (self = [super initWithSize:size]) {

        //[self spawnEnemy]; <--- This works with expected behavior

        [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(spawnEnemy) userInfo:nil repeats:NO];

    }
    return self
}

didBeginContact

- (void)didBeginContact:(SKPhysicsContact *)contact
{   
    if (contact.bodyA == self.testingEnemy.physicsBody && contact.bodyB == [self.map rightWall].physicsBody) {
        NSLog(@"Hit right wall");

        normalEnemyVelocityX = -normalEnemyVelocityX;

    }
}

Here is a link to the project file:

https://www.dropbox.com/s/e59piru3q1hcu0e/Goldrush.zip?dl=0

rjm226
  • 1,203
  • 1
  • 11
  • 21
  • don't use NSTimer ... http://stackoverflow.com/a/23978854/201863 – CodeSmile Aug 23 '14 at 10:13
  • Can you explain what is supposed to happen when you run the game? I downloaded the code and when I run it, a red block appears and falls off the screen. Sometimes, the block hits the floor, bounces off the right wall, moves left, and then falls off the screen. – 0x141E Aug 23 '14 at 16:08
  • right so... when you initially open the project the red block will stop at the right wall. Then if you comment out the NSTimer line and uncomment the function call right above that line the red block will fall out and bounce off the right wall. This is the expected behavior but when i run that function via nstimer the block will stop at the right wall. – rjm226 Aug 23 '14 at 16:10
  • There is a large gap between the floor and the left wall. Is that supposed to be there? The block spawns and then falls in the gap off the screen. Also, the right wall is not on the screen. – 0x141E Aug 23 '14 at 16:36
  • make sure you launch the project in a retina simulator – rjm226 Aug 23 '14 at 17:27
  • See the changes to my answer – 0x141E Aug 23 '14 at 18:25

1 Answers1

2

My 2 cents...

  1. You're not handling the case where contact.bodyA is the wall and contact.bodyB is the enemy.
  2. Where are you setting the categoryBitMask and contactTestBitMask?
  3. You should consider using an SKAction instead of an NSTimer, since SKActions pause/resume appropriately when you pause/resume your game.

Here's an example...

SKAction *wait = [SKAction waitForDuration:2.0];
SKAction *spawn = [SKAction performSelector:@selector(spawnEnemy) onTarget:self];
SKAction *waitThenSpawn = [SKAction sequence:@[wait, spawn]];

[self runAction:[SKAction repeatActionForever:waitThenSpawn]];

EDIT: Try the following...

1) The code isn't checking if the contact bodies are swapped and it won't work if multiple enemies are in the scene. Here's a way to address those issues:

static inline SKSpriteNode *bodyToNode(SKPhysicsBody *body1,
                                       SKPhysicsBody *body2,
                                       NSString *name) {
    SKSpriteNode *node = nil;
    if ([body1.node.name isEqualToString:name]) {
        node = (SKSpriteNode *)body1.node;
    }
    else if ([body2.node.name isEqualToString:name]) {
        node = (SKSpriteNode *)body2.node;
    }
    return node;
}

- (void)didBeginContact:(SKPhysicsContact *)contact
{
    SKSpriteNode *enemyNode = nil;
    SKSpriteNode *wallNode = nil;

    enemyNode = bodyToNode (contact.bodyA, contact.bodyB, @"enemy");
    wallNode = bodyToNode (contact.bodyA,contact.bodyB, @"wall");

    if (enemyNode && wallNode) {
        NSLog(@"Hit right wall %@", self.testingEnemy);
        normalEnemyVelocityX = -normalEnemyVelocityX;
    }
} 

2) Move the following from Map.m to Map.h

static const uint32_t playerCategory =  1 << 0;
static const uint32_t platformCategory =  1 << 1;

3) You didn't set the category for the enemy and you need to name the enemy so the didBeginContact works correctly. Add the following to the method called testing:

self.testingEnemy.physicsBody.categoryBitMask = playerCategory;
self.testingEnemy.name = @"enemy";

4) In Map.m, add the following at the appropriate place in your code:

self.leftWall.name = @"wall";

self.rightWall.name = @"wall";

5) Remove the playerCategory from this statement:

self.bottomRightHorizWall.physicsBody.contactTestBitMask = platformCategory;
0x141E
  • 12,613
  • 2
  • 41
  • 54
  • Agreed, SKAction is the best way to handle it. However, your #1, is the core issue in the behavior difference, as order is not guaranteed to be consistent. – prototypical Aug 23 '14 at 06:27
  • I'm going to remove my answer as this is most concise and covers the SKAction aspect. – prototypical Aug 23 '14 at 06:31
  • Here is the project file https://www.dropbox.com/s/e59piru3q1hcu0e/Goldrush.zip?dl=0 – rjm226 Aug 23 '14 at 06:35
  • I downloaded your game and when I run it, a red block appears and falls off the screen. What is supposed to happen? – 0x141E Aug 23 '14 at 06:46
  • I did as well, and it's not at all representative of the behavior suggested in the question. There are more than a few issues with the code. I did set up a quick project and tested with `NSTimer` and as expected, that is not the issue. I've upvoted this answer as all 3 suggestions are valid based on the code I saw. (as a starting point) – prototypical Aug 23 '14 at 06:58
  • @prototypical if you comment out the NSTimer and uncomment [self testing], the block hits the floor, moves right, and then bounces off the right wall. – 0x141E Aug 23 '14 at 07:25
  • Maybe you have a different version, because in what I downloaded the ViewController isn't set up correctly along with many other issues. – prototypical Aug 23 '14 at 07:38
  • However, I did just address #1 in your answer after setting up the ViewController correctly, and it works just as my project does. (via NSTimer and otherwise) – prototypical Aug 23 '14 at 07:44
  • @prototypical how did you fix the view controller? – 0x141E Aug 23 '14 at 07:51
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/59848/discussion-between-prototypical-and-0x141e). – prototypical Aug 23 '14 at 07:52