[코드조각] [3] 업로드한 오브젝트에 제스쳐 적용하기(오브젝트 이동, 스케일링, 히든, 애니메이션)

웰디(Well-D)·2025년 4월 14일
0

제스쳐를 인식하여 오브젝트에 적용하는 코드조각

제스쳐 recognizer

 func addGestureRecognizers() {        
 // 탭 제스처 추가        
 let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTap(gesture:)))        
 tapGestureRecognizer.delegate = self        
 sceneView.addGestureRecognizer(tapGestureRecognizer)        
 // 팬 제스처 추가 (드래그)        
 let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:)))        
 sceneView.addGestureRecognizer(panGestureRecognizer)        
 // 핀치 제스처 추가        
 let pinchGestureRecognizer = UIPinchGestureRecognizer(target: self, action: #selector(handlePinch(_:)))        
 sceneView.addGestureRecognizer(pinchGestureRecognizer)        
 // 롱프레스 제스처 추가        
 let longPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPress(_:)))        
 // 최소  2초동안은 프레싱되어야 제스쳐를 인식합니다        
 longPressGestureRecognizer.minimumPressDuration = 2        
 // 2초 안에 들어오는 제스쳐는 삭제합니다(버리기)        
 longPressGestureRecognizer.delaysTouchesBegan = true        
 sceneView.addGestureRecognizer(longPressGestureRecognizer)    
 }

제스쳐 함수 (오브젝트를 이동)

 @objc func handlePan(_ gesture: UIPanGestureRecognizer) {
    //  gesture.location(in: sceneView)는 제스처 이벤트가 발생한 터치 위치를 sceneView 좌표계에서 반환
    let location = gesture.location(in: sceneView)
    switch gesture.state {
    case .began:
        // 제스처의 상태를 확인합니다. 여기서는 제스처가 시작될 때(.began)만 코드를 실행합니다.
        let hitResults = sceneView.hitTest(location, options: nil)
        // sceneView.hitTest(location, options: nil)는 터치 위치(location)에서 히트 테스트를 수행하여 해당 위치에 있는 노드들을 반환(히트 테스트는 터치된 지점에 어떤 노드들이 있는지 확인하는 과정임)
        if let hitResult = hitResults.first {                
        selectedNode = hitResult.node
            //히트 테스트 결과에서 첫 번째 노드를 selectedNode로 선택합니다. hitResults.first는 히트된 노드들 중 첫 번째 노드를 반환합니다.
            while let parent = selectedNode?.parent, parent !== sceneView.scene.rootNode {                    
            selectedNode = parent
        }
            /* 
             - 선택된 노드의 최상위 부모 노드를 찾기 위해 `while` 루프를 사용합니다.                - `selectedNode?.parent`가 `nil`이 아니고, `selectedNode`의 부모가 `sceneView.scene.rootNode`가 아닐 때까지 루프를 계속 실행합니다.                - `selectedNode`를 계속 부모 노드로 갱신하여 최상위 노드에 도달할 때까지 반복합니다.                
              */
            originalNodePosition = selectedNode ? .position
        }
    case.changed: if
        let selectedNode = selectedNode,
        let originalNodePosition = originalNodePosition {
        // transition은 제스처 인식기에서 현재 팬 동작의 이동 거리를 가져옵니다. 이 거리는 사용자가 화면에서 손가락을 얼마나 이동했는지를 나타냅니다.
        let translation = gesture.translation(in: sceneView)                
        let newPosition = SCNVector3(                    
        x: originalNodePosition.x + Float(translation.x * 0.05),                    y: originalNodePosition.y + Float(translation.y * -0.05),                    z: originalNodePosition.z + Float(translation.y * -0.05)                
        )
        /*    
        - `translation` 값을 이용하여 `newPosition`을 계산합니다.                 - `originalNodePosition.x`에 `translation.x`을 더하여 노드의 새로운 x 위치를 계산합니다. `translation.x`에 0.001을 곱한 것은 화면상의 이동 거리를 3D 공간상의 이동 거리로 변환하기 위함입니다.                 - `originalNodePosition.y`는 그대로 유지됩니다. 이는 노드의 높이(y 축)를 변경하지 않음을 의미합니다.                 - `originalNodePosition.z`에 `translation.y`을 더하여 노드의 새로운 z 위치를 계산합니다. 마찬가지로 `translation.y`에 0.001을 곱한 것은 화면상의 이동 거리를 3D 공간상의 이동 거리로 변환하기 위함입니다.                 
        */
        selectedNode.position = newPosition
        // addAnimation(node: selectedNode)
        // addMoveUpDownAnimation(node: selectedNode)
    }
    case .ended, .cancelled:
        selectedNode = nil
        originalNodePosition = nil
    default:
        break
    }
}

제스쳐 함수 (오브젝트 스케일링)

@objc func handlePinch(_ gesture: UIPinchGestureRecognizer) {        
let location = gesture.location(in: (gesture.view as! ARSCNView))        
switch gesture.state {        
	case .began:            
	//  let hitResults = sceneView.hitTest(location, options: nil)            
		let hitTest = (gesture.view as! ARSCNView).hitTest(location)            
		if let hitTest = hitTest.first {                
		// 최상위 노드로 이동                
		selectedNode = hitTest.node
                while let parent = selectedNode?.parent, parent !== sceneView.scene.rootNode {  
                selectedNode = parent
             }                
                originalNodePosition = selectedNode?.position
            }        
            case .changed:            
	            if let selectedNode = selectedNode, let originalScale = originalScale {     
	            let scale = Float(gesture.scale)                
	            selectedNode.scale = SCNVector3(x: originalScale.x * scale, y: originalScale.y * scale, z: originalScale.z * scale)            
	            }        
	            case .ended, .cancelled:            
	            if let selectedNode = selectedNode 
	            {                
	            originalScale = selectedNode.scale
		           }        
            default:            
	            break        
 }    
}

제스쳐 함수 (오브젝트 hidden)

    @objc func handleTap(gesture: UITapGestureRecognizer) {        
    let location = gesture.location(in: sceneView)            
    let hitResults = sceneView.hitTest(location, options: nil)            
    if let hitResult = hitResults.first {                
    selectedNode = hitResult.node
                while let parent = selectedNode?.parent, parent !== sceneView.scene.rootNode {                    
                selectedNode = parent
                }                
                originalNodePosition = selectedNode?.position
                originalScale = selectedNode?.scale
                if let selectedNode = selectedNode {                    
                for i in 1...64 {                        
                let nodeName = String(format: "Feu%02d", i) 
                if let fireNode = selectedNode.childNode(withName: nodeName, recursively: true) {                            
                if fireNode.isHidden {                                
                fireNode.isHidden = false                                
                playSound(currentModelName)                            
                }                            
                else 
                {                                
                fireNode.isHidden = true                                
                pauseSound(currentModelName)                            
                }                        
               }                    
             }                    
             let candleNodeName = "Cone_3"                    
             if let candleNode = selectedNode.childNode(withName: candleNodeName, recursively: true) {                        
             if let candleInnerNode = candleNode.childNode(withName: "Object_4", recursively: true) {                            
             if candleInnerNode.isHidden 
             {                                
             candleInnerNode.isHidden = false                                
             playSound(currentModelName)                           
             }                            
            else {                                
            candleInnerNode.isHidden = true                                
            pauseSound(currentModelName)                            
            }                        
           }                    
          }                    
          let starOrbNodeName = "inner_0"                    
          if let starOrbInnerNode = selectedNode.childNode(withName: starOrbNodeName, recursively: true) {                        
          if let starOrbCenterNode = starOrbInnerNode.childNode(withName: "Object_4", recursively: true) {                            
          if let starOrbNode = starOrbCenterNode.childNode(withName: "Object_0", recursively: true) {                                
          if starOrbNode.isHidden {                                    
          starOrbNode.isHidden = false                                    
          playSound(currentModelName)                                
          }                                
          else {                                    
          starOrbNode.isHidden = true                                    
          pauseSound(currentModelName)                                
          }                            
         }                        
        }                    
       }               
      }            
	   }    
	  }

제스쳐 함수 (오브젝트 애니메이션)

    @objc func handleLongPress(_ gesture: UILongPressGestureRecognizer) {        
    let location = gesture.location(in: sceneView)        
    switch gesture.state {        
    case .began:            
    let hitResults = sceneView.hitTest(location, options: nil)            
    if let hitResult = hitResults.first {                
    selectedNode = hitResult.node
                while let parent = selectedNode?.parent, parent !== sceneView.scene.rootNode {                    
                selectedNode = parent
        }                
                originalNodePosition = selectedNode?.position
                originalScale = selectedNode?.scale
                if let selectedNode = selectedNode {                    
                for i in 1...64 {                        
                let nodeName = String(format: "Feu%02d", i)            
                if let fireNode = selectedNode.childNode(withName: nodeName, recursively: true) {                            
                let scaleZAction = SCNAction.scaleZ(to: 5, duration: 5.0)                            adjustPivot(node: fireNode)                            
                fireNode.runAction(scaleZAction)                        
                }                    
                }                    
                let candleNodeName = "Cone_3"                    
                if let candleNode = selectedNode.childNode(withName: candleNodeName, recursively: true) {
                if let candleInnerNode = candleNode.childNode(withName: "Object_4", recursively: true) {                            
                let scaleYAction = SCNAction.scaleY(to: 5, duration: 5.0)                            adjustPivot(node: candleInnerNode)                            candleInnerNode.runAction(scaleYAction)                            
                if candleInnerNode.action(forKey: "scaleY") != nil {             
                let scaleYAction = SCNAction.scaleY(to: 1, duration: 5.0)
                //adjustPivot(node: candleInnerNode)                                candleInnerNode.runAction(scaleYAction)                            
                }                        
                }                    
                }                    
                let starOrbNodeName = "inner_0"                    
                if let starOrbInnerNode = selectedNode.childNode(withName: starOrbNodeName, recursively: true) {                        
                if let starOrbCenterNode = starOrbInnerNode.childNode(withName: "Object_4", recursively: true) {                            
                if let starOrbNode = starOrbCenterNode.childNode(withName: "Object_0", recursively: true) {                                
                let scaleAction = SCNAction.scale(to: 5.0, duration: 2.5)                        starOrbNode.runAction(scaleAction)       
                if starOrbNode.action(forKey: "scale") != nil { 
                let scaleAction = SCNAction.scale(to: 1.0, duration: 2.5)  
                starOrbNode.runAction(scaleAction)  
                }                            
	            }                        
						}                    
					}                
				}            
			}        
			case .ended, .cancelled:            
			let hitResults = sceneView.hitTest(location, options: nil)            
			if let hitResult = hitResults.first {                
			selectedNode = hitResult.node
                while let parent = selectedNode?.parent, parent !== sceneView.scene.rootNode {                    
                selectedNode = parent
             }                
                originalNodePosition = selectedNode?.position
                originalScale = selectedNode?.scale
                if let selectedNode = selectedNode {                    
                for i in 1...64 {                        
                let nodeName = String(format: "Feu%02d", i)     
                if let fireNode = selectedNode.childNode(withName: nodeName, recursively: true) {                           
                if fireNode.action(forKey: "scaleZ") != nil {  
                let scaleZAction = SCNAction.scaleZ(to: 1, duration: 5.0)                        adjustPivot(node: fireNode)                                
                fireNode.runAction(scaleZAction)                            
                }                            
                fireNode.removeAllActions()                        
              }                    
            }                    
            let candleNodeName = "Cone_3"                    
            if let candleNode = selectedNode.childNode(withName: candleNodeName, recursively: true) {                        
            if let candleInnerNode = candleNode.childNode(withName: "Object_4", recursively: true) {                            
            candleInnerNode.removeAllActions()                        
            }                    
          }                    
          let starOrbNodeName = "inner_0"                    
          if let starOrbInnerNode = selectedNode.childNode(withName: starOrbNodeName, recursively: true) {                        
          if let starOrbCenterNode = starOrbInnerNode.childNode(withName: "Object_4", recursively: true) {                            
          if let starOrbNode = starOrbCenterNode.childNode(withName: "Object_0", recursively: true) {                                
          starOrbNode.removeAllActions()                            
          }                        
        }                    
      }                
    }            
  }            
  selectedNode = nil            
  originalNodePosition = nil            
  originalScale = nil            
  startY = nil            
  originalZScale = nil        
  default:            
	  break        
   }    
  }
profile
Wellness 잘사는 것에 진심인 웰디입니다. 여러분의 몸과 마음, 통장의 건강을 수호하고싶어요. 느리더라도, 꾸준히

0개의 댓글