如何在ARKit中点击和移动场景节点

时间:2018-11-26 08:22:18

标签: ios swift scenekit assets arkit

我目前正在尝试构建AR Chess应用程序,但无法正常工作。

我希望能够点击一个棋子,然后突出显示它在棋盘上可以进行的合法动作,并将其移动到用户点击的任何正方形。

棋盘设计和节点的图片: https://gyazo.com/2a88f9cda3f127301ed9b4a44f8be047

我要实现的目标: https://imgur.com/a/IGhUDBW

非常感谢您提供有关如何使其正常工作的建议。

谢谢!

ViewController代码:

import UIKit
import SceneKit
import ARKit

class ViewController: UIViewController, ARSCNViewDelegate {

    @IBOutlet var sceneView: ARSCNView!

    override func viewDidLoad() {
        super.viewDidLoad()

        // Set the view's delegate
        sceneView.delegate = self

        // Show statistics such as fps and timing information
        sceneView.showsStatistics = true

        // Add lighting to the scene
        sceneView.autoenablesDefaultLighting = true
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        // Create a session configuration to track an external image
        let configuration = ARImageTrackingConfiguration()

        // Image detection
        // Reference which group to find the image to detect in the Assets folder e.g. "Detection Card"
        if let imageDetect = ARReferenceImage.referenceImages(inGroupNamed: "Detection Card", bundle: Bundle.main) {
            // Sets image tracking properties to the image in the referenced group
            configuration.trackingImages = imageDetect
            // Amount of images to be tracked
            configuration.maximumNumberOfTrackedImages = 1
        }

        // Run the view's session
        sceneView.session.run(configuration)
    }

    // Run when horizontal surface is detected and display 3D object onto image
    // ARAnchor - tells a certain point in world space is relevant to your app, makes virtual content appear "attached" to some real-world point of interest
    func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode {
        // Creates 3D object
        let obj = SCNNode()
        // Check if image detected through camera is an ARImageAnchor - which contains position and orientation data about the image detected in the session
        if let imageAnchor = anchor as? ARImageAnchor {
            // Set dimensions of the horizontal plane to be displayed onto the image to be the same as the image uploaded
            let plane = SCNPlane(width: imageAnchor.referenceImage.physicalSize.width, height: imageAnchor.referenceImage.physicalSize.height)
            // Display mild transparent layer onto detected image
            // This is to ensure image detection works by display a faint layer on the image
            plane.firstMaterial?.diffuse.contents = UIColor(white: 1.0, alpha: 0.2)
            // Set geometry shape of the plane
            let planeNode = SCNNode(geometry: plane)
            // Flip vertical plane to horizontal plane
            planeNode.eulerAngles.x = -Float.pi / 2
            obj.addChildNode(planeNode)

            // Initialise chess scene
            if let chessBoardSCN = SCNScene(named: "art.scnassets/chess.scn") {
                // If there is a first in the scene file
                if let chessNodes = chessBoardSCN.rootNode.childNodes.first {
                    // Displays chessboard upright
                    chessNodes.eulerAngles.x = Float.pi / 2
                    // Adds chessboard to the overall 3D scene
                    obj.addChildNode(chessNodes)
                }
            }

        }

        return obj

    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)

        // Pause the view's session
        sceneView.session.pause()
    }

}

2 个答案:

答案 0 :(得分:0)

您将需要在视图上添加手势,并使用ARSceneViews hitTest方法检测手势在您的场景中所触摸的内容。然后,您可以根据手势的移动来更新位置。

这是一个与拖动节点大致相同的问题。

Placing, Dragging and Removing SCNNodes in ARKit

答案 1 :(得分:0)

首先,您需要添加一个手势识别器以点按到viewDidLoad中,如下所示:

 let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:)))
 myScnView.addGestureRecognizer(tapGesture)

然后实现处理程序功能:

    @objc
    func handleTap(_ gestureRecognize: UIGestureRecognizer) {
       // HERE YOU NEED TO DETECT THE TAP
       // check what nodes are tapped

        let location = gestureRecognize.location(in: myScnView)
        let hitResults = myScnView.hitTest(location, options: [:])

        // check that we clicked on at least one object
        if hitResults.count > 0 {
            // retrieved the first clicked object
            let tappedPiece = hitResults[0].node

            // HERE YOU CAN SHOW POSSIBLE MOVES
            //Ex. showPossibleMoves(for: tappedPiece) 
        }

    }

现在,要显示可能的移动,您需要 在棋盘上标识所有象限和节点位置

  

为此,您可以分配名称或数字,或者字母和数字的组合,或者数字的组合。 (我建议使用数字组合,例如第1行第1列,例如矩阵)。

让我接受我的建议,所以您可以命名每个象限1.1 1.2 ... 2.1 2.2等等。

现在,要检测您的作品在哪里,您可以检查与PhysicsContactDelegate的联系。

现在您有了tappedPiece以及它所在的位置,因此您必须为这些作品定义规则,例如:

let rules = ["tower":"cross"] //add the others

NB 。您可以选择要定义的规则。

让我接受我的建议,现在您应该创建突出显示功能:

 func highlight(quadrant: SCNNode){
   quadrant.geometry?.firstMaterial?.emission.contents = UIColor.yellow
 }

最后 showPossibleMoves(用于:tappedPiece)可能是这样的:

func showPossibleMoves(for piece: SCNNode){

let pieceType = piece.name //You have to give the name as you did into your rules variable
 //ex. if you have rules like ["tower":"cross"] you have to set all towers name to "tower"

let rule = rules[pieceType]

switch rule{

case "cross":

      //you have to highlight all nodes on the right, left, above and bottom
      // you can achieve this by selecting the start point and increase it
      //assuming you named your quadrants like 1.1 1.2 or 11 12 13 ecc...
      let startRow = Int(startQuadrant.name.first)
      let startColumn = Int(startQuadrant.name.last)

      //Now loop the highlight on right
      for column in startColumn+1...MAX_COLUMN-1{
         let quadrant = myScnView.scene.rootNode.childNode(withName:"\(startRow).\(column)" , recursively: true)
         // call highlight function
          highlight(quadrant: quadrant)
      }

      //Now loop for above quadrants
      for row in startRow+1...MAX_ROW-1{
         let quadrant = myScnView.scene.rootNode.childNode(withName:"\(row).\(startColumn)" , recursively: true)
         // call highlight function
          highlight(quadrant: quadrant)
      }

      //DO THE SAME FOR ALL DIRECTIONS
}

// ADD ALL CASES, like bishop movements "diagonals" and so on

}
  

注意: :在handlerTap函数中,您必须检查所点击的内容,例如,在选择一个片段后检查是否在一个象限上进行点击(您要移动),可以检查布尔值和所选节点的名称

  //assuming you have set the boolean value after selecting a piece

  if pieceSelected && node.name != "tower"{

      //HERE YOU CAN MOVE YOUR PIECE

  }