如何在ARKit中获取屏幕点击位置以实现子弹发射功能?
实现点击屏幕发射子弹的正确方法
问题分析
你的代码核心问题是混淆了发射起点和目标方向的逻辑:你通过射线检测获取了平面上的点,但后续计算的pointPosition并不是正确的发射路径参数,且缺少实际创建子弹节点并赋予移动逻辑的步骤。
正确实现方案
1. 处理屏幕点击事件
在ARSCNView的触摸回调中捕获点击位置,触发发射逻辑:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { guard let touch = touches.first else { return } let touchLocation = touch.location(in: sceneView) fireBullet(from: touchLocation) }
2. 核心发射逻辑(朝屏幕射线方向发射)
从相机位置出发,将屏幕点击点转换为3D空间的射线方向,创建子弹并赋予移动动力:
func fireBullet(from touchLocation: CGPoint) { guard let pointOfView = sceneView.pointOfView else { return } // 获取相机位置 let cameraPosition = pointOfView.position // 将屏幕点击点转换为3D射线方向 let ray = sceneView.unprojectPoint(SCNVector3(touchLocation.x, touchLocation.y, 1)) let direction = SCNVector3Normalize(SCNVector3(ray.x - cameraPosition.x, ray.y - cameraPosition.y, ray.z - cameraPosition.z)) // 创建子弹节点(示例用小球,可替换为自定义模型) let bullet = SCNNode(geometry: SCNSphere(radius: 0.02)) bullet.geometry?.firstMaterial?.diffuse.contents = UIColor.red bullet.position = cameraPosition // 从相机位置发射 // 添加物理体实现移动 bullet.physicsBody = SCNPhysicsBody(type: .dynamic, shape: SCNPhysicsShape(geometry: bullet.geometry!, options: nil)) bullet.physicsBody?.isAffectedByGravity = false // 不需要重力可关闭 bullet.physicsBody?.applyForce(direction * 10, asImpulse: true) // 施加瞬时力 sceneView.scene.rootNode.addChildNode(bullet) // 可选:5秒后移除子弹,避免内存堆积 DispatchQueue.main.asyncAfter(deadline: .now() + 5) { bullet.removeFromParentNode() } }
3. 可选:点击场景平面/物体发射
如果需求是点击场景中的平面或实体目标,朝该点发射,可保留射线检测逻辑,调整方向计算:
// 替换核心逻辑中的方向计算部分 let estimatedPlane: ARRaycastQuery.Target = .existingPlaneInfinite let alignment: ARRaycastQuery.TargetAlignment = .any guard let query = sceneView.raycastQuery(from: touchLocation, allowing: estimatedPlane, alignment: alignment), let result = sceneView.session.raycast(query).first else { return } // 获取目标点位置 let targetPosition = SCNVector3(result.worldTransform.columns.3.x, result.worldTransform.columns.3.y, result.worldTransform.columns.3.z) // 计算朝向目标的方向 let direction = SCNVector3Normalize(SCNVector3(targetPosition.x - cameraPosition.x, targetPosition.y - cameraPosition.y, targetPosition.z - cameraPosition.z))
注意事项
- 确保你的
ARSCNView已初始化物理世界:sceneView.scene.physicsWorld.gravity = SCNVector3(0, -9.8, 0)(需要重力时开启) - 子弹模型可替换为自定义的SCNGeometry或外部导入的模型
内容的提问来源于stack exchange,提问作者hui
相关产品推荐
相关产品推荐

