为什么 scaling/zooming-in 一个 SKNode 强制视图进入屏幕左侧?

Why is scaling/zooming-in an SKNode forcing the view into the left of the screen?

我正在关注 Ray Wenderlich 的 'iOS Games by Tutorials' 并且我的世界设置和工作中的所有内容都已完成:整个游戏处于横向模式并且有一个名为 _worldNode 的 SKNode 以及所有内容,除了 _uiNode(游戏中 UI), 被添加到它。玩家角色走到触摸到的位置,_worldNode 在他身下像跑步机一样移动。然而,就像所有功能(或他们称之为:"juice")的瘾君子一样,我想通过缩放 _worldNode 通过 UIPinchGestureRecognizer 添加缩放 in/out 功能,我做到了。但现在每次放大时,"camera" 都会移动到左下角。缩小会将视图移动到屏幕的右上角。一团糟。我需要视图以玩家角色为中心并且我已经尝试了所有我能想到并在网上找到的东西。我最接近的是使用 SKNode scale from the touched point 中的技术,但我仍然得到了该死的底部 left/top 正确的混乱。我意识到只有在我更新 camera/view 时才会发生这种混乱(实际上是 _worldNode.position)。因此,'didSimulatePhysics' 或 'didFinishUpdate' 方法无济于事。事实上,即使是稍微 moves/updates 相机视图 (_worldNode.position) 的一次性按钮仍然给我底部 left/top 正确的问题。这是我的代码。我希望有人能看一看并告诉我要修改什么才能使事情正常进行。

@interface GameScene () <SKPhysicsContactDelegate, UIGestureRecognizerDelegate>
{
    UIPinchGestureRecognizer        *pinchGestureRecognizer;
}

//我的GameScene的属性。

@property SKNode        *worldNode;   
@property etc. etc.


//Called by -(id)initWithSize:(CGSize)size method & creates the in-game world.
-(void)createWorld
{
    [_worldNode addChild:_backgroundLayer];

    [self addChild:_worldNode];
    self.anchorPoint        = CGPointMake(0.5, 0.5); //RW tutorial did it this way.
    _worldNode.position     = CGPointMake(-_backgroundLayer.layerSize.width/2, -_backgroundLayer.layerSize.height/2); //Center.

    //Then I add every node to _worldNode from this point on.
}

//Neccessary for gesture recognizers.
-(void)didMoveToView:(SKView *)view
{
    pinchGestureRecognizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(handleZoomFrom:)];
    [view addGestureRecognizer:pinchGestureRecognizer];
}

//"Camera" follows player character. 'didSimulatePhysics' doesn't help either.
-(void)didFinishUpdate
{
    //IF '_pinchingScreen' == YES then screen pinching is in progress. Thus, camera position update will seize for the duration of pinching.
    if (!_pinchingScreen)
    {
        _worldNode.position = [self pointToCenterViewOn:_player.position];
    }
}

//Method that is called by my UIPinchGestureRecognizer. Answer from: 
-(void)handleZoomFrom:(UIPinchGestureRecognizer*)recognizer
{
    CGPoint anchorPoint = [recognizer locationInView:recognizer.view];
    anchorPoint         = [self convertPointFromView:anchorPoint];

    if (recognizer.state == UIGestureRecognizerStateBegan)
    {
        // No code needed for zooming...

        _player.movementMode    = 2; //Stop character from moving from touches.
        _pinchingScreen         = YES; //Notifies 'didFinishUpdate' method that pinching began & camera position update should stop for now.
    }
    else if (recognizer.state == UIGestureRecognizerStateChanged)
    {
        //Technique from the above Stack Overflow link - Commented out.
//        CGPoint anchorPointInMySkNode = [_worldNode convertPoint:anchorPoint fromNode:self];
//        
//        [_worldNode setScale:(_worldNode.xScale * recognizer.scale)];
//        
//        CGPoint mySkNodeAnchorPointInScene = [self convertPoint:anchorPointInMySkNode fromNode:_worldNode];
//        CGPoint translationOfAnchorInScene = CGPointSubtract(anchorPoint, mySkNodeAnchorPointInScene);
//
//        _worldNode.position = CGPointAdd(_worldNode.position, translationOfAnchorInScene);
//        
//        recognizer.scale = 1.0;


        //Modified scale: 2.0
        if(recognizer.scale > _previousWorldScale)
        {
            _previousWorldScale = recognizer.scale;

            CGPoint anchorPointInMySkNode = [_worldNode convertPoint:anchorPoint fromNode:self];
            [_worldNode setScale:2.0];
            CGPoint worldNodeAnchorPointInScene = [self convertPoint:anchorPointInMySkNode fromNode:_worldNode];
            CGPoint translationOfAnchorInScene  = CGPointSubtract(anchorPoint, worldNodeAnchorPointInScene);
            _worldNode.position = CGPointAdd(_worldNode.position, translationOfAnchorInScene);


            //[_worldNode runAction:[SKAction scaleTo:2.0 duration:0]]; //This works too.
        }
        //Original scale: 1.0
        if(recognizer.scale < _previousWorldScale)
        {
            _previousWorldScale = recognizer.scale;

            CGPoint anchorPointInMySkNode = [_worldNode convertPoint:anchorPoint fromNode:self];
            [_worldNode setScale:1.0];
            CGPoint worldNodeAnchorPointInScene = [self convertPoint:anchorPointInMySkNode fromNode:_worldNode];
            CGPoint translationOfAnchorInScene  = CGPointSubtract(anchorPoint, worldNodeAnchorPointInScene);
            _worldNode.position = CGPointAdd(_worldNode.position, translationOfAnchorInScene);


            //[_worldNode runAction:[SKAction scaleTo:1.0 duration:0]]; //This works too.
        }
    }
    else if (recognizer.state == UIGestureRecognizerStateEnded)
    {
        // No code needed here for zooming...

        _pinchingScreen         = NO; //Notifies 'didFinishUpdate' method that pinching has stopped & camera position update should resume.
        _player.movementMode    = 0; //Resume character movement.
    }
}

所以谁能告诉我,通过查看上面的代码,为什么 camera/view 在放大时会移到左下角?我已经在这个问题上坐了好几天了,但我还是想不通。

感谢 JKallio,他在 Zoom and Scroll SKNode in SpriteKit 的回答中写了详细的代码,我已经找到了一段代码来解决这个问题。有一种叫做 'centerOnNode' 的方法,它小巧、优雅并且完美地解决了我的问题。这是给任何需要的人的:

-(void) centerOnNode:(SKNode*)node
{
    CGPoint posInScene = [node.scene convertPoint:node.position fromNode:node.parent];
    node.parent.position = CGPointMake(node.parent.position.x - posInScene.x, node.parent.position.y - posInScene.y);
}

然后在 'didSimulatePhysics' 或 'didFinishUpdate' 中调用该方法,如下所示:

//"Camera" follows player character.
-(void)didFinishUpdate
{
    //IF '_pinchingScreen' == YES then screen pinching is in progress. Thus, camera position update will seize for the duration of pinching.
    if (!_pinchingScreen)
    {
        if (_previousWorldScale > 1.0) //If _worldNode scale is greater than 1.0
        {
            [self centerOnNode:_player]; //THIS IS THE METHOD THAT SOLVES THE PROBLEM!
        }
        else if (_previousWorldScale == 1.0) //Standard _worldNode scale: 1.0
        {
            _worldNode.position = [self pointToCenterViewOn:_player.position];
        }
    }
}

P.S。请记住,问题不是如何放大。而是一旦世界已经放大,如何解决相机问题。