在运行时使用捏合手势缩放SCNNode

6
我是一位有用的助手,可以为您翻译文本。
我正在尝试使用捏合手势实时缩放 SCNNode:
这是我的当前代码:
let pinchGestureRecognizer = UIPinchGestureRecognizer(target: self, action: #selector(handlePinch(from:)))
sceneView.addGestureRecognizer(pinchGestureRecognizer)

@objc
func handlePinch(from recognizer: UIPinchGestureRecognizer){
  var pinchScale = recognizer.scale
  pinchScale = round(pinchScale * 1000) / 1000.0

  sceneView.scene.rootNode.enumerateChildNodes { (node, stop) -> Void in
    if(node.name == "Box01"){
       node.scale = SCNVector3(x: pinchScale, y: pinchScale, z: pinchScale)
    }
  }        
}

然而,这个节点无法在大或小规模上进行扩展?请有人指出我的错误吗?
SCNNode已加载并应用了动画,如下所示:
sceneView.scene.rootNode.addChildNode(node)
loadAnimation(animation: .Attack, sceneName: "art.scnassets/attack", animationIdentifier: "attackID");

节点的确是命名为'Box01'吗?考虑使用根节点上的childNode(withName:recursively:)方法,这样应该更清晰明了。 - jlsiewert
我对Scenekit很菜,也不擅长它。我会阅读你提到的方法文档。但是,我仍然无法正确地缩放它,对吗? - Pavan K
5个回答

8

已经在Swift中成功运行

@objc func handlePinch(gesture: UIPinchGestureRecognizer){
    if(scnnodeSelected){

        if (gesture.state == .changed) {
            let pinchScaleX = Float(gesture.scale) * tappedObjectNode.scale.x
            let pinchScaleY =  Float(gesture.scale) * tappedObjectNode.scale.y
            let pinchScaleZ =  Float(gesture.scale) * tappedObjectNode.scale.z
            tappedObjectNode.scale = SCNVector3(pinchScaleX, pinchScaleY, pinchScaleZ)
            gesture.scale=1
        }
    }
}

5
func addPinchGestureToSceneView(){
    let pinchGestureRecognizer = UIPinchGestureRecognizer(target: self, action: #selector(ViewController.handlePitch(withGestureRecognizer:)))
    sceneView.addGestureRecognizer(pinchGestureRecognizer)
}


@objc func handlePitch(withGestureRecognizer recognizer: UIPinchGestureRecognizer) {
    let tapRecognizer = recognizer.location(in: sceneView)
    let hitTestResults = sceneView.hitTest(tapRecognizer)
    guard let node = hitTestResults.first?.node else {
        return
    }

    if (recognizer.state == .changed) {
        let pinchScaleX = Float(recognizer.scale) * node.scale.x
        let pinchScaleY =  Float(recognizer.scale) * node.scale.y
        let pinchScaleZ =  Float(recognizer.scale) * node.scale.z

        node.scale = SCNVector3(x: Float(pinchScaleX), y: Float(pinchScaleY), z: Float(pinchScaleZ))
        recognizer.scale=1
    }

}

对我有用!谢谢 - Deepak

2

我使用这个来处理规模:

@objc
func handlePinch(from recognizer: UIPinchGestureRecognizer){
    var pinchScale = round(recognizer.scale * 1000)/1000000
    let node_arm = sceneView.scene.rootNode.childNode(withName: "army", recursively: true)
    node_arm?.runAction(.customAction(duration: 0, action: { node, progress in
        node.physicsBody = nil
        node.scale = SCNVector3(x: Float(pinchScale), y: Float(pinchScale), z: Float(pinchScale))
    }))
}

这个对你有用吗?当我使用它时,物品一开始只会变得非常小,然后我必须不断地捏它才能使它变大。 - Alan
@AlanS 你说的是我得到的结果。但这对我起作用了。如果你需要保留状态,恐怕我没有解决方案。如果你找到了一个,我很乐意接受答案 :) - Pavan K
是的,我找到了一些非常好的解决方案,我会发布一个答案,不过很抱歉,我正在使用Objective-C编程。 - Alan

1
我发现这个方法非常有效。我还使用两个手指来尝试检测是否在捏合对象,这样你就可以用任何一个手指抓取SCNNode。
请注意,重置比例的行是必要的,否则它会表现得非常不稳定。我不能特别说为什么会出现这种情况。
-(void)scaleObject:(UIPinchGestureRecognizer *)recognizer {
    if (recognizer.state == UIGestureRecognizerStateBegan) {

        CGPoint tapPoint = [recognizer locationOfTouch:1 inView:_sceneView]; //Get tap location on the screen from the 2nd touch
        NSArray <SCNHitTestResult *> *result = [self.sceneView hitTest:tapPoint options:nil]; //Get result array, checks on if we hit a SceneNode or not

        if ([result count] == 0) { //If the first touch doesn't grap the SceneNode, try the second touch
            tapPoint = [recognizer locationOfTouch:0 inView:_sceneView];
            result = [self.sceneView hitTest:tapPoint options:nil]; // Get the results
            if ([result count] == 0) {
                return; //No objects found, return
            }
        }

        SCNHitTestResult *hitResult = [result firstObject]; //Get the first hitResult
        scaledObject = [[hitResult node] parentNode];
        if (scaledObject) {
            [NotificationView showNotificationWithText:@"Object has been selected for pinch"];
        }
    }
    if (recognizer.state == UIGestureRecognizerStateChanged) { //When pinch status is changing
        if (scaledObject) { //If we have an object grabbed
            CGFloat pinchScaleX = recognizer.scale * scaledObject.scale.x;
            CGFloat pinchScaleY = recognizer.scale * scaledObject.scale.y;
            CGFloat pinchScaleZ = recognizer.scale * scaledObject.scale.z;
            [scaledObject setScale:SCNVector3Make(pinchScaleX, pinchScaleY, pinchScaleZ)];
        }
        recognizer.scale = 1; //Reset the scale, skipping this line causes really weird behavior
    }
    if (recognizer.state == UIGestureRecognizerStateEnded) {
        NSLog(@"Done pinching");
        scaledObject = nil; //Make sure that no object is set to scaledObject
    }
}

希望这有所帮助。

0

Swift 5 更新:

@objc func handlePinch(_ gesture: UIPinchGestureRecognizer) {
    if (gesture.state == .changed) {
        yourSKNode.xScale *= gesture.scale
        yourSKNode.yScale *= gesture.scale
        gesture.scale = 1
    }
}

还有我的didMove函数:

override func didMove(to view: SKView) {
    super.didMove(to: view)
    setupNodes()

    self.view!.addGestureRecognizer(UIPinchGestureRecognizer(target: self, action: #selector(handlePinch(_:))))
}

这里的didMove方法是什么? - Saurabh
你需要使用didMove方法来注册手势,以便每次用户捏屏幕时都会调用该方法。 - NicoC
1
在你的例子中使用的是SpriteKit而不是SceneKit。请删除此回答。 - Alisher

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接