Масштаб SKNode от точки касания

Я добавил UIPinchGestureRecognizer в свой scene.view, чтобы масштабировать мой контент. На самом деле я масштабирую родительский узел, в котором находится все мое видимое содержимое. Но у меня проблема с точкой масштабирования. Дело в масштабе узла от нижнего левого угла. Это определенно не то, что я хочу. Нужно ли мне писать много кода, чтобы иметь возможность масштабироваться с точки, где происходит защемление? Не могли бы вы дать несколько советов о том, каким путем следовать.


person Mikayil Abdullayev    schedule 20.02.2014    source источник
comment
Вы изменили узел anchorPoint?   -  person Andrey Gordeev    schedule 20.02.2014
comment
Невозможно изменить точку привязки SKNode.   -  person Mikayil Abdullayev    schedule 20.02.2014


Ответы (4)


Я работаю над той же проблемой, и мое решение показано ниже. Не уверен, что это лучший способ сделать это, но пока он работает. Я использую этот код для увеличения и уменьшения SKNode, у которого есть несколько дочерних узлов SKSpriteNode. Все дети перемещаются и масштабируются с помощью SKNode по желанию. Точкой привязки для масштабирования является положение жеста сжатия. Родительский SKScene и другие SKNodes в сцене не затрагиваются. Вся работа происходит во время распознавателя.state == UIGestureRecognizerStateChanged.

// instance variables of MyScene.
SKNode *_mySkNode;
UIPinchGestureRecognizer *_pinchGestureRecognizer;

- (void)didMoveToView:(SKView *)view
{
    _pinchGestureRecognizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(handleZoomFrom:)];
    [[self view] addGestureRecognizer:_pinchGestureRecognizer];
}

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

     if (recognizer.state == UIGestureRecognizerStateBegan) {

         // No code needed for zooming...

     } else if (recognizer.state == UIGestureRecognizerStateChanged) {

         CGPoint anchorPointInMySkNode = [_mySkNode convertPoint:anchorPoint fromNode:self];

         [_mySkNode setScale:(_mySkNode.xScale * recognizer.scale)];

         CGPoint mySkNodeAnchorPointInScene = [self convertPoint:anchorPointInMySkNode fromNode:_mySkNode];
         CGPoint translationOfAnchorInScene = CGPointSubtract(anchorPoint, mySkNodeAnchorPointInScene);

         _mySkNode.position = CGPointAdd(_mySkNode.position, translationOfAnchorInScene);

         recognizer.scale = 1.0;

     } else if (recognizer.state == UIGestureRecognizerStateEnded) {

         // No code needed here for zooming...

     }
}

Ниже приведены вспомогательные функции, которые использовались выше. Они из книги Рэя Вендерлиха о Sprite Kit.

SKT_INLINE CGPoint CGPointAdd(CGPoint point1, CGPoint point2) {
    return CGPointMake(point1.x + point2.x, point1.y + point2.y);
}

SKT_INLINE CGPoint CGPointSubtract(CGPoint point1, CGPoint point2) {
    return CGPointMake(point1.x - point2.x, point1.y - point2.y);
}

SKT_INLINE GLKVector2 GLKVector2FromCGPoint(CGPoint point) {
    return GLKVector2Make(point.x, point.y);
}

SKT_INLINE CGPoint CGPointFromGLKVector2(GLKVector2 vector) {
    return CGPointMake(vector.x, vector.y);
}

SKT_INLINE CGPoint CGPointMultiplyScalar(CGPoint point, CGFloat value) {
    return CGPointFromGLKVector2(GLKVector2MultiplyScalar(GLKVector2FromCGPoint(point), value));
}
person ninefifteen    schedule 21.02.2014
comment
Спасибо, тело. Но что такое trackNodeShift? Вы имели в виду mySkNodeShift? - person Mikayil Abdullayev; 22.02.2014
comment
Да, это должен быть mySkNodeShift. Я пропустил это, когда менял имена, чтобы сделать их более общими. Я вернулся и исправил. Спасибо! - person ninefifteen; 22.02.2014
comment
Я попробовал ваш подход, и это действительно помогло. Спасибо еще раз. - person Mikayil Abdullayev; 22.02.2014
comment
Я попробовал, и это сработало, но... не совсем так, как я ожидал. Предположим, вы увеличили масштаб (200, 200), подняли пальцы, уменьшили масштаб (50, 50)... этот момент все ломает. Дох... Есть идеи, как это исправить? - person AndrewShmig; 05.05.2014
comment
AndrewShmig... Можете ли вы опубликовать свой код? У меня нет этой проблемы. Я могу увеличивать или уменьшать масштаб, поднимать пальцы, а затем увеличивать или уменьшать масштаб без каких-либо проблем. - person ninefifteen; 06.05.2014
comment
Привет спасибо за код! Однако, когда я его использую, он масштабируется от центра, а не от места касания. Пожалуйста помоги :))) - person Anton O.; 27.03.2016

Я перевел решение Ninefifteen для Swift и Pinch Gestures. Я потратил пару дней, пытаясь заставить это работать самостоятельно. Спасибо за пост Obj-C от Ninefifteen! Вот версия Swift, которая, кажется, работает для меня.

func scaleExperiment(_ sender: UIPinchGestureRecognizer) {


    var anchorPoint = sender.location(in: sender.view)

    anchorPoint = self.convertPoint(fromView: anchorPoint)

    let anchorPointInMySkNode = _mySkNode.convert(anchorPoint, from: self)

    _mySkNode.setScale(_mySkNode.xScale * sender.scale)

    let mySkNodeAnchorPointInScene = self.convert(anchorPointInMySkNode, from: _mySkNode)

    let translationOfAnchorInScene = (x: anchorPoint.x - mySkNodeAnchorPointInScene.x, y: anchorPoint.y - mySkNodeAnchorPointInScene.y)

    _mySkNode.position = CGPoint(x: _mySkNode.position.x + translationOfAnchorInScene.x, y: _mySkNode.position.y + translationOfAnchorInScene.y)

    sender.scale = 1.0


}
person Dave Levy    schedule 28.10.2016
comment
Вот Swift-версия вспомогательных функций Рэя Вендерлиха в ответе NineFifteen: >github.com/raywenderlich/SKTUtils/blob/master/SKTUtils/ - person peacetype; 27.12.2017

Не могу масштабировать, не знаю почему, но основная проблема в этих SKT_INLINE. Я гуглил их и ничего о них не нашел... Проблема в том, что когда я копирую/вставляю их в свой проект, компилятор говорит мне, что я должен добавить ";" сразу после них. Интересно, это причина того, что я могу масштабировать?

person Alfro    schedule 13.04.2015

В Swift 4 мой SKScene добавляет UIPinchGestureRecognizer к представлению, но передает обработку жеста щипка одному из своих дочерних элементов SKNode, созданному в сцене init(), по некоторым причинам, здесь не относящимся к делу. Во всяком случае, это ответ NineFifteen с точки зрения того, что он/она называет _mySkNode. Он также включает небольшой код для ограничения масштабирования и не использует удобные функции, перечисленные в нижней части его сообщения. Часть @objc объявления позволяет использовать функцию в #selector().

Вот что у меня в SKScene:

override func didMove(to view: SKView) {
    let pinchRecognizer: UIPinchGestureRecognizer = UIPinchGestureRecognizer(target: self.grid, action: #selector(self.grid.pinchZoomGrid))
    self.view!.addGestureRecognizer(pinchRecognizer)
}

И это соответствующий раздел в моем SKNode:

// Pinch Management
@objc func pinchZoomGrid(_ recognizer: UIPinchGestureRecognizer){
    var anchorPoint: CGPoint = recognizer.location(in: recognizer.view)
    anchorPoint = self.scene!.convertPoint(fromView: anchorPoint)

    if recognizer.state == .began {
        // No zoom code
    } else if recognizer.state == .changed {
        let anchorPointInGrid = self.convert(anchorPoint, from: self.scene!)

        // Start section that limits the zoom
        if recognizer.scale < 1.0 {
            if self.xScale * recognizer.scale < 0.6 {
                self.setScale(0.6)
            } else {
                self.setScale(self.xScale * recognizer.scale)
            }
        } else if recognizer.scale > 1.0 {
            if self.xScale * recognizer.scale > 1.5 {
                self.setScale(1.5)
            } else {
                self.setScale(self.xScale * recognizer.scale)
            }
        }
        // End section that limits the zoom

        let gridAnchorPointInScene = self.scene!.convert(anchorPointInGrid, from: self)
        let translationOfAnchorPointInScene = CGPoint(x:anchorPoint.x - gridAnchorPointInScene.x,
                                                      y:anchorPoint.y - gridAnchorPointInScene.y)

        self.position = CGPoint(x:self.position.x + translationOfAnchorPointInScene.x,
                                y:self.position.y + translationOfAnchorPointInScene.y)
        recognizer.scale = 1.0

    } else if recognizer.state == .ended {
        // No zoom code
    }
}
person Clay    schedule 22.09.2017