Search
Write a publication
Pull to refresh

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

Level of difficultyEasy
Reading time4 min
Views8.1K

Аннотация

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

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

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

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

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

Введение

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

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

Related Work

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

  • Сила тяжести F_g = m g, где m - масса мяча, а g - гравитационная постоянная, около 9.81 м/с^2

  • Линейное трение: F_{fr} = - \beta * \overline{v} (t), где \beta - коэффициент вязкого трения среды, а \overline{v} (t) - вектор скорости в данный момент t.

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

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

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

Нам нужен момент 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, а также по ним обновляются состояния исходных, но от их значений зависит положение обьекта на экране пользователя, а не следующий пересчет: код

Результат
Результат


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

Вторая часть

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

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

Результат
Результат

Код проекта

Итоги

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

Tags:
Hubs:
Total votes 16: ↑15 and ↓1+17
Comments13

Articles