How to rotate a SCNSphere/Node through a pan gesture
I figured it out, It was working the whole time, its just that the lighting made appear like nothing moved. Instead, I needed to rotate the light.
SceneKit - Adding drift to a sphere rotated via a pan gesture
The way I've handled things like this before is to split the gesture handler into sections for the gesture state beginning, changing and ending. If the rotation is working for you all you need to do is add code to the bottom of your if (sender.state == .Ended)
statement.
After lastHeightRatio = heightRatio
you can find the velocity of the pan in the view using sender.velocityInView(sender.view!)
and then find the horizontal component as before, then add an SCNAction
to rotate the node with an EaseOut
timing mode.
You'll likely need a multiplier to translate the velocity in the view (measured in points) to the angle you wish to rotate through (measured in radians). A velocity of 10 points, which is relatively small, would result in a very fast rotation.
Also you'll need to remove all actions from the node when the gesture begins again if you want a touch to stop the residual rotation.
Some sampel code would be:
if (sender.state == .Ended) {
lastWidthRatio = widthRatio
lastHeightRatio = heightRatio
// Find velocity
let velocity = sender.velocityInView(sender.view!)
// Create Action for whichever axis is the correct one to rotate about.
// The 0.00001 is just a factor to provide the rotation speed to pan
// speed ratio you'd like. Play with this number and the duration to get the result you want
let rotate = SCNAction.rotateByX(velocity.x * 0.00001, y: 0, z: 0, duration: 2.0)
// Run the action on the camera orbit node (if I understand your setup correctly)
cameraOrbit.runAction(rotate)
}
This should be enough to get you started.
How to use a pan gesture to rotate a camera in SceneKit using quaternions
I was able to get this working using quaternions. The full code is here: ThreeSixtyPlayer. A sample is here:
let orientation = cameraNode.orientation
// Use the pan translation along the x axis to adjust the camera's rotation about the y axis (side to side navigation).
let yScalar = Float(translationDelta.x / translationBounds.size.width)
let yRadians = yScalar * maxRotation
// Use the pan translation along the y axis to adjust the camera's rotation about the x axis (up and down navigation).
let xScalar = Float(translationDelta.y / translationBounds.size.height)
let xRadians = xScalar * maxRotation
// Represent the orientation as a GLKQuaternion
var glQuaternion = GLKQuaternionMake(orientation.x, orientation.y, orientation.z, orientation.w)
// Perform up and down rotations around *CAMERA* X axis (note the order of multiplication)
let xMultiplier = GLKQuaternionMakeWithAngleAndAxis(xRadians, 1, 0, 0)
glQuaternion = GLKQuaternionMultiply(glQuaternion, xMultiplier)
// Perform side to side rotations around *WORLD* Y axis (note the order of multiplication, different from above)
let yMultiplier = GLKQuaternionMakeWithAngleAndAxis(yRadians, 0, 1, 0)
glQuaternion = GLKQuaternionMultiply(yMultiplier, glQuaternion)
cameraNode.orientation = SCNQuaternion(x: glQuaternion.x, y: glQuaternion.y, z: glQuaternion.z, w: glQuaternion.w)
swift SceneKit decline node move
You can put similar code into your UIViewController:
//**************************************************************************
// Gesture Recognizers
// MARK: Gesture Recognizers
//**************************************************************************
@objc func handleTap(recognizer: UITapGestureRecognizer)
{
if(data.isNavigationOff == true) { return } // No panel select if Add, Update, EndWave, or EndGame
if(gameMenuTableView.isHidden == false) { return } // No panel if game menu is showing
let location: CGPoint = recognizer.location(in: gameScene)
if(data.isAirStrikeModeOn == true)
{
let projectedPoint = gameScene.projectPoint(SCNVector3(0, 0, 0))
let scenePoint = gameScene.unprojectPoint(SCNVector3(location.x, location.y, CGFloat(projectedPoint.z)))
gameControl.airStrike(position: scenePoint)
}
else
{
let hitResults = gameScene.hitTest(location, options: hitTestOptions)
for vHit in hitResults
{
if(vHit.node.name?.prefix(5) == "Panel")
{
// May have selected an invalid panel or auto upgrade was on
if(gameControl.selectPanel(vPanel: vHit.node.name!) == false) { return }
return
}
}
}
}
//**************************************************************************
@objc func handlePan(recognizer: UIPanGestureRecognizer)
{
if(data.gameState != .run || data.isGamePaused == true) { return }
currentLocation = recognizer.location(in: gameScene)
switch recognizer.state
{
case UIGestureRecognizer.State.began:
beginLocation = recognizer.location(in: gameScene)
break
case UIGestureRecognizer.State.changed:
if(currentLocation.x > beginLocation.x * 1.1)
{
beginLocation.x = currentLocation.x
gNodes.camera.strafeLeft()
}
if(currentLocation.x < beginLocation.x * 0.9)
{
beginLocation.x = currentLocation.x
gNodes.camera.strafeRight()
}
break
case UIGestureRecognizer.State.ended:
break
default:
break
}
}
Getting location of tap on SCNSphere - Swift (SceneKit) / iOS
In the hitResult, you can get result.textureCoordinates which tells you the point in your map textures. From this point, you are supposed to know the location of your map as the map should have coordinations which was mapped to textures.
@objc func handleTap(_ gestureRecognize: UIGestureRecognizer) {
// retrieve the SCNView
let sceneView = self.view as! SCNView
// check what nodes are tapped
let p = gestureRecognize.location(in: scnView)
let hitResults = sceneView.hitTest(p, options: [:])
// check that we clicked on at least one object
if hitResults.count > 0 {
// retrieved the first clicked object
let result: SCNHitTestResult = hitResults[0]
print(result.node.name!)
print(result.textureCoordinates(withMappingChannel 0)) // This line is added here.
print("x: \(p.x) y: \(p.y)") // <--- THIS IS WHERE I PRINT THE COORDINATES
}
}
Related Topics
Save Depth Images from Truedepth Camera
How to Send Msmessage in Messages Extension
How to Change Searchbar Border Color
How to Use Gadbannerviewdelegate to Perform Action on Adviewdidreceivead
How to Use a Custom Initializer on a Uitableviewcell
How to Implement a Payment App with Braintree in iOS
How to Know a Device Is Available to Use Capturetextfromcamera API
Swift Socket.Io .Emit() Doesn't Fire
Launch Watch App into Middle View
Self Sizing Uitextview Till Specific Height
iOS Swift No Such File or Directory in Debug-Iphoneos
How to Pass in a Void Block to Objc_Setassociatedobject in Swift
How to Rotate an Uiimageview Using Touchesmoved
Two Uibezierpaths Intersection as a Uibezierpath
Transit Mkdirectionsrequest Produces Null Error Error Domain=Mkerrordomain Code=5 "(Null)"