当对象逆时针而不是顺时针时,intersectsNode 失败

intersectsNode fails when object goes counterclockwise but not clockwise

我正在创建一个非常简单的轮盘游戏。没有涉及物理。表盘围绕彩色方形轮旋转,必须将表盘停在正确的颜色上。如果他们停在正确的颜色上,表盘就会向相反的方向旋转,并使用新的颜色进行匹配。我在边界线上使用 "intersectsNode" 来检测刻度盘何时进入车轮的象限并检查其颜色是否正确等。当刻度盘逆时针旋转时,刻度盘在接触边界线时正确地与边界线相交。然而,当表盘顺时针旋转时,intersectsNode 在它连接之前就被触发了三分之一。

相同的代码用于检测逆时针和顺时针以及创建边界线,所以我很困惑为什么它会以一种方式失败而不是另一种方式。有人有什么想法吗?一个我可能不知道的错误?我的设置有明显问题吗?

@implementation GameScene {

    Sound *sound;
    GameModel *gameModel;

    SKSpriteNode  *wheel, *pin;
    float duration;

    Direction currentDirection;
    int colorCount;
    Color currentColor, startingColor, enteringColor;
    SKLabelNode *scoreLabel;
}

- (void)didMoveToView:(SKView *)view {

    gameModel = [GameModel sharedManager];

    self.anchorPoint = CGPointMake(0.5, 0.5);
    self.backgroundColor = [SKColor colorWithWhite:0.9 alpha:1.0];

    wheel = [SKSpriteNode spriteNodeWithImageNamed:@"wheel"];
    [self addChild:wheel];
    //wheel.zRotation = SK_DEGREES_TO_RADIANS(-45);

    SKSpriteNode *hub = [SKSpriteNode spriteNodeWithImageNamed:@"hub"];
    [wheel addChild:hub];

    pin = [SKSpriteNode spriteNodeWithImageNamed:@"blue_pin"];
    pin.anchorPoint = CGPointMake(0.5, 0);
    pin.size = pin.texture.size;
    //pin.zRotation = SK_DEGREES_TO_RADIANS(45);
    [self addChild:pin];

    [self createBoundaryLines];

    PushButton *hubButton = [[PushButton alloc] initWithUpImage:@"blank_button" andDownImage:@"blank_button"];
    [hubButton setTouchUpInsideTarget:self action:@selector(switchDirection) parent:self];
    hubButton.zPosition = 50;
    [wheel addChild:hubButton];

    [self createScoreLabel];

    [self createResetButton];

    [self resetGame];
}

- (void)setScore:(int)score {

    _score = score;

    scoreLabel.text = [NSString stringWithFormat:@"%d", _score];
}

- (void)createBoundaryLines {

    //counterclockwise lines
    [wheel addChild:[self createLine:@"counterLine" color:[SKColor blueColor] position:CGPointMake(0, wheel.size.height / 2) size:CGSizeMake(1, 150) value:0]];
    [wheel addChild:[self createLine:@"counterLine" color:[SKColor yellowColor] position:CGPointMake(wheel.size.width / 2, 0) size:CGSizeMake(150, 1) value:1]];
    [wheel addChild:[self createLine:@"counterLine" color:[SKColor greenColor] position:CGPointMake(0, -wheel.size.height / 2) size:CGSizeMake(1, 150) value:2]];
    [wheel addChild:[self createLine:@"counterLine" color:[SKColor redColor] position:CGPointMake(-wheel.size.width / 2, 0) size:CGSizeMake(150, 1) value:3]];

    //clockwise lines
    [wheel addChild:[self createLine:@"clockwiseLine" color:[SKColor yellowColor] position:CGPointMake(1, wheel.size.height / 2) size:CGSizeMake(1, 150) value:1]];
    [wheel addChild:[self createLine:@"clockwiseLine" color:[SKColor greenColor] position:CGPointMake(wheel.size.width / 2, 1) size:CGSizeMake(150, 1) value:2]];
    [wheel addChild:[self createLine:@"clockwiseLine" color:[SKColor orangeColor] position:CGPointMake(1, -wheel.size.height / 2) size:CGSizeMake(1, 150) value:3]];
    [wheel addChild:[self createLine:@"clockwiseLine" color:[SKColor blueColor] position:CGPointMake(-wheel.size.width / 2, 1) size:CGSizeMake(150, 1) value:0]];
}

- (SKSpriteNode *)createLine:(NSString *)name color:(SKColor *)color position:(CGPoint)position size:(CGSize)size value:(int)value {

    SKSpriteNode *line = [SKSpriteNode spriteNodeWithColor:color size:size];
    line.position = position;
    line.name = name;
    line.userData = [[NSMutableDictionary alloc] init];
    [line.userData setValue:[NSNumber numberWithInt:value] forKey:@"color"];
    line.zPosition = 500;

    return line;
}

#pragma mark - game methods

- (Color)getRandomColorButNotThisColor:(Color)color {

    //recursive method to find unique color other than one currently assigned
    Color randomColor = (int)arc4random_uniform(colorCount);

    if (randomColor == color)
        //same color so try again
        return [self getRandomColorButNotThisColor:color];

    return randomColor;
}

- (void)switchDirection {

    [pin removeAllActions];

    //if they are stopping the pin then it is either going to be correct or game over
    if (enteringColor != currentColor) {

        RLog(@"game over");
        return;
    }

    self.score += 1;

    //find a new color for them to match
    startingColor = currentColor;
    currentColor = [self getRandomColorButNotThisColor:currentColor];
    [self changePinToColor:currentColor];

    //change direction of the spinning pin
    currentDirection = (currentDirection == COUNTERCLOCKWISE) ? CLOCKWISE : COUNTERCLOCKWISE;
    [pin runAction:[SKAction rotateByAngle:2 * M_PI * currentDirection duration:duration]];
}

- (void)resetGame {

    [pin removeAllActions];

    pin.zRotation = 0;

    //reset all variables
    currentColor = startingColor = BLUE;
    [self changePinToColor:currentColor];

    self.score = 0;
    colorCount = 4;
    duration = 5.0;
    currentDirection = COUNTERCLOCKWISE;
    //pin.zRotation = SK_DEGREES_TO_RADIANS(45);
}

- (void)changePinToColor:(Color)color {

    //change the pin color based on the color that they need to match
    SKTexture *pinTexture;

    switch (color) {

        case BLUE:
            pinTexture = [SKTexture textureWithImageNamed:@"blue_pin"];
            break;

        case YELLOW:
            pinTexture = [SKTexture textureWithImageNamed:@"yellow_pin"];
            break;

        case GREEN:
            pinTexture = [SKTexture textureWithImageNamed:@"green_pin"];
            break;

        case RED:
            pinTexture = [SKTexture textureWithImageNamed:@"orange_pin"];
            break;

        default:
            break;
    }

    pin.texture = pinTexture;
}

#pragma mark - game loop methods

- (void)update:(CFTimeInterval)currentTime {

    [self checkForCollisions];
}

- (void)checkForCollisions{

    if (currentDirection == COUNTERCLOCKWISE) {

        [wheel enumerateChildNodesWithName:@"counterLine" usingBlock:^(SKNode *line, BOOL *stop) {

            if ([pin intersectsNode:line]) {

                int amount = [line.userData[@"color"] intValue];

                [line runAction:[gameModel flashRedAction]];

                if (amount != enteringColor) {

                    enteringColor = amount;

                    RLog(@"entered %@", amount == 0 ? @"blue" : amount == 1 ? @"yellow" : amount == 2 ? @"green" : @"red" );

                    if (amount == currentColor - 1 || ((currentColor == 0) && amount == colorCount - 1))
                        RLog(@"game over!");

                    *stop = YES;
                }
            }
        }];
    }
    else {

        [wheel enumerateChildNodesWithName:@"clockwiseLine" usingBlock:^(SKNode *line, BOOL *stop) {

            if ([pin intersectsNode:line]) {

                int amount = [line.userData[@"color"] intValue];

                [line runAction:[gameModel flashRedAction]];

                if (amount != enteringColor) {

                    enteringColor = amount;

                    RLog(@"entered %@", amount == 0 ? @"blue" : amount == 1 ? @"yellow" : amount == 2 ? @"green" : @"red" );

                    if (amount == currentColor + 1 || ((currentColor == colorCount - 1) && amount == 0))
                        RLog(@"game over!");

                    *stop = YES;
                }
            }
        }];
    }
}

来自 intersectsNode 文档,

two nodes are considered to intersect if their frames intersect.

节点的框架(即边界框)是

a rectangle in the parent’s coordinate system that contains the content of the node...

通过观察大头针旋转时生成的边界框(参见视频剪辑),可以清楚地了解为什么使用 intersectsNode 不是确定大头针在轮子内位置的可靠方法。

或者,您可以通过

直接从其 zRotation 属性 确定图钉的位置
// Make sure angle in [-2pi, 2pi]. It can be a very large number
CGFloat angle = fmod(sprite.zRotation+M_PI_2, 2*M_PI);
// Make sure angle is positive and in [0, 2pi]
angle = angle < 0 ? angle + M_PI*2 : angle;
// Determine which quadrant the pin is in. An integer in [0, 3]
int quadrant =  (int)floor(angle/M_PI_2);

此时'quadrant'表示pin的颜色位置,其中0=黄色,1=蓝色,2=橙色,3=绿色。