SceneKit SCNNode 跟随触摸移动
SceneKit SCNNode follow touch movement
我试图让球体节点仅在 x 轴上跟随我的手指在屏幕上的移动,而不影响球体的 y 或 z 值。我不确定我是否应该对物理体施加力或使用 SCNAction 移动它。球体将在占据屏幕宽度的立方体节点上弹跳。到目前为止,我最大的问题是弄清楚在 pangesture 函数中放入什么。这是我要复制的示例,其中用户在屏幕上滑动手指,球会反映其位置。
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for t in touches {
let touchLocation = t.location(in: self.view)
let touchCoordinatesX = (touchLocation.x - self.scnView.frame.width / 2)
ballNode.position.x = Float(touchCoordinatesX/75)
}
}
上面的代码有些工作,当我在屏幕上拖动我的手指时,y 位置会出现异常,并且上下滞后非常快,即使我的代码从未编辑球的 y 值。每次我在屏幕上拖动时,球的 y 位置都会重置为其添加到场景根节点时的原始值。
根据您的回答,这就是我所使用的并且完全符合我的需要。
@objc func handlePan(recognizer: UIPanGestureRecognizer)
{
currentLocation = recognizer.location(in: self.view)
let touchCoordinatesX = (currentLocation.x - self.scnView.frame.width / 2)
if recognizer.state == .changed
{
ballNode.position.x = Float(touchCoordinatesX/75)
ballNode.position.y = ballNode.presentation.position.y
}
}
尝试 handlePan 和 setting/saving dragMode 状态变量。下面的代码是我用来 select 面板和扫视屏幕的东西,所以这些东西有一些额外的代码,但我认为你可以修改它以满足你的需要。在你的情况下,我的 "strafing the screen" 应该类似于你需要将球拖向你的指尖。
如果您在 dragChanges() 中将球对象设置为 currentLocation.x,那么它应该在您的指尖上准确对齐,并且响应应该会很快。根据您的图片,我的猜测是您需要稍微延迟一下,这样更逼真。如果是这种情况,我建议添加一个计时器并处理一个保存的 X 位置的小队列,以便它以小的增量到达您的指尖。
希望对您有所帮助。
//**************************************************************************
@objc func handlePan(recognizer: UIPanGestureRecognizer)
{
currentLocation = recognizer.location(in: gameScene)
let getLocation: CGPoint = recognizer.location(in: gameScene)
let panHitTestResults = gameScene.hitTest(getLocation, options: hitTestOptions)
if(data.gameState == .endWave || data.gameState == .endGame ||
data.gameState == .defenseAdd || data.gameState == .defenseUpgrade)
{
dragMode = .none
selectorActive = false
lastPanelSelected = ""
return
}
// State Begins
if recognizer.state == UIGestureRecognizerState.began
{
dragMode = .drag
beginLocation = recognizer.location(in: gameScene)
data.panX = currentLocation.x
data.lastMouseX = Float(beginLocation.x)
data.lastMouseY = Float(beginLocation.y)
dragBegins(vRecognizer: recognizer)
selectorActive = false
}
// State Changes
if(recognizer.state == UIGestureRecognizerState.changed)
{
dragChanges(vRecognizer: recognizer)
if(data.gameState == .run && dragMode == .drag)
{
// If we were selecting cursor and want to cancel, just touch with 2 fingers
if(recognizer.numberOfTouches == 2)
{
dragMode = .none
selectorActive = false
grid.cancelCursor()
return
}
for vHit in panHitTestResults
{
if(vHit.node.name?.prefix(5) == "Panel")
{
selectorActive = true
lastPanelSelected = vHit.node.name!
data.panelSelected = lastPanelSelected
let _ = grid.selectCursor()
return
}
}
}
}
if(recognizer.state == UIGestureRecognizerState.ended)
{
dragEnds(vRecognizer: recognizer)
dragMode = .none
if(selectorActive == true)
{
gameControl.selectPanel(vPanel: lastPanelSelected)
lastPanelSelected = ""
selectorActive = false
return
}
}
}
//**************************************************************************
func dragBegins(vRecognizer: UIPanGestureRecognizer)
{
if(data.gameState == .run)
{
if(vRecognizer.numberOfTouches == 2) { dragMode = .strafe }
}
}
//**************************************************************************
func dragChanges(vRecognizer: UIPanGestureRecognizer)
{
if(data.gameState == .run)
{
if(dragMode == .strafe && vRecognizer.numberOfTouches == 1)
{
dragMode = .none
return
}
switch(dragMode)
{
case .strafe:
gNodes.camera.strafe(vX: Float(currentLocation.x), vY: Float(currentLocation.y))
break
case .none:
break
default:
break
}
}
}
//**************************************************************************
func dragEnds(vRecognizer: UIPanGestureRecognizer)
{
if(data.gameState == .run)
{
switch(dragMode)
{
case .strafe:
break
default:
break
}
}
dragMode = .none
}
我试图让球体节点仅在 x 轴上跟随我的手指在屏幕上的移动,而不影响球体的 y 或 z 值。我不确定我是否应该对物理体施加力或使用 SCNAction 移动它。球体将在占据屏幕宽度的立方体节点上弹跳。到目前为止,我最大的问题是弄清楚在 pangesture 函数中放入什么。这是我要复制的示例,其中用户在屏幕上滑动手指,球会反映其位置。
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for t in touches {
let touchLocation = t.location(in: self.view)
let touchCoordinatesX = (touchLocation.x - self.scnView.frame.width / 2)
ballNode.position.x = Float(touchCoordinatesX/75)
}
}
上面的代码有些工作,当我在屏幕上拖动我的手指时,y 位置会出现异常,并且上下滞后非常快,即使我的代码从未编辑球的 y 值。每次我在屏幕上拖动时,球的 y 位置都会重置为其添加到场景根节点时的原始值。
根据您的回答,这就是我所使用的并且完全符合我的需要。
@objc func handlePan(recognizer: UIPanGestureRecognizer)
{
currentLocation = recognizer.location(in: self.view)
let touchCoordinatesX = (currentLocation.x - self.scnView.frame.width / 2)
if recognizer.state == .changed
{
ballNode.position.x = Float(touchCoordinatesX/75)
ballNode.position.y = ballNode.presentation.position.y
}
}
尝试 handlePan 和 setting/saving dragMode 状态变量。下面的代码是我用来 select 面板和扫视屏幕的东西,所以这些东西有一些额外的代码,但我认为你可以修改它以满足你的需要。在你的情况下,我的 "strafing the screen" 应该类似于你需要将球拖向你的指尖。
如果您在 dragChanges() 中将球对象设置为 currentLocation.x,那么它应该在您的指尖上准确对齐,并且响应应该会很快。根据您的图片,我的猜测是您需要稍微延迟一下,这样更逼真。如果是这种情况,我建议添加一个计时器并处理一个保存的 X 位置的小队列,以便它以小的增量到达您的指尖。
希望对您有所帮助。
//**************************************************************************
@objc func handlePan(recognizer: UIPanGestureRecognizer)
{
currentLocation = recognizer.location(in: gameScene)
let getLocation: CGPoint = recognizer.location(in: gameScene)
let panHitTestResults = gameScene.hitTest(getLocation, options: hitTestOptions)
if(data.gameState == .endWave || data.gameState == .endGame ||
data.gameState == .defenseAdd || data.gameState == .defenseUpgrade)
{
dragMode = .none
selectorActive = false
lastPanelSelected = ""
return
}
// State Begins
if recognizer.state == UIGestureRecognizerState.began
{
dragMode = .drag
beginLocation = recognizer.location(in: gameScene)
data.panX = currentLocation.x
data.lastMouseX = Float(beginLocation.x)
data.lastMouseY = Float(beginLocation.y)
dragBegins(vRecognizer: recognizer)
selectorActive = false
}
// State Changes
if(recognizer.state == UIGestureRecognizerState.changed)
{
dragChanges(vRecognizer: recognizer)
if(data.gameState == .run && dragMode == .drag)
{
// If we were selecting cursor and want to cancel, just touch with 2 fingers
if(recognizer.numberOfTouches == 2)
{
dragMode = .none
selectorActive = false
grid.cancelCursor()
return
}
for vHit in panHitTestResults
{
if(vHit.node.name?.prefix(5) == "Panel")
{
selectorActive = true
lastPanelSelected = vHit.node.name!
data.panelSelected = lastPanelSelected
let _ = grid.selectCursor()
return
}
}
}
}
if(recognizer.state == UIGestureRecognizerState.ended)
{
dragEnds(vRecognizer: recognizer)
dragMode = .none
if(selectorActive == true)
{
gameControl.selectPanel(vPanel: lastPanelSelected)
lastPanelSelected = ""
selectorActive = false
return
}
}
}
//**************************************************************************
func dragBegins(vRecognizer: UIPanGestureRecognizer)
{
if(data.gameState == .run)
{
if(vRecognizer.numberOfTouches == 2) { dragMode = .strafe }
}
}
//**************************************************************************
func dragChanges(vRecognizer: UIPanGestureRecognizer)
{
if(data.gameState == .run)
{
if(dragMode == .strafe && vRecognizer.numberOfTouches == 1)
{
dragMode = .none
return
}
switch(dragMode)
{
case .strafe:
gNodes.camera.strafe(vX: Float(currentLocation.x), vY: Float(currentLocation.y))
break
case .none:
break
default:
break
}
}
}
//**************************************************************************
func dragEnds(vRecognizer: UIPanGestureRecognizer)
{
if(data.gameState == .run)
{
switch(dragMode)
{
case .strafe:
break
default:
break
}
}
dragMode = .none
}