SKFieldNode расположен неправильно после обновления iOS 9

У меня есть код, который отлично работал в iOS 8, однако, начиная с iOS 9, несколько SKFieldNodes ведут себя очень странно. Изображение ниже показывает сцену с двумя SKFieldNodes (отмечены красными спрайтами) и SKEmitterNode испуская золотые частицы.

введите описание изображения здесь

В iOS 8 поля будут притягивать частицы и "засасывать" их к кончикам магнита. Начиная с iOS 9, частицы ускоряются к верхнему правому углу, как показано красными стрелками. Кроме того, мне пришлось увеличить strength полей, чтобы даже сделать эффект видимым.

Вот соответствующий код:

            SKSpriteNode* magnet = [SKSpriteNode spriteNodeWithImageNamed:@"StarMagnet_main"];

            //Add field node
            SKFieldNode* field = [SKFieldNode radialGravityField];
            field.strength = 10.0;
            field.falloff = 0.1;

            //Add a red circle to visualize the node
            SKShapeNode* debugNode = [SKShapeNode shapeNodeWithCircleOfRadius:20.0];
            debugNode.fillColor = [UIColor redColor];
            [field addChild:debugNode];

            field.position = CGPointMake(magnet.size.width * 0.3, magnet.size.height * 0.45);

            field.categoryBitMask = LECategoryDust;
            [magnet addChild:field];

            //Add second field
            field = [field copy];
            field.position = CGPointMake(-field.position.x, field.position.y);
            [magnet addChild:field];

            //Add particle emitter
            SKEmitterNode* emitter = [SKEmitterNode emitterNamed:@"MagneticParticleEmitter"];
            emitter.fieldBitMask = LECategoryDust;
            emitter.position = CGPointMake(0, magnet.size.height/2.0);
            [magnet addChild:emitter];

Кажется, что узлы поля были расположены где-то рядом с верхним правым углом, но дочерние узлы красного круга показывают разные.

1 ответ

Решение

После тщательного изучения проблемы, я пришел к выводу, что это ошибка SpriteKit, представленная в iOS 9. Ниже вы можете найти тестовую сцену, которая состоит из SKEmitterNode, SKFieldNode и два SKShapeNodes отмечая начальный видовой экран и положение поля. Все узлы являются потомками rootNode раньше переводили "мир".

Хотя положение поля не меняется, центр его эффектов меняется.

Вычисления силы верны только в том случае, если SKEmitterNode'stargetNode свойство установлено, чтобы быть scene, что не всегда желательно.

Начальное состояние представления

Желтый круг отмечает положение поля. Тем не менее, частицы тянутся к верхнему правому углу. Вот некоторые позиции на данный момент:

Root scene pos: 160x, 284y | parent pos: 160x, 284y
Field scene pos: 80x, 142y | parent pos: -80x, -142y
Target scene pos: 80x, 142y | parent pos: -80x, -142y

Как видите, поле и желтый круг имеют одинаковую позицию (и родитель).

При перемещении сцены (то есть rootNode только), частицы притягиваются к другой точке, которая перемещается ближе к нижнему левому углу при переводе в противоположную позицию.

Опять несколько позиций после перевода:

Root scene pos: 84x, 149y | parent pos: 84x, 149y
Field scene pos: 4x, 7y | parent pos: -80x, -142y
Target scene pos: 4x, 7y | parent pos: -80x, -142y

Положение поля не изменилось, но центр тяжести изменился.

видео

Чтобы лучше проиллюстрировать поведение, я загрузил скринкаст: https://youtu.be/EJs4vPYMndU

Код

Любой, кто хочет попробовать это самостоятельно, приветствуется, вот код тестовой сцены, используемый в этом эксперименте:

#import "LEDebugScene.h"
#import "LEPhysicsCategories.h"

@implementation LEDebugScene

SKNode* rootNode;
SKFieldNode* fieldNode;
SKShapeNode* targetPosShape;

- (void) didMoveToView:(SKView *)view {
    self.backgroundColor = [SKColor blackColor];

    // This will be the desired field's position (in parent space)
    CGPoint fieldPosition = CGPointMake(-self.size.width * 0.25, -self.size.height * 0.25);

    // 1. Set up a root node to move around to translate the "world"
    rootNode = [SKNode node];
    rootNode.position = CGPointMake(self.size.width/2.0, self.size.height/2.0);
    [self addChild:rootNode];

    // 2. Mark the target position of the field with a circle
    targetPosShape = [SKShapeNode shapeNodeWithCircleOfRadius:20.0];
    targetPosShape.fillColor = [UIColor yellowColor];
    targetPosShape.strokeColor = [UIColor brownColor];
    [rootNode addChild:targetPosShape];
    targetPosShape.position = fieldPosition;

    // 3. Mark the initially visible portion of the scene
    SKShapeNode* initialViewportShape = [SKShapeNode shapeNodeWithRect: CGRectMake(-self.size.width/2.0,
                                                                        -self.size.height/2.0,
                                                                        self.size.width,
                                                                        self.size.height)];
    initialViewportShape.fillColor = [UIColor clearColor];
    initialViewportShape.strokeColor = [UIColor redColor];
    initialViewportShape.lineWidth = 3.0;
    [rootNode addChild:initialViewportShape];

    // 4. Add an emitter at the center of the initial viewport
    NSString* emitterPath = [[NSBundle mainBundle] pathForResource:@"DebugEmitter" ofType:@"sks"];
    SKEmitterNode* emitterNode = [NSKeyedUnarchiver unarchiveObjectWithFile:emitterPath];
    emitterNode.fieldBitMask = LECategoryDust;
    emitterNode.particlePositionRange = CGVectorMake(self.size.width, self.size.height);
    emitterNode.position = CGPointZero;
    [rootNode addChild:emitterNode];

    // 5. Create a field at the desired position
    fieldNode = [SKFieldNode radialGravityField];
    fieldNode.userData = [NSMutableDictionary dictionary];
    fieldNode.categoryBitMask = LECategoryDust;
    fieldNode.falloff = 1.8;
    fieldNode.minimumRadius = 20.0;
    fieldNode.strength = 2.5;
    fieldNode.position = fieldPosition;
    [rootNode addChild:fieldNode];
}

- (void) touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {

    //Get the touch movement vector
    UITouch* touch = [touches anyObject];
    CGPoint prevLoc = [touch previousLocationInNode:self];
    CGPoint location = [touch locationInNode:self];
    CGVector delta = CGVectorMake(location.x - prevLoc.x, location.y - prevLoc.y);

    //Translate the world by repositioning the root node
    rootNode.position = CGPointMake(rootNode.position.x + delta.dx, rootNode.position.y + delta.dy);

    CGPoint rootScenePos = [self convertPoint:rootNode.position fromNode:rootNode.parent];
    CGPoint fieldScenePos = [self convertPoint:fieldNode.position fromNode:fieldNode.parent];
    CGPoint targetShapeScenePos = [self convertPoint:targetPosShape.position fromNode:targetPosShape.parent];

    NSLog(@"");
    NSLog(@"Root scene pos: %.0fx, %.0fy | parent pos: %.0fx, %.0fy",
          rootScenePos.x, rootScenePos.y, rootNode.position.x, rootNode.position.y);
    NSLog(@"Field scene pos: %.0fx, %.0fy | parent pos: %.0fx, %.0fy",
          fieldScenePos.x, fieldScenePos.y, fieldNode.position.x, fieldNode.position.y);
    NSLog(@"Target scene pos: %.0fx, %.0fy | parent pos: %.0fx, %.0fy",
          targetShapeScenePos.x, targetShapeScenePos.y, targetPosShape.position.x, targetPosShape.position.y);
}

@end

Несмотря на попытки в течение нескольких часов, мне не удалось компенсировать ошибки вычислений путем изменения положения узла поля. Если кто-то и сделает, было бы здорово, если бы они могли поделиться этим.

РЕДАКТИРОВАТЬ: Я также подал отчет об ошибке (23063175).

Другие вопросы по тегам