检测在 arkit 上点击了哪个平面
detect which plane is tapped on arkit
我正在使用 ARKit,我在一个场景中有多个平面节点 (SCNPlanes)。我想使用 hittest 来检测飞机上的触摸,但我不确定如何检测哪架飞机被点击了。每架飞机都有一个与我的图像识别代码相关联的名称。这是我的 TouchesBegan 函数:
//detects taps on transparent planes created by reference images
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first as! UITouch
if(touch.view == self.sceneView){
let viewTouchLocation:CGPoint = touch.location(in: sceneView)
guard let result = sceneView.hitTest(viewTouchLocation, options: nil).first else {
return
}
print("results", "\(result)")
let touchPlaneNode = planeNode
if touchPlaneNode == result.node {
print("tapped on a match, but which plane did I tap on?")
}
}
}
谢谢!
假设我已经正确阅读了你的问题,开始的一个好方法是将 ARPlaneAnchor 及其关联的 SCNNode 存储在字典中,例如:
var planes = [ARPlaneAnchor: PlaneNode]()
我的 'Plane Node' 值是自定义 SCNNode,但您可以存储任何您喜欢的节点。
然后将在以下 ARSCNView 委托方法中创建此引用:
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
//1. Check The We Have An ARPlane Anchor
guard let planeAnchor = anchor as? ARPlaneAnchor else { return }
//2. Create Our Plane Node
let planeNode = PlaneNode(anchor: planeAnchor, node: node, image: true, identifier: planes.keys.count, opacity: 1)
//3. Store A Reference To It
planes[planeAnchor] = planeNode
}
然后在 Touches Began 中你可以这样做:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
/*
1. Get The Current Touch Location
2. Check That We Have Touched A Valid Node
3. Check That Our Touched Object Is An ARPlane Anchor
4. Determine If It Is One That Has Been Stored
5. Get It's Name
*/
guard let touchLocation = touches.first?.location(in: augmentedRealityView),
let hitTest = augmentedRealityView.hitTest(touchLocation, types: .existingPlaneUsingExtent).first,
let planeAnchor = hitTest.anchor as? ARPlaneAnchor,
let detectedPlane = self.planes[planeAnchor],
let nodeID = detectedPlane.name
else {
//No Valid Plane Has Been Detected
return
}
print(nodeID)
}
在touchesBegan方法中,augmentedRealityView引用了一个设置为IBOutlet的ARSCNView。
我的 PlaneNode Class 看起来像这样(您需要添加自己的图像,在我的 Assets Bundle 中称为:defaultGrid):
class PlaneNode: SCNNode {
let DEFAULT_IMAGE: String = "defaultGrid"
let NAME: String = "PlaneNode"
var planeGeometry: SCNPlane
var planeAnchor: ARPlaneAnchor
var widthInfo: String!
var heightInfo: String!
var alignmentInfo: String!
//---------------
//MARK: LifeCycle
//---------------
/// Inititialization
///
/// - Parameters:
/// - anchor: ARPlaneAnchor
/// - node: SCNNode
/// - node: Bool
init(anchor: ARPlaneAnchor, node: SCNNode, image: Bool, identifier: Int, opacity: CGFloat = 0.25){
self.planeAnchor = anchor
self.planeGeometry = SCNPlane(width: CGFloat(anchor.extent.x), height: CGFloat(anchor.extent.z))
let planeNode = SCNNode(geometry: planeGeometry)
super.init()
if image{
let planeMaterial = SCNMaterial()
planeMaterial.diffuse.contents = UIImage(named: DEFAULT_IMAGE)
self.planeGeometry.materials = [planeMaterial]
}
planeNode.simdPosition = float3(self.planeAnchor.center.x, 0, self.planeAnchor.center.z)
planeNode.eulerAngles.x = -.pi / 2
planeNode.opacity = opacity
node.addChildNode(planeNode)
node.name = "\(NAME) \(identifier)"
}
required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") }
deinit {
#if DEBUG
print("Plane Node Deinitialized")
#endif
}
/// Updates The Size Of The Plane As & When The ARPlaneAnchor Has Been Updated
///
/// - Parameter anchor: ARPlaneAnchor
func update(_ anchor: ARPlaneAnchor) {
self.planeAnchor = anchor
self.planeGeometry.width = CGFloat(anchor.extent.x)
self.planeGeometry.height = CGFloat(anchor.extent.z)
self.position = SCNVector3Make(anchor.center.x, 0.01, anchor.center.z)
returnPlaneInfo()
}
//-----------------------
//MARK: Plane Information
//-----------------------
/// Returns The Size Of The ARPlaneAnchor & Its Alignment
func returnPlaneInfo(){
let widthOfPlane = self.planeAnchor.extent.x
let heightOfPlane = self.planeAnchor.extent.z
var planeAlignment: String!
switch planeAnchor.alignment {
case .horizontal:
planeAlignment = "Horizontal"
case .vertical:
planeAlignment = "Vertical"
}
#if DEBUG
print("""
Width Of Plane = \(String(format: "%.2fm", widthOfPlane))
Height Of Plane = \(String(format: "%.2fm", heightOfPlane))
Plane Alignment = \(planeAlignment)
""")
#endif
self.widthInfo = String(format: "%.2fm", widthOfPlane)
self.heightInfo = String(format: "%.2fm", heightOfPlane)
self.alignmentInfo = planeAlignment
}
}
我正在使用 ARKit,我在一个场景中有多个平面节点 (SCNPlanes)。我想使用 hittest 来检测飞机上的触摸,但我不确定如何检测哪架飞机被点击了。每架飞机都有一个与我的图像识别代码相关联的名称。这是我的 TouchesBegan 函数:
//detects taps on transparent planes created by reference images
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first as! UITouch
if(touch.view == self.sceneView){
let viewTouchLocation:CGPoint = touch.location(in: sceneView)
guard let result = sceneView.hitTest(viewTouchLocation, options: nil).first else {
return
}
print("results", "\(result)")
let touchPlaneNode = planeNode
if touchPlaneNode == result.node {
print("tapped on a match, but which plane did I tap on?")
}
}
}
谢谢!
假设我已经正确阅读了你的问题,开始的一个好方法是将 ARPlaneAnchor 及其关联的 SCNNode 存储在字典中,例如:
var planes = [ARPlaneAnchor: PlaneNode]()
我的 'Plane Node' 值是自定义 SCNNode,但您可以存储任何您喜欢的节点。
然后将在以下 ARSCNView 委托方法中创建此引用:
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
//1. Check The We Have An ARPlane Anchor
guard let planeAnchor = anchor as? ARPlaneAnchor else { return }
//2. Create Our Plane Node
let planeNode = PlaneNode(anchor: planeAnchor, node: node, image: true, identifier: planes.keys.count, opacity: 1)
//3. Store A Reference To It
planes[planeAnchor] = planeNode
}
然后在 Touches Began 中你可以这样做:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
/*
1. Get The Current Touch Location
2. Check That We Have Touched A Valid Node
3. Check That Our Touched Object Is An ARPlane Anchor
4. Determine If It Is One That Has Been Stored
5. Get It's Name
*/
guard let touchLocation = touches.first?.location(in: augmentedRealityView),
let hitTest = augmentedRealityView.hitTest(touchLocation, types: .existingPlaneUsingExtent).first,
let planeAnchor = hitTest.anchor as? ARPlaneAnchor,
let detectedPlane = self.planes[planeAnchor],
let nodeID = detectedPlane.name
else {
//No Valid Plane Has Been Detected
return
}
print(nodeID)
}
在touchesBegan方法中,augmentedRealityView引用了一个设置为IBOutlet的ARSCNView。
我的 PlaneNode Class 看起来像这样(您需要添加自己的图像,在我的 Assets Bundle 中称为:defaultGrid):
class PlaneNode: SCNNode {
let DEFAULT_IMAGE: String = "defaultGrid"
let NAME: String = "PlaneNode"
var planeGeometry: SCNPlane
var planeAnchor: ARPlaneAnchor
var widthInfo: String!
var heightInfo: String!
var alignmentInfo: String!
//---------------
//MARK: LifeCycle
//---------------
/// Inititialization
///
/// - Parameters:
/// - anchor: ARPlaneAnchor
/// - node: SCNNode
/// - node: Bool
init(anchor: ARPlaneAnchor, node: SCNNode, image: Bool, identifier: Int, opacity: CGFloat = 0.25){
self.planeAnchor = anchor
self.planeGeometry = SCNPlane(width: CGFloat(anchor.extent.x), height: CGFloat(anchor.extent.z))
let planeNode = SCNNode(geometry: planeGeometry)
super.init()
if image{
let planeMaterial = SCNMaterial()
planeMaterial.diffuse.contents = UIImage(named: DEFAULT_IMAGE)
self.planeGeometry.materials = [planeMaterial]
}
planeNode.simdPosition = float3(self.planeAnchor.center.x, 0, self.planeAnchor.center.z)
planeNode.eulerAngles.x = -.pi / 2
planeNode.opacity = opacity
node.addChildNode(planeNode)
node.name = "\(NAME) \(identifier)"
}
required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") }
deinit {
#if DEBUG
print("Plane Node Deinitialized")
#endif
}
/// Updates The Size Of The Plane As & When The ARPlaneAnchor Has Been Updated
///
/// - Parameter anchor: ARPlaneAnchor
func update(_ anchor: ARPlaneAnchor) {
self.planeAnchor = anchor
self.planeGeometry.width = CGFloat(anchor.extent.x)
self.planeGeometry.height = CGFloat(anchor.extent.z)
self.position = SCNVector3Make(anchor.center.x, 0.01, anchor.center.z)
returnPlaneInfo()
}
//-----------------------
//MARK: Plane Information
//-----------------------
/// Returns The Size Of The ARPlaneAnchor & Its Alignment
func returnPlaneInfo(){
let widthOfPlane = self.planeAnchor.extent.x
let heightOfPlane = self.planeAnchor.extent.z
var planeAlignment: String!
switch planeAnchor.alignment {
case .horizontal:
planeAlignment = "Horizontal"
case .vertical:
planeAlignment = "Vertical"
}
#if DEBUG
print("""
Width Of Plane = \(String(format: "%.2fm", widthOfPlane))
Height Of Plane = \(String(format: "%.2fm", heightOfPlane))
Plane Alignment = \(planeAlignment)
""")
#endif
self.widthInfo = String(format: "%.2fm", widthOfPlane)
self.heightInfo = String(format: "%.2fm", heightOfPlane)
self.alignmentInfo = planeAlignment
}
}