«Физика для программистов» — как физтехи применяют её в приложениях. Бросок объекта под уголом к горизонту

Аннотация

Данная статья входит в цикл, освещающий задачи на моделирование физических процессов на факультете МТФИ ВШПИ.

Полученной задание выглядело достаточно скучно:

5fdfded583805897076b7b8401415375.png

Мы решили, что хотим повеселиться. А то, что из этого вышло, вы узнаете в этой статье.

Первая реализация — Angry Ball

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

Введение

Коэфицент вязкого трения - 2

Коэфицент вязкого трения — 2

Проект Angry Ball — это приложение Flutter, которое демонстрирует законы полета через интерактивную игру, где игроки бросают мяч в среде с линейной функцией трения от скорости. Проект разработан так, чтобы быть как развлекательным, так и образовательным, предоставляя возможность узнать о гравитации, дифференциальных уравнениях, мобильной разработке и игровых движках. Автор надеется, что эта игра может потенциально вызвать жгучий интерес в багажнике к дальнейшему обучению.

Related Work

Раскрутим клубок, объясняющий симуляцию. Какие силы влияют на мяч? Их две:

Чтобы не иметь дело с вектором скорости и оставить только числа, пусть:

6c4a3764602f1adad8cb7a39af6a42d5.png

С помощью второго закона Ньютона: F = m a, дифференциальное уравнение для мяча в воздухе будет:

f6c78a589232ff3b6cc179359333f70f.png

Расстояние от начала координат и высота мяча от времени:

8c384c856533590bc551470e26c9791c.png

Нам нужен момент t_0, когда:

y(t_0) = 0

Зависимость расстояния и высоты от времени

Зависимость расстояния и высоты от времени

Построив эти 2 графика в Desmos, мы замечаем, что у y(t) есть только 2 решения. Первое — это t = 0 и действительно y(0) = 0 из-за начальных условий дифференциального уравнения. Второй корень можно найти с помощью алгоритма тернарного поиска. Теперь, зная t_0, программа вставляет значение его в функцию длины x(t_0), чтобы получить координату x_0, оценочное расстояние текущего броска. Полученное решение на практике дает погрешность не более 3–5%, однако настоящая точность не высчитывалась.

Flame?) Серьезно?

Коэфицент вязкого трения - 8

Коэфицент вязкого трения — 8

Отец спросил, хватит ли у меня смелости забабахать мобильное приложение. Так как с Unity я никогда не работал, а писать надо с 0, то взял за основу первый попавшийся движок. Им оказался Flame, новинка от команды Flutter, мультиплатформенного фреймворка от Гугла.

Настоящие мужчины используют указатели, ссылки, разбираются в State Management. Я же наследовал все классы от HasGameRef и все текущие параметры программы хранил обычными переменными в main классе. Не лучшая идея, но для маленького pet-project без дальнейшего будущего самое то. Быстро и незамысловато.

Итого, у меня было 2 класса для движков (сил действия на объект и сам объект, шарик), пару вспомогательных классов для математических расчетов и задания параметров окружения, в котором будет находится объект. Не то, чтобы все это было категорически необходимо для реализации, но если уж не хардкодить единицы длины (метр), то к делу надо подходить серьезно.

Вторая реализация — React

А что если на React?

Я задался таким вопросом, когда решил написать эту работу по физике. Мне хотелось попрактиковаться в React.

Первая часть

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

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

const updateTime = () => {
   const date = new Date();
   return (
      date.getHours() * 60 * 60 * 1000 +
      date.getMinutes() * 60 * 1000 +
      date.getSeconds() * 1000 +
      date.getMilliseconds()
   );
};

Переменные, которые описывают положение и скорость объекта:

const [objectPositionX, setObjectPositionX] = useState(startPositionX);
const [objectPositionY, setObjectPositionY] = useState(startPositionY);
const [objectSpeedX, setObjectSpeedX] = useState(startSpeedX);
const [objectSpeedY, setObjectSpeedY] = useState(startSpeedY);

Начальный (нерабочий) вид пересчета: код
Реализован функционал остановки объекта при клике на область.
Проблема: пересчитывать переменные состояния каждую милисекунду не получится, так как setState и setInterval — асинхронные функции, и значение переменной не успевает измениться к следующему пересчету.
Решение: я добавил переменные без состояния, которые являются полными копиями исходных, теперь на них происходит пересчет в setInterval, а также по ним обновляются состояния исходных, но от их значений зависит положение обьекта на экране пользователя, а не следующий пересчет: код

Результат

Результат


Реализация этой части

Вторая часть

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

222aadaed8c89e5ae71cec141d95191f.png

Из-за этого пользователь получает данные о дальности и максимальной высоте с погрешностью. Я решил добавить пользователю возможность вводить требуемую погрешность, по которой сервис будет выдавать интервалы значений, которые пользователь может ввести в оставшиеся поля. Чтобы это посчитать, я делал бинарный поиск и выдавал подходящий интревал, по невведеному значению. Бинпоиск подходит, потому что погрешность растет, когда объект проводит мало времени в движении, то есть при маленькой скорости или сильном сопротивлении.
Чтобы посчитать корректные значения дальности и максимальной высоты при выбранных параметрах в бинпоиске, я брал выше частоту обновления. Мне не нужно отрисовывать это, поэтому поиск подходящих интервалов работал быстро.
Если пользователь захочет посчитать интервал для скорости, то он должен будет ввести все известные параметры, а мой сервис вернет интревал.

Результат

Результат

Код проекта

Итоги

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

© Habrahabr.ru