SpriteKit拖放墙

我用物理学在SpriteKit中进行了简单的拖放操作。 它按预期工作,但当我使用快速拖动时,元素穿过墙。

self.runner是墙

self.runner2是正方形

Wall的动态设置为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 ()  @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 

您正在直接设置节点的位置,这会绕过碰撞检测。 如果快速移动,下一个位置可以位于墙的另一侧(或者足够接近,以便物理将通过将拖动的节点移动到墙的外部和上方来解决碰撞)。

在动态主体上启用usesPreciseCollisionDetection可能会改善这种情况,但可能无法完全阻止它。 实际上,由于直接设置节点位置而不是使用力/脉冲移动身体,它可能没有任何区别。

要解决此问题,您不能依赖联系事件。 而是使用bodyAlongRayStart:end:来确定节点的当前位置和下一个位置之间是否存在阻塞体。 即使如此,这只适用于您当前的设置,但无法阻止光线成功穿过墙体中的小空隙,动态的身体无法穿过。

在拖动节点时,如果您通过以下方式更改其位置,

 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中创建了一个示例项目。 video就在这里