2014-09-19 67 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