Есть ли способ ограничить физическое тело внутри обычного н-гона? - PullRequest
0 голосов
/ 30 апреля 2019

Я строю простую игру, в которой я генерирую n-gon вокруг шара, а затем перетаскиваю мяч вокруг n-gon, чтобы столкнуться со стенами.Я хочу, чтобы мяч следовал за моим пальцем, но не оставлял н-гона, когда мой палец выходит за пределы н-гона.Вместо этого, если мой искатель находится вне формы, шар должен отслеживать мой палец, скользя вдоль стены в том же радиане, что и положение моего пальца.

Я добавил физические объекты как к мячу, так и ко всем стенам, однако мой сценарий движения для мяча в настоящее время устанавливает позицию мяча в положение касания, и поэтому шар с телепортом за пределами фигурыесли палец перемещается за пределы фигуры.

Я уже пытался добавить большие прямоугольные физические объекты к внешней стороне каждой стены фигуры, чтобы предотвратить телепортацию мяча туда, однако это просто вызываетфизический объект беситься и сбивать с толку повсюду.

Есть ли лучший способ контролировать мяч, чтобы держать его внутри н-гона?

1 Ответ

1 голос
/ 06 мая 2019

В итоге я решил эту проблему, проверив, было ли положение пальца внутри n-гона, построив очень тонкий прямоугольник от центра к пальцу следующим образом:

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)
    }

затем с помощью функции «пересекается» для каждой стены, чтобы проверить, был ли палец внутри (пересекался хотя бы с 1 стеной) или снаружи (не пересекался ни с какими стенами).

примечание: причина, по которой это должен быть тонкий прямоугольник, а не просто путь, заключается в том, что функция пересечений может принимать только два прямоугольника. поэтому ввод пути просто сгенерирует прямоугольник вокруг двух точек, что очень расстраивает. Из-за этого стены n-гонов (или того, что вы хотите пересечь) также должны быть прямоугольниками.

Если он не пересекается, мы можем явно установить положение мяча в положение пальца. Однако, если он пересекается, мы можем использовать координаты положения стены, позиции касания и нашей центральной точки, чтобы вычислить, где должен быть помещен шарик внутри n-гона, при этом отслеживая палец. Чтобы вычислить эту позицию, мы просто должны применить линейную алгебру к координатам. Для этого я создал структуру для хранения линейных функций:

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
    }

Я надеюсь, что это может помочь некоторым людям, которые пытаются решить эту проблему. Возможно, не самый эффективный способ решения проблемы, но, на мой взгляд, он выглядит чище, чем пытаться поиграть с физикой.

...