Unity - Как реалистично переместить корабль в точку в 2D-игре сверху вниз

14

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

я был бы рад, если бы кто-то мог помочь мне с этим вопросом, спасибо движение корабля

DavidT
источник
1
Как на вашем изображении изображены паруса: следует ли учитывать ветер? Некоторые маневры невозможно сделать при неправильном ветре или его отсутствии.
Никто
Более того, реалистично выглядящее движение парусного судна действительно требует учета ветра; игнорирование этого было бы почти равносильно игнорированию гравитации при осуществлении прыжка. Вам не обязательно нужна особенно детальная модель ветра, но вы должны иметь в виду, что ваши корабли отталкиваются ветром в их парусах (а вода - против их киля и руля). В частности, корабли не могут плавать прямо против ветра; они должны бить свой путь там вместо этого.
Илмари Каронен,
Обычно «точка перехода» может быть разделена на фазу вращения и фазу движения вперед. Возьмите тот же подход, но навязывайте вращению движение вперед. Пример каждого радиуса вращения, двигайте лодку вперед на y метров
dnk drone.vs.drones

Ответы:

7

Смотрите эту страницу

Добавление реалистичных поворотов

Следующий шаг - добавить реалистичные изогнутые повороты для наших юнитов, чтобы они не меняли направление внезапно при каждом повороте. Простое решение включает использование сплайна для сглаживания крутых углов в поворотах. Хотя это решает некоторые эстетические проблемы, оно все же приводит к физически очень нереалистичным движениям для большинства юнитов. Например, это может превратить резкий поворот танка в крутой поворот, но изогнутый поворот все равно будет намного более жестким, чем танк может фактически выполнить.

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

Допустим, вы находитесь в некоторой точке (начале координат) и указали в определенном направлении, и вам нужно добраться до какой-либо другой точки (пункта назначения), как показано на рисунке 5. Кратчайший путь можно найти, повернув налево так далеко, как вы может, идти по кругу до тех пор, пока вы не будете прямо направлены на пункт назначения, а затем двигаться вперед или повернуть направо и сделать то же самое. Рисунок 5: Определение кратчайшего пути от пункта отправления до пункта назначения.

На рисунке 5 кратчайшим маршрутом является зеленая линия внизу. Этот путь оказывается довольно простым для расчета из-за некоторых геометрических соотношений, показанных на рисунке 6.

Рисунок 6: Расчет длины пути.

Сначала мы вычисляем местоположение точки P, которая является центром нашего круга поворота и всегда находится в радиусе r от начальной точки. Если мы поворачиваем направо от нашего начального направления, это означает, что P находится под углом (initial_direction - 90) от начала координат, поэтому:

angleToP = initial_direction - 90
P.x = Origin.x + r * cos(angleToP)
P.y = Origin.y + r * sin(angleToP)

Теперь, когда мы знаем местоположение центральной точки P, мы можем рассчитать расстояние от P до пункта назначения, обозначенного на диаграмме как h:

dx = Destination.x - P.x
dy = Destination.y - P.y
h = sqrt(dx*dx + dy*dy)

На этом этапе мы также хотим проверить, что пункт назначения не находится внутри круга, потому что если бы он был, мы бы никогда не смогли его достичь:

if (h < r)
    return false

Теперь мы можем вычислить длину сегмента d, так как мы уже знаем длины двух других сторон прямоугольного треугольника, а именно h и r. Мы также можем определить угол по соотношению прямоугольник:

d = sqrt(h*h - r*r)
theta = arccos(r / h)

Наконец, чтобы выяснить точку Q, в которой нужно выйти из круга и начать по прямой, нам нужно знать общий угол +, и его легко определить как угол от P до пункта назначения:

phi = arctan(dy / dx) [offset to the correct quadrant]
Q.x = P.x + r * cos(phi + theta)
Q.y = P.y + r * sin(phi + theta)

Вышеуказанные расчеты представляют собой поворот направо. Левый путь может быть рассчитан точно таким же образом, за исключением того, что мы добавляем 90 к initial_direction для вычисления angleToP, а позже мы используем - вместо +. Вычислив оба, мы просто видим, какой путь короче, и используем этот.

В нашей реализации этого и последующих алгоритмов мы используем структуру данных, в которой хранится до четырех отдельных «отрезков», каждый из которых является прямым или изогнутым. Для описанных здесь изогнутых траекторий используются только два сегмента: дуга, за которой следует прямая линия. Структура данных содержит элементы, которые определяют, является ли сегмент дугой или прямой линией, длиной сегмента и его начальной позицией. Если сегмент является прямой линией, структура данных также определяет угол; для дуг он указывает центр круга, начальный угол на окружности и общее количество радиан, охватываемых дугой.

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

ЛИСТИНГ 2. Расчет положения и ориентации в конкретный момент времени.

distance = unit_speed * elapsed_time
loop i = 0 to 3:
    if (distance < LineSegment[i].length)
        // Unit is somewhere on this line segment
        if LineSegment[i] is an arc
            //determine current angle on arc (theta) by adding or
            //subtracting (distance / r) to the starting angle
            //depending on whether turning to the left or right
            position.x = LineSegment[i].center.x + r*cos(theta)
            position.y = LineSegment[i].center.y + r*sin(theta)
        //determine current direction (direction) by adding or
        //subtracting 90 to theta, depending on left/right
        else
            position.x = LineSegment[i].start.x 
              + distance * cos(LineSegment[i].line_angle)
            position.y = LineSegment[i].start.y
              + distance * sin(LineSegment[i].line_angle)
        direction = theta
        break out of loop
    else
        distance = distance - LineSegment[i].length
Draco18s больше не доверяет SE
источник
4
Этот ответ на самом деле не смотрит на физику кораблей. Я также считаю проблематичным, что это в основном ссылка и длинная выдержка из другого сайта (я не уверен в законности).
Никто
В прошлый раз, когда я предоставлял существующий ресурс, который был решением поставленного вопроса, меня попросили включить содержание ссылки в том случае, если целевой сайт перестал быть. Теперь меня просят не включать контент. Решайся.
Draco18s больше не доверяет SE
3
@ Draco18s: Что вы должны сделать, это кратко изложить основные моменты связанного материала своими словами. (Или, что еще лучше, ответьте на вопрос, основываясь на своем собственном опыте, и используйте ссылки только в качестве вспомогательного материала или для дальнейшего чтения.) Короткие цитаты, как правило, приемлемы, особенно в ситуациях, когда их невозможно избежать (например, цитируя чью-то точную информацию). слова, чтобы продемонстрировать, что они действительно что-то сказали), но цитирование значительной части статьи действительно выходит за рамки добросовестного использования .
Илмари Каронен
Если точка находится внутри круга, вы можете немного выйти и вернуться.
user253751
(Пс. Смотрите также эти два вопроса на meta.SE.)
Илмари Каронен
7

Как простое решение, как я уже сказал в комментарии, вы можете попробовать этот подход:

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

turnAngSpeed = 0.4 --direction changing speed
ForwordSpeed = 40 -- full forward speed
turnForwordSpeed = ForwordSpeed *0.6 -- forward speed while turning
function ent:update(dt)
            dir = getVec2(self.tx-self.x,self.ty-self.y) -- ship --> target direction (vec2)
            dir = dir.normalize(dir) --normalized                               
            a= dir:angle() - self.forward:angle() --angle between target direction e current forward ship vector
            if (a<0) then
             a=a+math.pi *2 -- some workaround to have all positive values
            end

            if a > 0.05 then -- if angle difference 
                if a < math.pi then
                    --turn right
                    self.forward = vec2.rotate(self.forward,getVec2(0,0),turnAngSpeed * dt)
                else
                    --turn left
                    self.forward = vec2.rotate(self.forward,getVec2(0,0),-turnAngSpeed * dt)
                end             
                --apply turnForwordSpeed
                self.x = self.x+ self.forward.x * turnForwordSpeed * dt
                self.y = self.y+ self.forward.y * turnForwordSpeed * dt
            else 
                --applly ForwordSpeed
                self.x = self.x+ self.forward.x * ForwordSpeed * dt
                self.y = self.y+ self.forward.y * ForwordSpeed * dt
            end
end

введите описание изображения здесь

Анимация примера показывает (последний цикл) случай, когда корабль не может достичь цели, так как комбинация скорости поворота и движения вперед определяет слишком большой радиус поворота, в этом случае может быть полезно уменьшить " turnForwordSpeed" или лучше сделать это зависит от углового расстояния ( a) и целевого расстояния.

dnk drone.vs.drones
источник
Это хороший ответ, но он может быть или не быть достаточно реалистичным для ОП. В отличие, скажем, от автомобилей, у кораблей на самом деле нет «радиуса поворота»: большинство кораблей с собственным двигателем (двигателем / человеком) могут, по сути, включать десять центов, в то время как парусные корабли зависят от ветра и могут фактически иметь отрицательный эффективный поворот. радиус при взлете, в том смысле, что поворот налево на ветер может привести к смещению корабля вправо. Какие корабли делают есть инерция (и сопротивление): они не могут повернуть или переместить мгновенно, и после перемещения или поворота, потребуется некоторое время и силы , чтобы остановить. Тем не менее, есть +1.
Ильмари Каронен,
Большое спасибо за ваш ответ!!! :) Вы, сэр, мой герой!
DavidT
@DavidT Тогда подумайте о том, чтобы пометить его / ее ответ как принятый, если он смог удовлетворительно решить вашу проблему. :)
И
-2

Система ячеек Unity Nav, вероятно, будет делать то, что вы хотите, немного поиграв со значениями агента навигации.

Навигационные сетки довольно просты в использовании. И используется только в настройках сверху вниз (или, по крайней мере, доступно только для перемещения по оси X / Z)

Страница руководства Unity по настройке навигационной сетки

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

ProtoJazz
источник
Я нахожу, что в этом отношении у Драко-18 тоже нет ответа. Тем не менее, ваш не реальный ответ, а скорее комментарий.
Никто
2
Это хорошее предложение, но для того, чтобы получить хороший ответ, ему нужна поддержка и информация о реализации. Пожалуйста, добавьте некоторую информацию о настройке навигационных сеток, чтобы сделать это хорошим ответом. Я думаю, что это то, что вышеупомянутые комментаторы пытаются сказать :)
sirdank