gpt4 book ai didi

swift - 如何根据用户手势在 SceneKit 中正确实现 throw 弹丸?

转载 作者:行者123 更新时间:2023-11-28 07:57:59 54 4
gpt4 key购买 nike

简介

我正在使用 ARKit 在增强现实中设计一款 iPhone 游戏。我的游戏的一部分涉及用户滑动以将球扔向目标。我的目的是创建一个向量,其中包含目标的 y 值和 z 值,但包含用户滑动位置的 x 值。然而,在测试这个系统时,球总是向左移动。我认为这是我使用的轴的问题,但我无法理解如何更改它。目标可以是 AR 世界中飞机上的任何地方。

图像中提供了描述。用户从球上滑动,球应向目标的 y 和 z 坐标以及滑动结束位置的 x 值移动。

enter image description here

当前代码

我使用的大部分代码都是基于以下 Tutorial on RayWenderlich.com .

我首先实现 touchesBegan 和 touchesEnded 函数,如下所示,以计算滑动的终点和滑动的速度:

var startTouch: UITouch?
var endTouch: UITouch?
var startTouchTime: TimeInterval!
var endTouchTime: TimeInterval!

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {

if self.currentMode == .throwingProjectiles {

guard let firstTouch = touches.first else { return }
let point = firstTouch.location(in: sceneView)
let hitResults = sceneView.hitTest(point, options: [:])

if hitResults.first?.node == projectileIndicator {
startTouch = touches.first
startTouchTime = Date().timeIntervalSince1970
}
}
}

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {

guard startTouch != nil else { return }

endTouch = touches.first
endTouchTime = Date().timeIntervalSince1970
throwBall()
}

然后我按如下方式实现 throwBall() 函数:

func throwBall() {

guard let endingTouch = endTouch else { return }

let firstTouchResult = sceneView.hitTest(
endingTouch.location(in: sceneView),
options: nil
).filter({
$0.node == targetNode || $0.node == floorNode
}).first


guard let touchResult = firstTouchResult else {return}

let vp = endingTouch.location(in: sceneView)
let vpWithDepth = SCNVector3Make(Float(vp.x), Float(vp.y), 0)
let scenePoint = sceneView.unprojectPoint(vpWithDepth)

let timeDifference = ( endTouchTime - startTouchTime)/4
let velocityComponent = Float(min(max(1 - timeDifference, 0.1), 10.0))

let impulseVector = SCNVector3(
x: scenePoint.x,
y: targetNode.worldPosition.y + 1.005 * velocityComponent * 5,
z: targetNode.worldPosition.z * velocityComponent * 10
)
spawnProjectile()
currentBallNode?.name = "Ball"
balls.append(currentBallNode!)
currentBallNode?.physicsBody?.applyForce(impulseVector, asImpulse: true)

currentBallNode = nil
startTouchTime = nil
endTouchTime = nil
startTouch = nil
endTouch = nil
}

取消项目点代码来自这里:https://stackoverflow.com/a/25154025/6642130

所以我的问题是,我如何促进这种 throw ?

最佳答案

我修复了滑动,这看起来很完美。当我发现如何完美地做到这一点时,我改变了答案。我使用一个 PanGestureRecognizer,它被拖到我的 ARview sceneView 中。

@objc func handlePan(recognizer : UIPanGestureRecognizer) {
guard let food = foodToShot else {return}
//If removes the finger or something
if recognizer.state == .cancelled {
food.removeFromParentNode()
//If it actually shoots
} else if recognizer.state == .ended {
//I do something only when direction makes sense - MARK: MAYBE STUDY THIS
switch recognizer.direction {
case .bottomToTop :
//Takes swipe velocity in CGFloat/seconds
let velocity = recognizer.velocity(in: sceneView)
//Calculate V
let magnitude = sqrt((velocity.x * velocity.x) + (velocity.y * velocity.y))
//Adjustements to have a decent not too fast velocity
let slideMultiplier = magnitude / 200
let slideFactor = 0.1 * slideMultiplier
//I save current frame
let currentFrame = self.sceneView.session.currentFrame!
//Creation of physicalBody
let foodPhysicsBody = SCNPhysicsBody(
type: .dynamic,
shape: SCNPhysicsShape(geometry: SCNSphere(radius: 0.04))
)
foodPhysicsBody.isAffectedByGravity = true
//Collisions
foodPhysicsBody.categoryBitMask = CollisionCategory.food.rawValue
foodPhysicsBody.categoryBitMask = CollisionCategory.floor.rawValue
foodPhysicsBody.contactTestBitMask = CollisionCategory.food.rawValue
foodPhysicsBody.mass = 3
foodPhysicsBody.restitution = 0.75
foodPhysicsBody.damping = 0.1
foodPhysicsBody.friction = 2
food.physicsBody = foodPhysicsBody
//Calculate with my V and Vx and my acot modified function
let closeTranslation = simd_make_float4(0,Float(acotForSwipes(x: Double(magnitude / velocity.x))),-1.2,0)
let rotatedForce = simd_mul(currentFrame.camera.transform, closeTranslation)
//Calculate force
let force = SCNVector3Make(rotatedForce.x, rotatedForce.y, rotatedForce.z)
//I give force to physicBody
food.physicsBody?.applyForce(force * SCNFloat(slideFactor) * 15, asImpulse: true)
//I don't care about other swipes
case .leftToRight:
return
case .rightToLeft:
return
case .topToBottom:
return
case .undefined:
return
}
}
}

这是我的反正切函数,针对滑动进行了修改:

//Arcotangent modified to suit my swipe needs
func acotForSwipes(x : Double) -> Double {
if x > 1.0 || x < -1.0{
return atan(1.0/x)
} else {
return .pi/2 - atan(x)
}
}

最后,如果您关心的话,这是 panGestureRecognizer 的扩展

    public enum UIPanGestureRecognizerDirection {
case undefined
case bottomToTop
case topToBottom
case rightToLeft
case leftToRight
}
public enum TransitionOrientation {
case unknown
case topToBottom
case bottomToTop
case leftToRight
case rightToLeft
}


extension UIPanGestureRecognizer {
public var direction: UIPanGestureRecognizerDirection {
let velocity = self.velocity(in: view)
let isVertical = abs(velocity.y) > abs(velocity.x)

var direction: UIPanGestureRecognizerDirection

if isVertical {
direction = velocity.y > 0 ? .topToBottom : .bottomToTop
} else {
direction = velocity.x > 0 ? .leftToRight : .rightToLeft
}

return direction
}

public func isQuickSwipe(for orientation: TransitionOrientation) -> Bool {
let velocity = self.velocity(in: view)
return isQuickSwipeForVelocity(velocity, for: orientation)
}

private func isQuickSwipeForVelocity(_ velocity: CGPoint, for orientation: TransitionOrientation) -> Bool {
switch orientation {
case .unknown : return false
case .topToBottom : return velocity.y > 1000
case .bottomToTop : return velocity.y < -1000
case .leftToRight : return velocity.x > 1000
case .rightToLeft : return velocity.x < -1000
}
}
}

extension UIPanGestureRecognizer {
typealias GestureHandlingTuple = (gesture: UIPanGestureRecognizer? , handle: (UIPanGestureRecognizer) -> ())
fileprivate static var handlers = [GestureHandlingTuple]()

public convenience init(gestureHandle: @escaping (UIPanGestureRecognizer) -> ()) {
self.init()
UIPanGestureRecognizer.cleanup()
set(gestureHandle: gestureHandle)
}

public func set(gestureHandle: @escaping (UIPanGestureRecognizer) -> ()) {
weak var weakSelf = self
let tuple = (weakSelf, gestureHandle)
UIPanGestureRecognizer.handlers.append(tuple)
addTarget(self, action: #selector(handleGesture))
}

fileprivate static func cleanup() {
handlers = handlers.filter { $0.0?.view != nil }
}

@objc private func handleGesture(_ gesture: UIPanGestureRecognizer) {
let handleTuples = UIPanGestureRecognizer.handlers.filter{ $0.gesture === self }
handleTuples.forEach { $0.handle(gesture)}
}
}



extension UIPanGestureRecognizerDirection {
public var orientation: TransitionOrientation {
switch self {
case .rightToLeft: return .rightToLeft
case .leftToRight: return .leftToRight
case .bottomToTop: return .bottomToTop
case .topToBottom: return .topToBottom
default: return .unknown
}
}
}

extension UIPanGestureRecognizerDirection {
public var isHorizontal: Bool {
switch self {
case .rightToLeft, .leftToRight:
return true
default:
return false
}
}
}

如果有人觉得这个答案有帮助,请告诉我,这是我第一次回答 :)

关于swift - 如何根据用户手势在 SceneKit 中正确实现 throw 弹丸?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47464555/

54 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com