SK3DNode Hit Test дает ошибочные результаты c - PullRequest
0 голосов
/ 28 апреля 2020

Я пытаюсь отобразить базовый c 3D-контент в моем приложении, основанном в основном на SpriteKit. Я действительно не могу сделать это наоборот (т.е. добавить оверлеи SpriteKit в приложение на основе SceneKit).

Одна из моих сцен имеет SK3DNode с некоторой базовой c геометрией и Я не могу заставить тестирование сработать .

Этот код создает сцену SceneKit для визуализации в 3D-узле:

class ObjectScene: SCNScene {

    override init() {
        super.init()
        let box = createSolidBox(side: 1, radius: 0.05, color: .red)
        let boxNode = SCNNode(geometry: box)
        self.rootNode.addChildNode(boxNode)
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

func createSolidBox(side: CGFloat, radius: CGFloat, color: SKColor) -> SCNBox {
    let box = SCNBox(width: side, height: side, length: side, chamferRadius: radius)

    let material = SCNMaterial()
    material.diffuse.contents = color
    material.locksAmbientWithDiffuse = true
    box.materials = [SCNMaterial](repeating: material, count: 6)
    return box
}

Сцена SpriteKit , с другой стороны, выглядит следующим образом:

class GameScene: SKScene {

    let objectNode: SK3DNode

    override init(size: CGSize) {
        self.objectNode = SK3DNode(viewportSize: size)
        super.init(size: size)

        self.anchorPoint = CGPoint(x: 0.5, y: 0.5)

        objectNode.scnScene = ObjectScene()
        self.addChild(objectNode)

        let camera = SCNCamera()
        let cameraNode = SCNNode()
        cameraNode.camera = camera
        objectNode.pointOfView = cameraNode
        objectNode.pointOfView?.position = SCNVector3(x: 0, y: 0, z: 10)

        /* 
         Based on Apple's sample code at:
      https://developer.apple.com/documentation/spritekit/sk3dnode/displaying_3d_content_in_a_spritekit_scene
         */
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        let location = touches.first!.location(in: objectNode)
        let results = objectNode.hitTest(location, options: nil)
        print("Results: \(results.count)")
    }
}

(поскольку сцена создается экземпляром программно (не из файла sks), она создается / представлен на viewDidAppear ( :) вместо viewDidLoad (), так что размер представления известен и может быть правильно задан.) _

Я поместил приведенный выше код в MRE на Github .


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

Я думаю, что возможно, я не используя системы координат правильно, однако документация Apple не предоставляет конкретный код и, по-видимому, предполагает, что SK3DNode автоматически преобразует координаты SpriteKit в SceneKit для выполнения теста.

Я не нашел ни одного примера кода, который пытается выполнить точно это, так что я понятия не имею.

  • Должен ли я выполнить какое-то дополнительное преобразование на касании даже перед тем, как вызвать hitTest(_:options:)?
  • Имеет ли свойство scaleMode свойства SKScene влияет на это каким-либо образом? если да, то как?
  • Имеет ли значение свойство anchorPoint?

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


ОБНОВЛЕНИЕ

Я попытался оставить привязку SKScene по умолчанию (0, 0) и вместо этого поместите SK3DNode в центр (ширина / 2, высота / 2).

Затем я добавил код для проверки всех целых координат сетки SKView, как показано ниже:

for x in 0 ..< Int(size.width) {
    for y in 0 ..< Int(size.height) {

        let x0 = CGFloat(x)
        let y0 = CGFloat(y)

        let point = CGPoint(x: x0, y: y0)
        let results = objectNode.hitTest(point, options: nil)
        if results.count > 0 {
            print("Results at (\(x), \(y)): \(results.count)")
        }
    }
}

... и сравнил выходной журнал с ручными измерениями отрисованного окна на снимке экрана, который я сделал в имитаторе, используя инструмент редактирования изображений (Pixelmator) , Я изменил размер скриншота до 50%, так что я мог измерить точек вместо пикселей .

Я понял, что сообщенные координаты отклоняются с коэффициентом тео; То есть код ниже:

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    let location = touches.first!.location(in: self)
    let point = CGPoint(x: 2*location.x, y: 2*location.y)
    let results = objectNode.hitTest(point, options: nil)
    print("Results: \(results.count)")
}

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

Возможно, UITouch дает мне координаты в точках, но каким-то образом SK3DNode ожидает пиксели ?? Я определенно не хочу жестко закодировать эти маги c 2x в мой код ...

...