简体   繁体   中英

SceneKit SCNNode follow touch movement

I'm trying to have a Sphere node follow the movement of my finger on the screen on it's x axis only, without affecting the y or z values of the sphere. I'm not sure if I should be applying a force to the physics body or moving it with an SCNAction. The sphere will be bouncing on a cube node that takes up the width of the screen. My biggest problem as of now is figuring out what to put inside the pangesture function. Here is an example of what I'm trying to replicate, where the user slides their finger across the screen and the ball mirrors its location.

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {

    for t in touches {

        let touchLocation = t.location(in: self.view)
        let touchCoordinatesX = (touchLocation.x - self.scnView.frame.width / 2)
        ballNode.position.x = Float(touchCoordinatesX/75)
    }
}

The code above works somewhat, when I drag my finger across the screen the y-position freaks out, and lags up and down really quickly even though my code never edits the y-value of the ball. Everytime I drag across the screen, the y-position of the ball is reset to its original value when it was added to the scene's root node. 跳

Based off your answer this is what I used and does exactly what I need it to.

@objc func handlePan(recognizer: UIPanGestureRecognizer)
{
    currentLocation = recognizer.location(in: self.view)

    let touchCoordinatesX = (currentLocation.x - self.scnView.frame.width / 2)



   if recognizer.state == .changed

   {

    ballNode.position.x = Float(touchCoordinatesX/75)
    ballNode.position.y = ballNode.presentation.position.y

    }

}

Try handlePan and setting/saving a dragMode state variable. The code below is something I used to select a panel and strafe the screen, so there is some extra code in for those things, but I think you could modify it to fit your needs. In your case, my "strafing the screen" should be similar to your need to drag the ball towards your fingertip.

If you set your ball object to currentLocation.x in dragChanges(), then it should line up exactly on your finger tip and the response should happen quickly. My guess based on your picture is that you'd want a slight delay so that it's more realistic. If that's the case, I'd suggest adding a timer and processing a small queue of saved X positions so that it comes towards your finger tip in small increments.

Hope that helps.

//**************************************************************************
    @objc func handlePan(recognizer: UIPanGestureRecognizer)
    {
        currentLocation = recognizer.location(in: gameScene)
        let getLocation: CGPoint = recognizer.location(in: gameScene)
        let panHitTestResults = gameScene.hitTest(getLocation, options: hitTestOptions)

        if(data.gameState == .endWave || data.gameState == .endGame ||
            data.gameState == .defenseAdd || data.gameState == .defenseUpgrade)
        {
            dragMode = .none
            selectorActive = false
            lastPanelSelected = ""
            return
        }

        // State Begins
        if recognizer.state == UIGestureRecognizerState.began
        {
            dragMode = .drag
            beginLocation = recognizer.location(in: gameScene)
            data.panX = currentLocation.x
            data.lastMouseX = Float(beginLocation.x)
            data.lastMouseY = Float(beginLocation.y)
            dragBegins(vRecognizer: recognizer)
            selectorActive = false
        }
        // State Changes
        if(recognizer.state == UIGestureRecognizerState.changed)
        {
            dragChanges(vRecognizer: recognizer)

            if(data.gameState == .run && dragMode == .drag)
            {
                // If we were selecting cursor and want to cancel, just touch with 2 fingers
                if(recognizer.numberOfTouches == 2)
                {
                    dragMode = .none
                    selectorActive = false
                    grid.cancelCursor()
                    return
                }
                for vHit in panHitTestResults
                {
                    if(vHit.node.name?.prefix(5) == "Panel")
                    {
                        selectorActive = true
                        lastPanelSelected = vHit.node.name!
                        data.panelSelected = lastPanelSelected
                        let _ = grid.selectCursor()
                        return
                    }
                }
            }
        }

        if(recognizer.state == UIGestureRecognizerState.ended)
        {
            dragEnds(vRecognizer: recognizer)
            dragMode = .none

            if(selectorActive == true)
            {
                gameControl.selectPanel(vPanel: lastPanelSelected)
                lastPanelSelected = ""
                selectorActive = false
                return
            }
        }
    }
    //**************************************************************************
    func dragBegins(vRecognizer: UIPanGestureRecognizer)
    {
        if(data.gameState == .run)
        {
            if(vRecognizer.numberOfTouches == 2) { dragMode = .strafe }
        }
    }
    //**************************************************************************
    func dragChanges(vRecognizer: UIPanGestureRecognizer)
    {
        if(data.gameState == .run)
        {
            if(dragMode == .strafe && vRecognizer.numberOfTouches == 1)
            {
                dragMode = .none
                return
            }

            switch(dragMode)
            {
            case .strafe:
                gNodes.camera.strafe(vX: Float(currentLocation.x), vY: Float(currentLocation.y))
                break
            case .none:
                break
            default:
                break
            }
        }
    }
    //**************************************************************************
    func dragEnds(vRecognizer: UIPanGestureRecognizer)
    {
        if(data.gameState == .run)
        {
            switch(dragMode)
            {
            case .strafe:
                break
            default:
                break
            }
        }
        dragMode = .none
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM