如何使用 Swift 5 在 SpriteKit 中以某个角度应用Impulse?

Posted

技术标签:

【中文标题】如何使用 Swift 5 在 SpriteKit 中以某个角度应用Impulse?【英文标题】:How do I applyImpulse at a certain angle in SpriteKit using Swift 5? 【发布时间】:2020-06-08 18:31:45 【问题描述】:

我试图在 ios 游戏中向 touchEnded 方法的方向扔球。我已经搜索了许多线程,但有些是旧的,而对于其他我无法弄清楚问题。

我正在尝试做的事情如下:

1- 添加一个球作为 SKShapeNode - 将受影响的重力设置为 false。

2- 在 touchesEnded 时 - 我得到了位置 CGPoint(不知怎的,我把 y 位置颠倒了 - 我更正了它)。

3- 我使用 atan() 计算角轴承。

4- 我根据角度的 sin() 和 cos() 计算 CGVector dx 和 dy 脉冲。

5- 我根据上面的 CGVector 对球应用Impulse。

我的问题是球没有完全按照 touchesEnded 方向射出。如果 touchesEnded 方向在球的垂直下方,那么它可以工作。离垂直越远,偏差越大。

为了帮助解决问题并将问题可视化,我添加了一条线并在球应遵循的方向结束蓝色球。理想情况下,红球应该与蓝球相撞。 但是,我得到的真正方向是添加的绿球。理想情况下,绿球应该在线上。

我试图完全像这里所做的那样做,但我没有得到结果: SpriteKit physics applyImpulse in direction

感谢您的帮助。 '''

import SpriteKit

class GameScene: SKScene 
    var sprite = SKShapeNode()
    var radius: CGFloat = 10
    var ballSpawnPosition: CGPoint = CGPoint(x: 0, y: 0)
    override func didMove(to view: SKView) 

        self.physicsBody = SKPhysicsBody(edgeLoopFrom: self.frame)
        ballSpawnPosition = CGPoint(x: frame.midX, y: frame.maxY - 50)

        spawnBall()
    

    func spawnBall() 

        sprite = SKShapeNode(circleOfRadius: radius)
        sprite.physicsBody = SKPhysicsBody(circleOfRadius: radius)
        sprite.physicsBody?.collisionBitMask = 0x1
        sprite.fillColor = .red
        sprite.position = ballSpawnPosition
        addChild(sprite)
        sprite.physicsBody?.affectedByGravity = false
    


    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) 
        if let touch = touches.first 
            let position = touch.location(in: view)
            let blueTestTouchPointBall = SKShapeNode(circleOfRadius: radius)
            blueTestTouchPointBall.physicsBody = SKPhysicsBody(circleOfRadius: radius)
            blueTestTouchPointBall.physicsBody?.categoryBitMask = 0x1
            blueTestTouchPointBall.fillColor = .blue
            let y = frame.size.height - position.y
            blueTestTouchPointBall.position = CGPoint(x: position.x, y: y)
            blueTestTouchPointBall.physicsBody?.affectedByGravity = false
            addChild(blueTestTouchPointBall)

            var path = CGMutablePath()
            path.move(to: ballSpawnPosition)
            path.addLine(to: CGPoint(x: position.x, y: y))
            path.closeSubpath()

            let line = SKShapeNode()
            line.path = path
            line.strokeColor = .white
            line.lineWidth = 2
            addChild(line)
        
    


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

        if let touch = touches.first  //should not work if dragged.
            let position = touch.location(in: view)
            let dx = position.x - ballSpawnPosition.x
            let dy = frame.size.height - abs( position.y - ballSpawnPosition.y)
            let ang = atan(abs(dx)/abs(dy))
            //let tang = sqrt(dx*dx + dy*dy)
            let impulse: CGFloat = 15.0
            let x_impulse = impulse * sin(ang) * dx/abs(dx)
            let y_impulse = -1.0 * impulse * cos(ang)
//            let x_impulse = impulse * dx/abs(dx) * dx/tang
//            let y_impulse = impulse * -1 * dy/tang
            sprite.physicsBody?.applyImpulse(CGVector(dx: x_impulse , dy: y_impulse ))


            let greenTestBall = SKShapeNode(circleOfRadius: radius/2)
            greenTestBall.fillColor = .green
            greenTestBall.position = CGPoint(x: ballSpawnPosition.x + 300 * sin(ang) * dx/abs(dx), y: ballSpawnPosition.y - 300 * cos(ang) )
            addChild(greenTestBall)

        

    


'''

【问题讨论】:

【参考方案1】:

您正在混合不同坐标系中的点。使用UITouchlocation(in:) 方法并传入SKNode,应将UITouch 的位置转换为球所在的节点(通常是场景,但也可以是另一个节点)。有关 SpriteKit 中坐标转换的更多信息,请参阅here。由于球是场景的孩子,所以改变

let position = touch.location(in: view)

let position = touch.location(in: self) // i.e. the `GameScene`

假设您使用球的生成位置作为参考点,并且相对于垂直方向取角度,则还需要调整角度计算。改变

let dy = frame.size.height - abs(position.y - ballSpawnPosition.y)

let dy = abs(position.y - ballSpawnPosition.y)

更改上述内容会创建我相信您正在寻找的内容。请注意,动量是相加的:在球向左移动时向右施加冲量不一定会导致球开始向左移动(除非冲量足够大)

【讨论】:

不知道如何将此标记为已回答。但是这篇文章回答了我的问题。非常感谢。 见 (meta.stackexchange.com/questions/5234/…)。数字下方应该有一个灰色复选标记,当您选择它时会变为绿色。

以上是关于如何使用 Swift 5 在 SpriteKit 中以某个角度应用Impulse?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Swift 和 SpriteKit 中使用 UISwipeGestureRecognizer 而不是 touchesMoved?

SpriteKit + Swift - 如何通过缓动移动节点?

如何使用 Swift 在 SpriteKit 上绘制贝塞尔曲线?

如何使用 Swift 正确切换 SpriteKit 中的 SKScene?

如何使用 Swift 在 SpriteKit 程序中初始化/移动相机?

如何使用 swift 使用带有 spriteKit 的情节提要