I am trying to scale and SCNNode in real time using the Pinch gesture:
This is my current code
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)
}
}
}
However the node doesn't scale big or small? Can someone please point my mistake?
The SCNNode is loaded and has an animation on applied like so,
sceneView.scene.rootNode.addChildNode(node)
loadAnimation(animation: .Attack, sceneName: "art.scnassets/attack", animationIdentifier: "attackID");
Got it to work in 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
}
}
}
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
}
}
I used this to handle the scale:
@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))
}))
}
I've found that this works pretty well. I also use both touches to try and detect if I'm pinching on an object or not, so you can grab the SCNNode with either finger.
Note, the line that resets the scale is necessary, otherwise it acts really buggy. I can't particularly say why that it is the case though.
-(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
}
}
Hope this helps
Update for Swift 5 :
@objc func handlePinch(_ gesture: UIPinchGestureRecognizer) {
if (gesture.state == .changed) {
yourSKNode.xScale *= gesture.scale
yourSKNode.yScale *= gesture.scale
gesture.scale = 1
}
}
and my didMove
function :
override func didMove(to view: SKView) {
super.didMove(to: view)
setupNodes()
self.view!.addGestureRecognizer(UIPinchGestureRecognizer(target: self, action: #selector(handlePinch(_:))))
}