SpriteKit拖放墙

时间:2014-09-19 14:31:47

标签: ios objective-c sprite-kit physics

我用物理学在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 () <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 个答案:

答案 0 :(得分:3)

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

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

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

答案 1 :(得分: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