swift SceneKit 拒绝节点移动
swift SceneKit decline node move
我创建了一个球体节点,我需要用户只能旋转(左/右,上/下)和放大/缩小节点,但默认情况下他可以从中心移动节点(使用两个手指) - 是否可以禁止用户将节点从中心移开?感谢您的帮助
sceneView.scene = scene
cameraOrbit = SCNNode()
cameraNode = SCNNode()
camera = SCNCamera()
// camera stuff
camera.usesOrthographicProjection = true
camera.orthographicScale = 5
camera.zNear = 1
camera.zFar = 100
cameraNode.position = SCNVector3(x: 0, y: 0, z: 70)
cameraNode.camera = camera
cameraOrbit = SCNNode()
cameraOrbit.addChildNode(cameraNode)
scene.rootNode.addChildNode(cameraNode)
let sphere = SCNSphere(radius: 2)
sphere.firstMaterial?.diffuse.contents = UIColor.red
let earthNode = SCNNode(geometry: sphere)
earthNode.name = "sphere"
earthNode.geometry?.materials = [blueMaterial]
scene.rootNode.addChildNode(earthNode)
earthNode.rotation = SCNVector4(0, 1, 0, 0)
let lightNode = SCNNode()
let light = SCNLight()
light.type = .ambient
light.intensity = 200
lightNode.light = light
scene.rootNode.addChildNode(lightNode)
sceneView.allowsCameraControl = true
sceneView.backgroundColor = UIColor.clear
sceneView.cameraControlConfiguration.allowsTranslation = true
sceneView.cameraControlConfiguration.rotationSensitivity = 0.4
是的,所有这些都是可行的。首先,创建您自己的相机 class 并关闭 allowsCameraControl。然后就可以实现zoom/strafe/whatever.
这里有一些可能有用的示例,只需在堆栈搜索栏中搜索这些数字并找到我的 answers/examples。
57018359 - 这个 post 告诉您如何触摸 2d 屏幕(点击)并将其转换为 3d 坐标,同时您决定深度 (z),就像您想要点击屏幕并放置一样3d 中的对象 space.
57003908 - 此 post 告诉您如何 select 使用 hitTest(点击)的对象。例如,如果您显示带有门的房屋正面并点击它,那么该函数将 return 您的门节点,前提是您将节点命名为“门”并在它被触摸时采取某种动作。然后你可以根据那个位置重新定位你的相机。您需要遍历所有结果,因为可能存在重叠或加 Z 节点
55129224 - 此 post 为您提供了创建相机的快速示例 class。您可以使用它来重新定位您的相机或前后移动它等。
双指拖动:
func dragBegins(vRecognizer: UIPanGestureRecognizer)
{
if(data.gameState == .run)
{
if(vRecognizer.numberOfTouches == 2) { dragMode = .strafe }
}
}
class Camera
{
var data = Data.sharedInstance
var util = Util.sharedInstance
var gameDefaults = Defaults()
var cameraEye = SCNNode()
var cameraFocus = SCNNode()
var centerX: Int = 100
var strafeDelta: Float = 0.8
var zoomLevel: Int = 35
var zoomLevelMax: Int = 35 // Max number of zoom levels
//********************************************************************
init()
{
cameraEye.name = "Camera Eye"
cameraFocus.name = "Camera Focus"
cameraFocus.isHidden = true
cameraFocus.position = SCNVector3(x: 0, y: 0, z: 0)
cameraEye.camera = SCNCamera()
cameraEye.constraints = []
cameraEye.position = SCNVector3(x: 0, y: 15, z: 0.1)
let vConstraint = SCNLookAtConstraint(target: cameraFocus)
vConstraint.isGimbalLockEnabled = true
cameraEye.constraints = [vConstraint]
}
//********************************************************************
func reset()
{
centerX = 100
cameraFocus.position = SCNVector3(x: 0, y: 0, z: 0)
cameraEye.constraints = []
cameraEye.position = SCNVector3(x: 0, y: 32, z: 0.1)
cameraFocus.position = SCNVector3Make(0, 0, 0)
let vConstraint = SCNLookAtConstraint(target: cameraFocus)
vConstraint.isGimbalLockEnabled = true
cameraEye.constraints = [vConstraint]
}
//********************************************************************
func strafeRight()
{
if(centerX + 1 < 112)
{
centerX += 1
cameraEye.position.x += strafeDelta
cameraFocus.position.x += strafeDelta
}
}
//********************************************************************
func strafeLeft()
{
if(centerX - 1 > 90)
{
centerX -= 1
cameraEye.position.x -= strafeDelta
cameraFocus.position.x -= strafeDelta
}
}
//********************************************************************
}
//********************************************************************
func lerp(start: SCNVector3, end: SCNVector3, percent: Float) -> SCNVector3
{
let v3 = cgVecSub(v1: end, v2: start)
let v4 = cgVecScalarMult(v: v3, s: percent)
return cgVecAdd(v1: start, v2: v4)
}
您可以将类似的代码放入您的 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
}
}
我创建了一个球体节点,我需要用户只能旋转(左/右,上/下)和放大/缩小节点,但默认情况下他可以从中心移动节点(使用两个手指) - 是否可以禁止用户将节点从中心移开?感谢您的帮助
sceneView.scene = scene
cameraOrbit = SCNNode()
cameraNode = SCNNode()
camera = SCNCamera()
// camera stuff
camera.usesOrthographicProjection = true
camera.orthographicScale = 5
camera.zNear = 1
camera.zFar = 100
cameraNode.position = SCNVector3(x: 0, y: 0, z: 70)
cameraNode.camera = camera
cameraOrbit = SCNNode()
cameraOrbit.addChildNode(cameraNode)
scene.rootNode.addChildNode(cameraNode)
let sphere = SCNSphere(radius: 2)
sphere.firstMaterial?.diffuse.contents = UIColor.red
let earthNode = SCNNode(geometry: sphere)
earthNode.name = "sphere"
earthNode.geometry?.materials = [blueMaterial]
scene.rootNode.addChildNode(earthNode)
earthNode.rotation = SCNVector4(0, 1, 0, 0)
let lightNode = SCNNode()
let light = SCNLight()
light.type = .ambient
light.intensity = 200
lightNode.light = light
scene.rootNode.addChildNode(lightNode)
sceneView.allowsCameraControl = true
sceneView.backgroundColor = UIColor.clear
sceneView.cameraControlConfiguration.allowsTranslation = true
sceneView.cameraControlConfiguration.rotationSensitivity = 0.4
是的,所有这些都是可行的。首先,创建您自己的相机 class 并关闭 allowsCameraControl。然后就可以实现zoom/strafe/whatever.
这里有一些可能有用的示例,只需在堆栈搜索栏中搜索这些数字并找到我的 answers/examples。
57018359 - 这个 post 告诉您如何触摸 2d 屏幕(点击)并将其转换为 3d 坐标,同时您决定深度 (z),就像您想要点击屏幕并放置一样3d 中的对象 space.
57003908 - 此 post 告诉您如何 select 使用 hitTest(点击)的对象。例如,如果您显示带有门的房屋正面并点击它,那么该函数将 return 您的门节点,前提是您将节点命名为“门”并在它被触摸时采取某种动作。然后你可以根据那个位置重新定位你的相机。您需要遍历所有结果,因为可能存在重叠或加 Z 节点
55129224 - 此 post 为您提供了创建相机的快速示例 class。您可以使用它来重新定位您的相机或前后移动它等。
双指拖动:
func dragBegins(vRecognizer: UIPanGestureRecognizer)
{
if(data.gameState == .run)
{
if(vRecognizer.numberOfTouches == 2) { dragMode = .strafe }
}
}
class Camera
{
var data = Data.sharedInstance
var util = Util.sharedInstance
var gameDefaults = Defaults()
var cameraEye = SCNNode()
var cameraFocus = SCNNode()
var centerX: Int = 100
var strafeDelta: Float = 0.8
var zoomLevel: Int = 35
var zoomLevelMax: Int = 35 // Max number of zoom levels
//********************************************************************
init()
{
cameraEye.name = "Camera Eye"
cameraFocus.name = "Camera Focus"
cameraFocus.isHidden = true
cameraFocus.position = SCNVector3(x: 0, y: 0, z: 0)
cameraEye.camera = SCNCamera()
cameraEye.constraints = []
cameraEye.position = SCNVector3(x: 0, y: 15, z: 0.1)
let vConstraint = SCNLookAtConstraint(target: cameraFocus)
vConstraint.isGimbalLockEnabled = true
cameraEye.constraints = [vConstraint]
}
//********************************************************************
func reset()
{
centerX = 100
cameraFocus.position = SCNVector3(x: 0, y: 0, z: 0)
cameraEye.constraints = []
cameraEye.position = SCNVector3(x: 0, y: 32, z: 0.1)
cameraFocus.position = SCNVector3Make(0, 0, 0)
let vConstraint = SCNLookAtConstraint(target: cameraFocus)
vConstraint.isGimbalLockEnabled = true
cameraEye.constraints = [vConstraint]
}
//********************************************************************
func strafeRight()
{
if(centerX + 1 < 112)
{
centerX += 1
cameraEye.position.x += strafeDelta
cameraFocus.position.x += strafeDelta
}
}
//********************************************************************
func strafeLeft()
{
if(centerX - 1 > 90)
{
centerX -= 1
cameraEye.position.x -= strafeDelta
cameraFocus.position.x -= strafeDelta
}
}
//********************************************************************
}
//********************************************************************
func lerp(start: SCNVector3, end: SCNVector3, percent: Float) -> SCNVector3
{
let v3 = cgVecSub(v1: end, v2: start)
let v4 = cgVecScalarMult(v: v3, s: percent)
return cgVecAdd(v1: start, v2: v4)
}
您可以将类似的代码放入您的 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
}
}