答案 0 :(得分:1)
如果您想制作虚拟按钮,您有多种选择:
1 :(实验性):您可以在其上呈现带有2个按钮的自定义UIView
。
第二名:使用SCNNodes
创建两个SCNPlane Geometry
,然后使用图片提供真或假提示。
第3名:使用上图中显示的SCNText
。
如果您选择了第一个选项,则无需执行SCNHitTest
,因为您可以使用IBActions
来确定哪个被点按。
对于其他选项,您需要使用SCNHitTest
:
沿您指定的光线查找SCNGeometry对象。对于每一个 在射线和几何体之间的交集,SceneKit创建了一个 命中测试结果,提供有关SCNNode对象的信息 包含几何和交叉点的位置 几何的表面。
我不会与您讨论第一个选项的细节,因为这不是一个“标准”也不是广泛采用的做法(如果全部)。
让我们先看看使用两个SCNNodes
作为带有SCNPlaneGeometry
的“虚拟按钮”:
/// Creates A Menu With A True Or False Button Using SCNPlane Geometry
func createTrueOrFalseMenu(){
//1. Create A Menu Holder
let menu = SCNNode()
//2. Create A True Button With An SCNPlane Geometry & Green Colour
let trueButton = SCNNode();
let trueButtonGeometry = SCNPlane(width: 0.2, height: 0.2)
let greenMaterial = SCNMaterial()
greenMaterial.diffuse.contents = UIColor.green
greenMaterial.isDoubleSided = true
trueButtonGeometry.firstMaterial = greenMaterial
trueButton.geometry = trueButtonGeometry
trueButton.name = "True"
//3. Create A False Button With An SCNPlane Geometry & A Red Colour
let falseButton = SCNNode();
let falseButtonGeometry = SCNPlane(width: 0.2, height: 0.2)
let redMaterial = SCNMaterial()
redMaterial.diffuse.contents = UIColor.red
redMaterial.isDoubleSided = true
falseButtonGeometry.firstMaterial = redMaterial
falseButton.geometry = falseButtonGeometry
falseButton.name = "False"
//4. Set The Buttons Postions
trueButton.position = SCNVector3(-0.2,0,0)
falseButton.position = SCNVector3(0.2,0,0)
//5. Add The Buttons To The Menu Node & Set Its Position
menu.addChildNode(trueButton)
menu.addChildNode(falseButton)
menu.position = SCNVector3(0,0, -1.5)
//6. Add The Menu To The View
augmentedRealityView.scene.rootNode.addChildNode(menu)
}
现在让我们看一下使用SCNNodes
将两个SCNTextGeometry
用作“虚拟按钮”:
/// Creates A Menu With A True Or False Button Using SCNText Geometry
func createTrueOrFalseMenuWithText(){
//1. Create A Menu Holder
let menu = SCNNode()
//2. Create A True Button With An SCNText Geometry & Green Colour
let trueButton = SCNNode();
let trueTextGeometry = SCNText(string: "True" , extrusionDepth: 1)
trueTextGeometry.font = UIFont(name: "Helvatica", size: 3)
trueTextGeometry.flatness = 0
trueTextGeometry.firstMaterial?.diffuse.contents = UIColor.green
trueButton.geometry = trueTextGeometry
trueButton.scale = SCNVector3(0.01, 0.01 , 0.01)
trueButton.name = "True"
//3. Create A False Button With An SCNText Geometry & Red Colour
let falseButton = SCNNode();
let falseTextGeometry = SCNText(string: "False" , extrusionDepth: 1)
falseTextGeometry.font = UIFont(name: "Helvatica", size: 3)
falseTextGeometry.flatness = 0
falseTextGeometry.firstMaterial?.diffuse.contents = UIColor.red
falseButton.geometry = falseTextGeometry
falseButton.scale = SCNVector3(0.01, 0.01 , 0.01)
falseButton.name = "False"
//4. Set The Buttons Postions
trueButton.position = SCNVector3(-0.2,0,0)
falseButton.position = SCNVector3(0.2,0,0)
//5. Add The Buttons To The Menu Node & Set Its Position
menu.addChildNode(trueButton)
menu.addChildNode(falseButton)
menu.position = SCNVector3(0,0, -1.5)
//6. Add The Menu To The View
augmentedRealityView.scene.rootNode.addChildNode(menu)
}
现在我们有不同的实现设置,然后您需要创建某种逻辑来处理我们是否触及了true或false按钮。
您会注意到,在创建真或假按钮时,我使用了name property
来帮助我们确定哪个被点击了,例如:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
//1. Get The Current Touch Location
guard let touchLocation = touches.first?.location(in: self.augmentedRealityView),
//2. Perform An SCNHitTest & Get The Node Touched
let hitTestNode = self.augmentedRealityView.hitTest(touchLocation, options: nil).first?.node else { return }
//3. Determine Whether The User Pressed True Or False & Handle Game Logic
if hitTestNode.name == "True"{
print("User Has A Correct Answer")
}else if hitTestNode.name == "False"{
print("User Has An InCorrect Answer")
}
}
更新:为了检测在标准触摸之外选择了哪个虚拟按钮,您有两个选项,一个用于确定按钮是inViewOfFrostum
还是使用基于的SCNHitTest指定的CGPoint
例如屏幕的中心
考虑第一个选项,我们需要考虑到ARCamera有一个Frostrum,其中显示了我们的内容:
然后,您可以通过创建确定此功能的函数来确定用户是否选择了virtualButton。但是,这可能不是你想要的,因为这意味着你必须确保SCNNode按钮放置得足够分开,以确保一次只能看到一个。
如果您需要此选项,首先需要两个SCNNodes
,例如:
var trueButton: SCNNode!
var falseButton: SCNNode!
然后创建一个这样的函数:
/// Detects If An Object Is In View Of The Camera Frostrum
func detectButtonInFrostrumOfCamera(){
//1. Get The Current Point Of View
if let currentPointOfView = augmentedRealityView.pointOfView{
if augmentedRealityView.isNode(trueButton, insideFrustumOf: currentPointOfView){
print("True Button Is In View & Has Been Selected As The Answer")
}
if augmentedRealityView.isNode(falseButton, insideFrustumOf: currentPointOfView){
print("False Button Is In View & Has Been Selected As The Answer")
}
}
}
然后您将在以下delegate
回调中触发,例如
func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {
detectButtonInFrostrumOfCamera()
}
然而,更可能的解决方案是使用CGPoint作为参考来执行虚拟光线投射。
在这个示例中,让我们首先创建我们的CGPoint var
,它将引用屏幕的中心:
var screenCenter: CGPoint!
然后我们会在viewDidLoad
中设置此项:
DispatchQueue.main.async {
self.screenCenter = CGPoint(x: self.view.bounds.width/2, y: self.view.bounds.height/2)
}
然后我们将创建一个对屏幕中心执行SCNHitTest
的功能,以查看这些点是否触及真或假按钮,例如:
/// Detects If We Have Intersected A Virtual Button
func detectIntersetionOfButton(){
guard let rayCastTarget = self.augmentedRealityView?.hitTest(screenCenter, options: nil).first else { return }
if rayCastTarget.node.name == "True"{
print("User Has Selected A True Answer")
}
if rayCastTarget.node.name == "False"{
print("User Has Selected A False Answer")
}
}
将在以下委托回调中再次调用:
func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {
detectIntersetionOfButton()
}
答案 1 :(得分:0)
我认为这个有趣的信息化答案可以适用于许多不同的用途和方法,并且也得到了很好的解释(一如既往地提供答案: - )
答案 2 :(得分:0)
嘿@JoshRobbins我正在使用你给定的代码用SCNTextgeometry创建两个SCNNode,然后使用光线投射代码。我无法在设备上获得任何输出。当我运行项目时,真正的错误按钮不可见。我写的代码有什么问题吗?任何帮助将不胜感激 class ViewController:UIViewController,ARSCNViewDelegate {
@IBOutlet var sceneView: ARSCNView!
var screenCenter: CGPoint!
var trueButton: SCNNode!
var falseButton: SCNNode!
override func viewDidLoad() {
super.viewDidLoad()
// Set the view's delegate
sceneView.delegate = self
/// Creates A Menu With A True Or False Button Using SCNText Geometry
func createTrueOrFalseMenuWithText(){
//1. Create A Menu Holder
let menu = SCNNode()
//2. Create A True Button With An SCNText Geometry & Green Colour
let trueButton = SCNNode();
let trueTextGeometry = SCNText(string: "True" , extrusionDepth: 1)
trueTextGeometry.font = UIFont(name: "Helvatica", size: 3)
trueTextGeometry.flatness = 0
trueTextGeometry.firstMaterial?.diffuse.contents = UIColor.green
trueButton.geometry = trueTextGeometry
trueButton.scale = SCNVector3(0.01, 0.01 , 0.01)
trueButton.name = "True"
//3. Create A False Button With An SCNText Geometry & Red Colour
let falseButton = SCNNode();
let falseTextGeometry = SCNText(string: "False" , extrusionDepth: 1)
falseTextGeometry.font = UIFont(name: "Helvatica", size: 3)
falseTextGeometry.flatness = 0
falseTextGeometry.firstMaterial?.diffuse.contents = UIColor.red
falseButton.geometry = falseTextGeometry
falseButton.scale = SCNVector3(0.01, 0.01 , 0.01)
falseButton.name = "False"
//4. Set The Buttons Postions
trueButton.position = SCNVector3(-0.2,0,0)
falseButton.position = SCNVector3(0.2,0,0)
//5. Add The Buttons To The Menu Node & Set Its Position
menu.addChildNode(trueButton)
menu.addChildNode(falseButton)
menu.position = SCNVector3(0,0, -1.5)
//6. Add The Menu To The View
sceneView.scene.rootNode.addChildNode(menu)
}
DispatchQueue.main.async {
self.screenCenter = CGPoint(x: self.view.bounds.width/2, y: self.view.bounds.height/2)
}
/// Detects If We Have Intersected A Virtual Button
func detectIntersetionOfButton(){
guard let rayCastTarget = self.sceneView?.hitTest(screenCenter, options: nil).first else { return }
if rayCastTarget.node.name == "TrueButton"{
print("User Has Selected A True Answer")
}
if rayCastTarget.node.name == "FalseButton"{
print("User Has Selected A False Answer")
}
}
func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {
detectIntersetionOfButton()
}
}