2017-04-17 206 views
1

我正在试图计算伸展角度以在场景套件场景中击中目标。我的数学能力约为3 /马铃薯,但在汗学院几个小时后,我想我已经设法从维基百科中梳理出这个“达到角度”公式的逻辑(我在这个Stack Exchange answer中偶然发现了这个公式)。 ..在Scenekit中计算伸展角度

enter image description here

我创建返回SCNVector3的功能,将力施加到球体与给定质量,以击中目标。

据我所知,公式中的±表示有两个可能的角度,我将分别调用angleAangleB,它们分别使用正值和负值。

我想到的功能很接近,但有两个问题。

  1. angleB有时是一个小小的痕迹。
  2. angleA总是不足。

我可以住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 
    } 

任何帮助非常赞赏

回答

2

你不应该“应用力“,你应该将初始速度设置为公式中使用的V.

如果您要返回的向量是单位向量(具有1的大小),则将每个分量乘以V并直接设置对象的速度。此外,这些公式并不考虑阻尼(空气摩擦) - 所以要准确击中目标,请将球的linearDampingangularDamping设置为0.0(默认值为0.1)。你会注意到更长的轨迹更多的阻尼。

注意:如果你这样做,不要在角度计算

+0

确定用“质量”?我正在应用一种冲动,我想你在描述什么。在场景包中,就像这样做了'ball.physicsBody?.applyForce(launchVector,asImpulse:true)'。我不能将'某物'设置为'V'。我需要知道V对于给定角度的X和Y分量,因为脉冲被应用为Vector3。 – gargantuan

+0

试试吧。您可以将ball.velocity设置为3D矢量。确保矢量的大小等于公式中使用的V. –

+0

冲击力与设定速度不一样。这会导致球有一定的速度,但不一定是用于公式计算角度的球。 –