2014-09-19 65 views
2

我用物理方法在SpriteKit中進行了簡單的拖放操作。它按預期工作,但是當我使用快速拖動時,元素會穿過牆壁。SpriteKit通過牆壁拖放

self.runner是壁

self.runner2是正方形

牆有動態設置爲NO。

影片中展示的一切:https://www.dropbox.com/s/ozncf9i16o1z80o/spritekit_sample.mov?dl=0

測試在模擬器和真正的設備,不論是iOS 7

我想阻止廣場從穿牆去。有任何想法嗎?

#import "MMMyScene.h" 

static NSString * const kRunnerImg = @"wall.png"; 
static NSString * const kRunnerName = @"runner"; 

static NSString * const kRunnerImg2 = @"zebraRunner.png"; 
static NSString * const kRunnerName2 = @"runner"; 

static const int kRunnerCategory = 1; 
static const int kRunner2Category = 2; 
static const int kEdgeCategory = 3; 


@interface MMMyScene() <SKPhysicsContactDelegate> 

@property (nonatomic, weak) SKNode *draggedNode; 
@property (nonatomic, strong) SKSpriteNode *runner; 
@property (nonatomic, strong) SKSpriteNode *runner2; 

@end 

@implementation MMMyScene 

-(id)initWithSize:(CGSize)size {  
    if (self = [super initWithSize:size]) { 
     self.backgroundColor = [SKColor colorWithRed:0.15 green:0.15 blue:0.3 alpha:1.0]; 

     self.runner = [SKSpriteNode spriteNodeWithImageNamed:kRunnerImg]; 
     self.runner.texture = [SKTexture textureWithImageNamed:kRunnerImg]; 
     [self.runner setName:kRunnerName]; 

     [self.runner setPosition:CGPointMake(160, 300)]; 
     [self.runner setSize:CGSizeMake(320, 75)]; 
     [self addChild:self.runner]; 

     self.runner.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(320, 75)]; 
     self.runner.physicsBody.categoryBitMask = kRunnerCategory; 
     self.runner.physicsBody.contactTestBitMask = kRunner2Category; 
     self.runner.physicsBody.collisionBitMask = kRunner2Category; 
     self.runner.physicsBody.dynamic = NO; 
     self.runner.physicsBody.allowsRotation = NO; 

     self.runner2 = [SKSpriteNode spriteNodeWithImageNamed:kRunnerImg2]; 
     [self.runner2 setName:kRunnerName2]; 

     [self.runner2 setPosition:CGPointMake(100, 100)]; 
     [self.runner2 setSize:CGSizeMake(75, 75)]; 

     self.runner2.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(75, 75)]; 
     self.runner2.physicsBody.categoryBitMask = kRunner2Category; 
     self.runner2.physicsBody.contactTestBitMask = kRunnerCategory; 
     self.runner2.physicsBody.collisionBitMask = kRunnerCategory; 
     self.runner2.physicsBody.dynamic = YES; 
     self.runner2.physicsBody.allowsRotation = NO; 

     [self addChild:self.runner2]; 

     self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame]; 
     self.physicsBody.categoryBitMask = kEdgeCategory; 
     self.physicsBody.collisionBitMask = 0; 
     self.physicsBody.contactTestBitMask = 0; 
     self.physicsWorld.gravity = CGVectorMake(0,0); 
     self.physicsWorld.contactDelegate = self;  
    } 

    return self; 
} 

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

    firstBody = contact.bodyA; 
    secondBody = contact.bodyB; 

    if(firstBody.categoryBitMask == kRunnerCategory) 
    { 
    NSLog(@"collision"); 
    //self.draggedNode = nil; 

    } 
} 

- (void)didMoveToView:(SKView *)view { 
    UIPanGestureRecognizer *gestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanFrom:)]; 
    [[self view] addGestureRecognizer:gestureRecognizer]; 
} 

- (void)panForTranslation:(CGPoint)translation { 
    CGPoint position = [self.draggedNode position]; 
    if([[self.draggedNode name] isEqualToString:kRunnerName]) { 
    [self.draggedNode setPosition:CGPointMake(position.x + translation.x, position.y + translation.y)]; 
    } 
} 

- (void)handlePanFrom:(UIPanGestureRecognizer *)recognizer { 
    if (recognizer.state == UIGestureRecognizerStateBegan) { 
    CGPoint touchLocation = [recognizer locationInView:recognizer.view]; 
    touchLocation = [self convertPointFromView:touchLocation]; 
    [self selectNodeForTouch:touchLocation]; 
    } 
    else if (recognizer.state == UIGestureRecognizerStateChanged) { 
    CGPoint translation = [recognizer translationInView:recognizer.view]; 
    translation = CGPointMake(translation.x, -translation.y); 
    [self panForTranslation:translation]; 
    [recognizer setTranslation:CGPointZero inView:recognizer.view]; 
    } 
} 

- (void)selectNodeForTouch:(CGPoint)touchLocation { 
    SKSpriteNode *touchedNode = (SKSpriteNode *)[self nodeAtPoint:touchLocation]; 

    if(![self.draggedNode isEqual:touchedNode]) { 
    self.draggedNode = touchedNode; 
    // self.draggedNode.physicsBody.affectedByGravity = NO; 
    } 
} 


@end 

回答

2

您正在設置節點的位置,直接繞過碰撞檢測。如果快速移動,則下一個位置可以位於牆的另一側(或足夠接近,以便物理通過移動拖動節點在牆外或上方來解決碰撞)。

在動態身體上啓用usesPreciseCollisionDetection可能改善情況,但可能無法完全阻止它。實際上,由於直接設置節點位置而不是使用力/衝動來移動身體,它可能沒有任何區別。

要解決此問題,您不能依賴聯繫事件。而是使用bodyAlongRayStart:end:來確定節點的當前位置和下一個位置之間是否存在阻塞體。即使如此,這將只適用於您當前的設置,但無法阻止光線順利穿過動態物體無法穿過的牆壁中的小間隙。

1

在拖動節點,如果更改在以下幾個方面的立場,

draggedNode.position = CGPoint(x: position.x + translation.x, y: position.y + translation.y) 

SKAction.move(to: CGPoint(x: 100, y: sprite.position.y), duration: 1)) 

節點將失去其物理性質。這就是爲什麼他們穿過牆壁或其他節點。因此你需要改變速度或施加力量或衝動。

我在Github創建了一個example項目。而視頻是here