检测触摸并忽略其他手势 Swift SceneKit

问题描述 投票:0回答:1

我有一个交互式地球仪,我想检测地球仪上的点击,这样我就可以获得它们的 3D 位置。

当我重写函数

public override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
时,我能够检测到手势何时开始,并且从 touches' 参数中我可以获得点击在地球上的 3d 位置。

问题是这个函数可以通过任何手势(拖动、放大等)执行,但我只想通过单击或点击执行某些操作。

我可以向场景视图添加点击手势,但从点击手势调用的函数不会给我点击在地球内的 3D 位置。

是否有任何其他功能我可以重写以在获取

touches: Set
时仅检测点击?
如果没有,有什么方法可以过滤掉触摸以仅点击手势吗?

import Foundation
import SceneKit
import CoreImage
import SwiftUI
import MapKit

public typealias GenericController = UIViewController

public class GlobeViewController: GenericController {
    var nodePos: CGPoint? = nil
    public var earthNode: SCNNode!
    private var sceneView : SCNView!
    private var cameraNode: SCNNode!
    private var dotCount = 50000
    
    public init(earthRadius: Double) {
        self.earthRadius = earthRadius
        super.init(nibName: nil, bundle: nil)
    }
    
    public init(earthRadius: Double, dotCount: Int) {
        self.earthRadius = earthRadius
        self.dotCount = dotCount
        super.init(nibName: nil, bundle: nil)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    public override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard let touch = touches.first, touch.view == self.sceneView else {
            return
        }

        let touchLocation = touch.location(in: sceneView)
        let hitTestResults = sceneView.hitTest(touchLocation, options: nil)
        
        let touchLocation3D = sceneView.unprojectPoint(SCNVector3(Float(touchLocation.x), Float(touchLocation.y), 0.0))
        
        print(touchLocation3D)
        
        if let tappedNode = hitTestResults.first?.node {
            print(tappedNode)
            // Handle the tapped node
            if tappedNode.name == "NewYorkDot" {
                // This is the New York dot, perform your action here
                print("Tapped on New York! Position: \(tappedNode.position)")
            } else if tappedNode.name == "RegularDot" {
                // Handle other nodes if needed
                print("Tapped on a regular dot. Position: \(tappedNode.position)")
            }
        }
    }

    public override func viewDidLoad() {
        super.viewDidLoad()
        setupScene()
        
        setupParticles()
        
        setupCamera()
        setupGlobe()
        
        setupDotGeometry()
    }
    
    private func setupScene() {
        let scene = SCNScene()
        sceneView = SCNView(frame: view.frame)
        sceneView.scene = scene
        sceneView.showsStatistics = true
        sceneView.backgroundColor = .clear
        sceneView.allowsCameraControl = true
        sceneView.isUserInteractionEnabled = true
        self.view.addSubview(sceneView)
    }
        
    private func setupParticles() {
        guard let stars = SCNParticleSystem(named: "StarsParticles.scnp", inDirectory: nil) else { return }
        stars.isLightingEnabled = false
                
        if sceneView != nil {
            sceneView.scene?.rootNode.addParticleSystem(stars)
        }
    }
    
    private func setupCamera() {
        self.cameraNode = SCNNode()
        cameraNode.camera = SCNCamera()
        cameraNode.position = SCNVector3(x: 0, y: 0, z: 5)
        sceneView.scene?.rootNode.addChildNode(cameraNode)
    }

    private func setupGlobe() {
        self.earthNode = EarthNode(radius: earthRadius, earthColor: earthColor, earthGlow: glowColor, earthReflection: reflectionColor)
        sceneView.scene?.rootNode.addChildNode(earthNode)
    }

    private func setupDotGeometry() {
        let textureMap = generateTextureMap(dots: dotCount, sphereRadius: CGFloat(earthRadius))

        let newYork = CLLocationCoordinate2D(latitude: 44.0682, longitude: -121.3153)
        let newYorkDot = closestDotPosition(to: newYork, in: textureMap)

        let dotColor = GenericColor(white: 1, alpha: 1)
        let oceanColor = GenericColor(cgColor: UIColor.systemRed.cgColor)
        let highlightColor = GenericColor(cgColor: UIColor.systemRed.cgColor)
        
        // threshold to determine if the pixel in the earth-dark.jpg represents terrain (0.03 represents rgb(7.65,7.65,7.65), which is almost black)
        let threshold: CGFloat = 0.03
        
        let dotGeometry = SCNSphere(radius: dotRadius)
        dotGeometry.firstMaterial?.diffuse.contents = dotColor
        dotGeometry.firstMaterial?.lightingModel = SCNMaterial.LightingModel.constant
        
        let highlightGeometry = SCNSphere(radius: dotRadius)
        highlightGeometry.firstMaterial?.diffuse.contents = highlightColor
        highlightGeometry.firstMaterial?.lightingModel = SCNMaterial.LightingModel.constant
        
        let oceanGeometry = SCNSphere(radius: dotRadius)
        oceanGeometry.firstMaterial?.diffuse.contents = oceanColor
        oceanGeometry.firstMaterial?.lightingModel = SCNMaterial.LightingModel.constant
        
        var positions = [SCNVector3]()
        var dotNodes = [SCNNode]()
        
        var highlightedNode: SCNNode? = nil
        
        for i in 0...textureMap.count - 1 {
            let u = textureMap[i].x
            let v = textureMap[i].y
            
            let pixelColor = self.getPixelColor(x: Int(u), y: Int(v))
            let isHighlight = u == newYorkDot.x && v == newYorkDot.y
            
            if (isHighlight) {
                let dotNode = SCNNode(geometry: highlightGeometry)
                dotNode.name = "NewYorkDot"
                dotNode.position = textureMap[i].position
                positions.append(dotNode.position)
                dotNodes.append(dotNode)
                
                print("myloc \(textureMap[i].position)")
                
                highlightedNode = dotNode
            } else if (pixelColor.red < threshold && pixelColor.green < threshold && pixelColor.blue < threshold) {
                let dotNode = SCNNode(geometry: dotGeometry)
                dotNode.name = "Other"
                dotNode.position = textureMap[i].position
                positions.append(dotNode.position)
                dotNodes.append(dotNode)
            }
        }
        
        DispatchQueue.main.async {
            let dotPositions = positions as NSArray
            let dotIndices = NSArray()
            let source = SCNGeometrySource(vertices: dotPositions as! [SCNVector3])
            let element = SCNGeometryElement(indices: dotIndices as! [Int32], primitiveType: .point)
            
            let pointCloud = SCNGeometry(sources: [source], elements: [element])
            
            let pointCloudNode = SCNNode(geometry: pointCloud)
            for dotNode in dotNodes {
                pointCloudNode.addChildNode(dotNode)
            }
     
            self.sceneView.scene?.rootNode.addChildNode(pointCloudNode)
            
        }
   }
}


typealias GenericControllerRepresentable = UIViewControllerRepresentable

@available(iOS 13.0, *)
private struct GlobeViewControllerRepresentable: GenericControllerRepresentable {
    var particles: SCNParticleSystem? = nil

    func makeUIViewController(context: Context) -> GlobeViewController {
        let globeController = GlobeViewController(earthRadius: 1.0)
        updateGlobeController(globeController)
        return globeController
    }
    
    func updateUIViewController(_ uiViewController: GlobeViewController, context: Context) { }
}

@available(iOS 13.0, *)
public struct GlobeView: View {
    public var body: some View {
        GlobeViewControllerRepresentable()
    }
}
ios swift scenekit uigesturerecognizer scnnode
1个回答
0
投票

我可以通过在视图确实加载函数中向场景视图添加点击手势处理程序来解决此问题。使用 gesture.location(in: sceneView)sceneView.hitTest(touchLocation, options: nil) 也可以获取 3D 位置。

@objc func handleTap(_ gesture: UITapGestureRecognizer) {
    let touchLocation = gesture.location(in: sceneView)
    let hitTestResults = sceneView.hitTest(touchLocation, options: nil)

    hitTestResults.forEach { result in
        
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.