Search code examples
ios7sprite-kitskactionsknode

Sprite Kit Moving Nodes using SKAction causing trouble


I am trying to create a block game where I have a row of blocks, I slide the end block from right to left and each of the other blocks moves right 1 position. If I then slide the block back again they all slide left by 1 position. The trouble I am having is that If i drag slowly it works fine but if I drag quickly back and forth the blocks get all messed up, they move over the top of each other so instead of a row of 6 I have a row of 4 as 2 blocks are sitting behind the other ones. Any advice is appreciated.

Here is code from my Scene.M

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

self.selectedNode = [self nodeAtPoint:[[touches anyObject] locationInNode:self]];

[self.selectedNode.physicsBody setDynamic:YES];
self.selectedNode.zPosition = 1;
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
CGPoint currentPoint = CGPointMake([[touches anyObject] locationInNode:self].x , selectedNode.position.y);
CGPoint previousPoint = CGPointMake([[touches anyObject] previousLocationInNode:self].x , selectedNode.position.y);
_deltaPoint = CGPointSubtract(currentPoint, previousPoint);
}

-(void)update:(CFTimeInterval)currentTime {

CGPoint newPoint = CGPointAdd(self.selectedNode.position, _deltaPoint);

self.selectedNode.position = newPoint;

_deltaPoint = CGPointZero;

}

-(void)didBeginContact:(SKPhysicsContact *)contact{

SKNode *node = contact.bodyA.node;
if ([node isKindOfClass:[Block class]] && node != selectedNode) {
    [(Block *)node collidedWith:contact.bodyB contact:contact];
}

node = contact.bodyB.node;
if ([node isKindOfClass:[Block class]] && node != selectedNode) {
    [(Block *)node collidedWith:contact.bodyA contact:contact];
}
}

Here is my code from my Block.M

-(instancetype)initWithPosition:(CGPoint)pos andType:(NSString *)blockType{

SKTextureAtlas *atlas =
[SKTextureAtlas atlasNamed: @"Blocks"];
SKTexture *texture = [atlas textureNamed:blockType];
texture.filteringMode = SKTextureFilteringNearest;

if (self = [super initWithTexture:texture]){
    self.name = blockType;
    self.position = pos;
    self.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(self.size.width - 30, self.size.height - 8)];
    self.physicsBody.usesPreciseCollisionDetection = YES;
    self.physicsBody.categoryBitMask = 1;
    self.physicsBody.collisionBitMask =  0;
    self.physicsBody.contactTestBitMask = 1;

}

return self;
}

- (void)collidedWith:(SKPhysicsBody *)body contact:(SKPhysicsContact*)contact {

CGPoint localContactPoint = [self.scene convertPoint:contact.contactPoint toNode:self];

if (localContactPoint.x < 0) {
    SKAction *moveLeftAction = [SKAction moveByX:-48 y:0 duration:0.0];
    [self runAction:moveLeftAction];
} else {
    SKAction *moveRightAction = [SKAction moveByX:48 y:0 duration:0.0];
    [self runAction:moveRightAction];
}
}

Solution

  • You could have multiple move actions running simultaneously if you start a collision before previous move actions on a given block complete, which can lead to weird animations.

    One fix is to remove existing animations before the next starts. Something like this (not tested):

    - (void)collidedWith:(SKPhysicsBody *)body contact:(SKPhysicsContact*)contact {
    
    CGPoint localContactPoint = [self.scene convertPoint:contact.contactPoint toNode:self];
    
        // If we have any move actions still in progress, complete them so we can start a new one
        if ([self actionForKey:@"blockMove"]) {
            [self removeActionForKey:@"blockMove"];
            self.position = CGPointMake(_targetX, self.position.y);
        }
    
        if (localContactPoint.x < 0) {
            _targetX = self.position.x - 48;
        } else {
            _targetX = self.position.x + 48;
        }
        SKAction *moveLeftAction = [SKAction moveToX:_targetX y:0 duration:0.0];
        [self runAction:moveLeftAction withKey:@"blockMove"];
    }