计算Scenekit中的覆盖角度

时间:2017-04-17 12:36:37

标签: swift game-physics scenekit projectile

我试图计算到达场景包场景中目标的触及角度。我的数学能力大约是3 /马铃薯,但是在可汗学院工作几个小时后,我想我已经设法梳理了这个"达到的角度"来自维基百科的公式,(我偶然发现了这个Stack Exchange answer)......

enter image description here

我创建了一个函数来返回SCNVector3以将力施加到具有给定质量的球体,以击中目标。

据我了解,公式中的±表示有两个可能的角度,我将分别使用加号和减号angleAangleB

我提出的功能很接近,但有两个问题。

  1. angleB有时候会有点微不足道。
  2. angleA总是不足。
  3. 我可以和angleB生活在一起。我把它归结为SceneKit的物理引擎并不具有确定性。有人请告诉我这是不正确的。

    但我真的想知道angleA出了什么问题。 AngleA应该提供更大的角度来产生我正在寻找的高而清晰的抛物线轨迹。

    这里有完整的功能。

    func launchVectorFor(
            target: SCNVector3,
            fromPosition origin: SCNVector3, 
            withProjectileMass mass: Float) -> SCNVector3 {
    
            let g: Float =  -1 * (scene?.physicsWorld.gravity.y)!; //m/s
    
            // Distance (Displacemet)
            let Dx: Float = (target.x - origin.x)
            let Dy: Float = (target.y - origin.y)
    
            // Velocity
            // I'm just fudging a Velocity here, using the distance
            // between he origin and the target
            let V: Float= sqrt(Dx * Dx + Dy * Dy)
    
            // Useful powers
            let V2: Float = pow(V, 2)
            let V4: Float = pow(V, 4)
            let Dx2 = pow(Dx, 2)
    
            // All the math
            let sqrtInput: Float = V4 - ( g * ( g * Dx2 + 2 * Dy * V2))
    
            let numeratorPlus: Float = V2 + sqrt(sqrtInput)
            let numeratorMinus: Float = V2 - sqrt(sqrtInput)
    
            let angleInRadiansA = atan(numeratorPlus / ( g * Dx ) )
            let angleInRadiansB = atan(numeratorMinus / ( g * Dx ) )
    
            let angleA = angleInRadiansA * (Float(180) / Float.pi)
            let angleB = angleInRadiansB * (Float(180) / Float.pi)
            print("solutions A: \(angleA), -- B \(angleB)")
    
            // Get X & Y components of force * angle * mass
            let Fx = (V * cos(angleInRadiansA)) * mass
            let Fy = (V * sin(angleInRadiansA)) * mass
            let multiplier: Float = Dx > 0 ? 1 : -1
    
            return SCNVector3(Fx * multiplier, Fy * multiplier, 0)
    
        }
    

    以下是调用我的函数的代码,如果它有任何相关性......

    func handleTap(_ gestureRecognize: UIGestureRecognizer) {
            let launchPosition = convertTouchToWorldCoords(location: gestureRecognize.location(in: self.view))
            let target: SCNNode = (scene?.rootNode.childNode(withName: "target", recursively: false))!
    
            let ball: SCNNode = createBall(atLocation: launchPosition)
            scene?.rootNode.addChildNode(ball)
            let launchVector = launchVectorFor(target: target.position, fromPosition: launchPosition, withProjectileMass: 0.5)
            ball.physicsBody?.applyForce(launchVector, asImpulse: true)
            print(launchVector)
        }
    
        func createBall(atLocation location: SCNVector3) -> SCNNode {
            let sphere = SCNSphere(radius: 0.5)
            let shape = SCNPhysicsShape(geometry: sphere, options: [:])
            let ball = SCNNode(geometry: sphere);
            let pBody = SCNPhysicsBody(type: .dynamic, shape: shape)
            pBody.restitution = 0.95
            pBody.mass = 0.5
            ball.physicsBody = pBody
            ball.position = location
            ball.castsShadow = true
            return ball;
        }
    
        func convertTouchToWorldCoords(location: CGPoint) -> SCNVector3 {
            let hits = scnView?.hitTest(location, options: nil)
            if let hitResult = hits?.first {
                return hitResult.worldCoordinates
            }
    
            return SCNVector3Zero
        }
    

    任何帮助非常感谢

1 个答案:

答案 0 :(得分:2)

你不应该"施加一个力",你应该将初始速度设置为你在公式中使用的V.

如果要返回的矢量是单位矢量(幅度为1),则将每个分量乘以V并直接设置对象的速度。

另外,这些公式不考虑阻尼(空气摩擦) - 所以为了准确击中目标,设置你的球linearDamping和{{1} }到0.0(默认值为0.1)。在较长的轨迹上你会发现更多的阻尼。

注意:如果你这样做,不要使用" mass"在角度计算中