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

时间:2015-09-02 12:15:33

标签: swift sprite-kit skphysicsbody

物理体圈的运动对于我想达到的目标来说太不稳定了。我想限制它,以便它遵循某个特定点(或一系列点)的路径,如下图所示。如何设置物理属性以遍历类似的路径?

enter image description here

2 个答案:

答案 0 :(得分:6)

  

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

基本上,您希望使用实时动作将节点移动到特定点。我有一个答案here显示如何做到这一点,但考虑到这个问题已经收到的票数,我将提供更详细的答案。

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

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

最后一点,我的回答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。

关于碰撞的说明

为了解决碰撞过程中的不规则运动,在2个物体碰撞后设定&#34;率&#34;属性为0或最好是一个非常低的数字,以减少行进速度脉冲,这将给你更多的运动失真空间。然后在将来的某个时刻(可能是碰撞发生后的某个时间或优选,当身体再次缓慢移动时)将速率设置回其初始值。如果你真的想要一个很好的效果,你实际上可以随着时间的推移将速率值从0增加到初始值,以给自己一个平稳和渐进的加速。

答案 1 :(得分:1)

使用followPath:duration:

快速实施
override func didMoveToView(view: SKView) {
    self.backgroundColor = UIColor.blackColor()
    let xWidth: CGFloat = CGRectGetMaxX(self.frame)
    let yHeight: CGFloat = CGRectGetMaxY(self.frame)

    let ball = SKSpriteNode(color: UIColor.redColor(), size: CGSizeMake(50.0, 50.0))
    let offset : CGFloat = ball.size.width / 2

    // Moving path
    let path = CGPathCreateMutable()
    CGPathMoveToPoint(path, nil, offset, yHeight / 2)
    CGPathAddLineToPoint(path, nil, xWidth * 2 / 3, yHeight - offset)
    CGPathAddLineToPoint(path, nil, xWidth - offset, yHeight * 2 / 3)
    CGPathAddLineToPoint(path, nil, xWidth / 2, offset)
    CGPathAddLineToPoint(path, nil, offset, yHeight / 2)

    // Movement
    let moveByPath = SKAction.followPath(path, asOffset: false, orientToPath: false, duration: 4.0)
    let moveForever = SKAction.repeatActionForever(moveByPath)
    ball.runAction(moveForever)

    self.addChild(ball)
}

GameViewController.swift 中,我将默认的GameScene.unarchiveFromFile方法更改为let scene = GameScene(size: view.bounds.size)以创建场景的大小。

预览:

enter image description here