SpriteKit通过墙拖放 [英] SpriteKit drag and drop through wall

查看:85
本文介绍了SpriteKit通过墙拖放的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I did simple drag and drop in SpriteKit with physics. It works as expected, but when I use fast drag, element goes through wall.

self.runner是墙

self.runner is wall

self.runner2是正方形

self.runner2 is square

将墙动态设置为NO.

Wall have dynamic set to NO.

电影会显示所有内容: https://www.dropbox.com/s/ozncf9i16o1z80o/spritekit_sample.mov?dl = 0

Movie shows everything: https://www.dropbox.com/s/ozncf9i16o1z80o/spritekit_sample.mov?dl=0

在iOS 7的模拟器和真实设备上进行了测试.

Tested on simulator and real device, both iOS 7.

我想防止正方形穿过墙壁.有任何想法吗?

I want to prevent square from going through wall. Any ideas?

#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

推荐答案

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

You're setting the position of the node directly, this bypasses collision detection. If you move fast, the next position can be on the other side of the wall (or close enough so that physics will resolve the collision by moving the dragged node outside and above the wall).

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

Enabling usesPreciseCollisionDetection on the dynamic body may improve the situation but may not entirely prevent it. Actually it may not make any difference due to setting the node position directly instead of moving the body using force/impulse.

要解决此问题,您不能依赖联系人事件.而是使用 bodyAlongRayStart:end:确定在节点的当前位置与下一个位置之间是否存在阻挡物体.即便如此,这仅适用于您当前的设置,但无法阻止射线成功穿过墙体中无法容纳动态物体的小缝隙.

To fix this behavior you can't rely on contact events. Instead use bodyAlongRayStart:end: to determine if there's a blocking body between the node's current and the next position. Even so this will only work with your current setup, but couldn't prevent the ray successfully passing through small gaps in the wall where the dynamic body wouldn't be able to fit through.

这篇关于SpriteKit通过墙拖放的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆