swift - 如何设置圆的物理属性使其遵循给定路径

标签 swift sprite-kit skphysicsbody

物理体圈的运动对于我想要实现的目标来说太不稳定了。我想对其进行限制,使其遵循特定路径接触特定点(或一系列点),如下图所示。如何设置物理属性以遍历相似的路径?

enter image description here

最佳答案

how to set physics properties for a circle so it follows given path

所以本质上,您希望使用实时运动将节点移动到特定点。我有一个答案here显示如何执行此操作,但是考虑到此问题收到的赞成票数量,我将提供更详细的答案。

我链接到的答案没有提供遍历点的路径。因此,下面我提供了一个解决方案,展示了如何在下面完成此操作。它只是简单地移动到路径中的每个点,每次节点到达一个点时,我们都会增加索引以移动到下一个点。我还为行进速度、速率(使运动更平滑或更静态)以及节点是否应重复路径添加了一些变量。您可以进一步扩展此解决方案以更好地满足您的游戏需求。我肯定会考虑对节点进行子类化并将此行为构建到其中,以便您可以为多个节点重复使用此 Action 。

最后一点,您可能会注意到我下面的解决方案和上面链接的答案之间的冲量计算有所不同。这是因为我避免使用角度计算,因为它们非常昂贵。相反,我正在计算一个法线,这样计算的计算效率更高。

最后一点,我的回答here解释了使用速率因子来平滑运动并为运动失真留出空间。

import SpriteKit

class GameScene: SKScene {
    var node: SKShapeNode! //The node.
    let path: [CGPoint] = [CGPoint(x: 100, y: 100),CGPoint(x: 100, y: 300),CGPoint(x: 300, y: 300),CGPoint(x: 300, y: 100)] //The path of points to travel.
    let repeats: Bool = true //Whether to repeat the path.
    var pathIndex = 0 //The index of the current point to travel.
    let pointRadius: CGFloat = 10 //How close the node must be to reach the destination point.
    let travelSpeed: CGFloat = 200 //Speed the node will travel at.
    let rate: CGFloat = 0.5 //Motion smoothing.

    override func didMoveToView(view: SKView) {
        node = SKShapeNode(circleOfRadius: 10)
        node.physicsBody = SKPhysicsBody(circleOfRadius: 10)
        node.physicsBody!.affectedByGravity = false
        self.addChild(node)
    }

    final func didReachPoint() {
        //We reached a point!
        pathIndex++

        if pathIndex >= path.count && repeats {
            pathIndex = 0
        }
    }

    override func update(currentTime: NSTimeInterval) {
        if pathIndex >= 0 && pathIndex < path.count {
            let destination = path[pathIndex]
            let displacement = CGVector(dx: destination.x-node.position.x, dy: destination.y-node.position.y)
            let radius = sqrt(displacement.dx*displacement.dx+displacement.dy*displacement.dy)
            let normal = CGVector(dx: displacement.dx/radius, dy: displacement.dy/radius)
            let impulse = CGVector(dx: normal.dx*travelSpeed, dy: normal.dy*travelSpeed)
            let relativeVelocity = CGVector(dx:impulse.dx-node.physicsBody!.velocity.dx, dy:impulse.dy-node.physicsBody!.velocity.dy);
            node.physicsBody!.velocity=CGVectorMake(node.physicsBody!.velocity.dx+relativeVelocity.dx*rate, node.physicsBody!.velocity.dy+relativeVelocity.dy*rate);
            if radius < pointRadius {
                didReachPoint()
            }
        }
    }
}

我做的很快,所以如果有错误,我深表歉意。我现在没有时间,但稍后我会添加一个显示解决方案的 gif。


关于碰撞的注意事项

要修复碰撞期间的不稳定运动,在两个物体碰撞后将“速率”属性设置为 0 或最好设置一个非常低的数字,以减少行进速度脉冲,这将为运动失真提供更多空间。然后在将来的某个时间点(可能是碰撞发生后的某个时间,或者最好是当 body 再次缓慢移动时)将速率设置回其初始值。如果您真的想要一个不错的效果,您实际上可以随着时间的推移将速率值从 0 增加到初始值,让您自己获得平稳和渐进的加速。

关于swift - 如何设置圆的物理属性使其遵循给定路径,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32352886/

相关文章:

ios - stackView 在中心 scrollView

ios - Sprite Kit bodyAtPoint 和 bodyInRect 返回不正确的值?

swift - RxSwift - 谁是观察者?

ios - 将数据从详细 View 传递到主视图? (UITableView) swift

ios - swift - 弹出窗口指针未指向其调用者

ios - 访问所有具有相同 categoryBitMask 的单个对象

ios - 在 SpriteKit 中使用 CAGradientLayer?

ios - 切换 View Controller 后第二次调用方法的问题

swift - SpriteKit : Sprites are moving through each other with a physicsBody already set