我使用物理学在SpriteKit中进行了简单的拖放。它可以按预期工作,但是当我使用快速拖动时,元素会穿过墙。
自跑者是墙
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
最佳答案
您直接设置节点的位置,这会跳过冲突检测。如果快速移动,则下一个位置可以在墙的另一侧(或足够靠近,以便物理结构通过将拖动的节点移到墙的外部和上方来解决碰撞)。
在动态主体上启用usesPreciseCollisionDetection
可能会改善这种情况,但可能无法完全阻止这种情况。实际上,由于直接设置节点位置而不是使用力/脉冲移动实体,可能不会有任何区别。
要解决此问题,您不能依赖联系事件。而是使用bodyAlongRayStart:end:来确定该节点的当前位置与下一个位置之间是否存在阻挡体。即便如此,这仅适用于您当前的设置,但无法阻止射线成功穿过墙体中无法容纳动态物体的小缝隙。