无法阻止精灵移出屏幕

时间:2015-07-18 13:57:18

标签: ios sprite-kit uigesturerecognizer

我想让我的精灵只在屏幕范围内移动。所以我通过bodyWithEdgeLoopFromRect创建了一个边循环,并且碰撞位掩码也被设置为使它们相互碰撞。

static const uint32_t kRocketCategory = 0x1 << 0;
static const uint32_t kEdgeCategory = 0x1 << 6;

我使用pan识别器移动精灵,这是设置所有东西属性的当前代码。

- (void)didMoveToView:(SKView *)view
{
    // Pan gesture
    self.panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanFrom:)];
    [self.view addGestureRecognizer:self.panRecognizer];

    // Edge
    self.backgroundColor = [SKColor blackColor];
    self.physicsWorld.gravity = CGVectorMake(0.0f, 0.0f);
    self.physicsWorld.contactDelegate = self;
    self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame];
    self.physicsBody.categoryBitMask = kEdgeCategory;
    self.physicsBody.contactTestBitMask = kRocketCategory;
    self.physicsBody.collisionBitMask = kRocketCategory;
    self.physicsBody.usesPreciseCollisionDetection = YES;
    self.physicsBody.restitution = 0;

    // Rocket
    SKTexture *texture = [SKTexture textureWithImageNamed:@"Rocketship-v2-1"];
    texture.filteringMode = SKTextureFilteringNearest;
    self.rocketSprite = [SKSpriteNode spriteNodeWithTexture:texture];

    self.rocketSprite.position = CGPointMake(CGRectGetMidX(self.scene.frame), CGRectGetMaxY(self.scene.frame)*0.3);    // 30% Y-axis
    self.rocketSprite.name = @"rocketNode";
    self.rocketSprite.xScale = 2;
    self.rocketSprite.yScale = 2;

    self.rocketSprite.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:self.rocketSprite.size];
    self.rocketSprite.physicsBody.categoryBitMask = kRocketCategory;
    self.rocketSprite.physicsBody.contactTestBitMask = kEdgeCategory;
    self.rocketSprite.physicsBody.collisionBitMask = kEdgeCategory;
    self.rocketSprite.physicsBody.usesPreciseCollisionDetection = YES;
    self.rocketSprite.physicsBody.allowsRotation = NO;
    self.rocketSprite.physicsBody.restitution = 0;

    [self addChild:self.rocketSprite];
}

泛识别器代码:

- (void)handlePanFrom:(UIPanGestureRecognizer *)recognizer
{
    if (recognizer.state == UIGestureRecognizerStateBegan) {
        self.selectedRocket = self.rocketSprite;
    } 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];
    } else if (recognizer.state == UIGestureRecognizerStateEnded) {
    }
}

- (void)panForTranslation:(CGPoint)translation
{
    CGPoint position = self.selectedRocket.position;
    if ([self.selectedRocket.name isEqualToString:@"rocketNode"]) {
        self.selectedRocket.position = CGPointMake(position.x + translation.x, position.y);
    }
}

现在的问题是当我慢慢移动精灵(火箭飞船)时,边缘会阻止精灵离开屏幕。但是,当我非常快速地移动精灵时,精灵会冲出范围。请参阅下面的动画。我已经阅读了类似问题的一些解决方案,但我仍然不知道我的代码有什么问题。对于这种情况,边缘循环和碰撞位掩码是否不够?

enter image description here

3 个答案:

答案 0 :(得分:1)

使用SpriteKit操作,而不是直接修改精灵的位置。

正在发生的事情是识别器没有足够快地发送更新。精灵的边界永远不会与任何一帧中的边相交。

在一次更新中,识别器表示触摸在边缘循环的范围内。但是在下一次更新中,触摸已经超越了边缘循环。这样就不会留下精灵界限与边缘相交的帧,因此不会检测到碰撞。

答案 1 :(得分:1)

您的尝试与我在网上搜索时发现的相似,并且已经阅读了您尝试过的内容,看起来您几乎涵盖了大多数常见问题(不使用SKAction) , 例如)。

我目前没有使用联系人位掩码来处理场景中的碰撞,但我确实使用了一个主动检查位置的循环。

这里是我用来重新定位对象的方法:(重新定位所有内容,你可以指定它只移动火箭。

-(void)repositionObjects
{
    for (CXSpriteNode *i in self.sceneObjects) // CXSpriteNode is a certain subclass
    {
        CGPoint position = i.position;
        if (position.x > self.background.size.width || position.x < 0)
        {
            CGPoint newPosition;
            if (position.x > self.background.size.width)
            {
                newPosition = CGPointMake(self.background.size.width-1, position.y);
            } else {
                newPosition = CGPointMake(1, position.y);
            }
            [i runAction:[SKAction moveTo:newPosition duration:0.0f]];
        }

        if (position.y > self.background.size.height || position.y < 0)
        {
            CGPoint newPosition;
            if (position.y > self.background.size.height)
            {
                newPosition = CGPointMake(self.background.size.height-1, 1);
            } else {
                newPosition = CGPointMake(position.x, 1);
            }
            [i runAction:[SKAction moveTo:newPosition duration:0.0f]];
        }
    }
}

这是从SKScene循环调用的:

-(void)update:(NSTimeInterval)currentTime
{ 
    [self repositionObjects];
}

现在,它显然没有你期望的结果那么优雅,我感觉它可能仍然会引起闪烁,但我还是试一试。

此外,一旦超出范围暂停以停止可能导致闪烁的重复滑动,可能值得尝试禁用/取消手势。

希望这会有所帮助。

答案 2 :(得分:0)

我在日常生活中不使用精灵套件,但你可以通过在精灵的更新method

中添加类似的内容来解决困难。

(伪代码)

CGFloat x = clamp(self.position.x, minXValue, maxXValue);
CGFloat y = clamp(self.position.y, minYValue, maxYValue);
self.position = CGPointMake(x, y);