有没有办法将物理物体约束在规则的n-gon内?

时间:2019-04-30 01:29:07

标签: sprite-kit skphysicsbody

我正在构建一个简单的游戏,在该游戏中,我围绕一个球生成n个角,然后将球拖动到该n个角的内部以与墙碰撞。我希望球跟随我的手指,但当我的手指移到n形以外时不离开n形。相反,如果我的发现者不在形状范围内,则球应该以与我的手指位置相同的弧度滑动到墙壁上来跟踪我的手指。

我已经向球和所有墙壁都添加了物理对象,但是我的球运动脚本当前将球的位置设置为触摸位置,因此,如果手指将球传送到形状之外,被移到形状之外。

我已经尝试将大型的矩形物理对象添加到形状的每面墙的外部,以防止球在此处移动,但是这只会导致物理对象在整个位置上跳出来并出现毛刺。

控制球以使其保持在n-gon内是更好的方法吗?

1 个答案:

答案 0 :(得分:1)

我最终解决此问题的方法是,通过从中心到手指构造一个非常细的矩形来检查手指的位置是否在n-gon内:

func updateRadianRect(fingerPos: CGPoint) {
        radianRect.removeFromParent()

// find the length of the rectangle
        let length = CGFloat(round(100 * (CGFloat(pow(fingerPos.x - center.x, 2) + pow(fingerPos.y - center.y, 2)).squareRoot())) / 100)

//create rectangle with given parameters
        radianRect = SKShapeNode(rect: CGRect(x: 0, y: 0, width: 1, height: length))

//place the rectangle at our center point
        radianRect.position = CGPoint(x: tether.x, y: tether.y)

//determine the rotation needed to line up with the touch position
        if fingerPos.x > center.x {
            radianRect.zRotation = atan((fingerPos.y - center.y) / (fingerPos.x - center.x)) - (.pi / 2)
        } else if fingerPos.x < center.y {
            radianRect.zRotation = atan((fingerPos.y - center.y) / (fingerPos.x - center.x)) + (.pi / 2)
        }

        radianRect.lineWidth = 0
        addChild(radianRect)
    }

然后对每个墙使用“相交”功能,以检查手指是在手指内(至少与一堵墙相交)还是在手指外(没有与任何墙相交)。

旁注:之所以必须是细长矩形而不是仅是路径,是因为相交函数只能采用两个矩形。因此输入路径只会在两点周围生成一个矩形,这会非常令人沮丧。因此,n边形的壁(或任何要相交的壁)也必须是矩形。

如果不相交,我们可以将球位置显式设置为手指位置。但是,如果确实相交,我们可以使用墙位置,触摸位置和我们的中心点的坐标来计算球应该放置在n-gon内的位置,同时仍然跟踪手指。要计算该位置,我们只需将一些线性代数应用于坐标即可。为此,我创建了一个包含线性函数的结构:

struct LinearFunction {
// for the form y=slope(x) + b
    var slope = CGFloat()
    var b = CGFloat()
}

然后使用一个函数为一组给定的坐标创建线性函数。

func findEquation(point1: CGPoint, point2: CGPoint) -> LinearFunction {
        let slope = (point2.y - point1.y) / (point2.x - point1.x)
        let b = point1.y - slope * point1.x
        return LinearFunction(slope: slope, b: b)
    }

使用这些方程式,我们可以先计算两条线的相交,然后在n边内侧直接找到该点的旁边

func findBallPositionOnEdge(touchPos: CGPoint) -> CGPoint{
//calculate equations for both the wall and the line 
//from the center point to the touch position
        let wallLine = findEquation(point1: startPoint, point2: endPoint)
        let touchLine = findEquation(point1: centerPoint, point2: touchPos)
//calculate the x and y of the intersect of the two lines
        let intersectX = (touchLine.b - wallLine.b) / (wallLine.slope - touchLine.slope)
        let intersectY = (wallLine.slope * intersectX) + wallLine.b
//find the distance from center point to intersect
        let length = (pow(center.x - intersectX, 2) + pow(center.y - intersectY, 2)).squareRoot()
//use the ratio to find the point 10 unit along the line towards the center
        let ratio = 10/length
//calculate the position just inside of the n-gon
        let insidePoint = CGPoint(x: ((1 - ratio) * intersectX) + (ratio * center.x), y:((1 - ratio) * intersectY) + (ratio * center.y))
        return insidePoint
    }

我希望这可以帮助一些尝试解决此问题的人。在我看来,这也许不是解决问题的最有效方法,但它看起来比尝试摆弄物理学更干净。