Как сделать столкновение в pygame

Обновлено: 02.07.2024

В этом листке-проекты мы создадим и визуализируем модель двумерного газа. Также мы поставим над этим газом некоторые эксперименты: посмотрим на броуновское движение, диффузию и т.п.

pygame

Библиотека pygame достаточно обширная, но нам от неё будет нужно не слишком много. Почти всё, что нужно, это — рисование элементарных фигур. Остальное вы будете делать руками.

Пример программы на pygame с шариками

A: Прямолинейное движение

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

B: Отскоки от стен

Сделайте так, чтобы шарики из предыдущей задачи отскакивали от стенок:

Упругое столкновение двух шаров на плоскости

Будем рассматривать только одновременное столкновение ровно двух шариков. Чтобы честно всё посчитать, потребуются закон сохранения импульса, закон сохранения энергии и закон сохранения момента импульса.

Пусть масса первого шарика — $m_1$, радиус — $r_1$, его скорость до столкновения — $\vec = (v_, v_)$, скорость после столкновения — $\vec = (w_, w_)$. А ещё обозначим через $\vec$ вектор от точки контакта шаров до центра первого шара. Для второго шарика всё то же самое, только с индексом 2. Тогда закон сохранения момента требует, чтобы $$ m_1 \vec + m_2 \vec = m_1 \vec + m_2 \vec.$$ Закон сохранения энергии требует, чтобы $$ \frac + \frac = \frac + \frac. $$ Закон сохранения момента импульса более хитрый. Он требует, чтобы $$ [\vec, m_1\vec] + [\vec, m_2\vec] = [\vec, m_1\vec] + [\vec, m_2\vec]. $$ Всё вместе это даёт систему из 4 уравнений на 4 неизвестных — $x$- и $y$-компоненты новых скоростей шариков.

К счастью, если массы и радиусы шаров равны, то решать эту систему не потребуется. Перейдём в систему отсчёта первого шарика. В ней он покоится, а второй шарик натыкается на него со скоростью $\vec-\vec$. После столкновения вся часть этой скорости, направленная вдоль линии центров, достанется первому шарику, а ортогональная ей — второму. То есть после столкновения первый шар полетит вдоль линии центров, а второй — в направлении касательной.

Чтобы доказать это, пригодится система отсчёта общего центра масс, двигающая со скоростью $\dfrac+m_2\vec>=\dfrac+\vec>$. Центр масс в ней неподвижен. Дальше попробуйте разобраться сами.

C: Столкновения — 1: все шарики одинаковы

Реализуйте столкновение одинаковых шаров. Алгорим, работающий за время $O(n^2)$, где $n$ — число шаров, подойдёт.

D: Столкновения — 2: одинаковые массы

Реализуйте столкновение шаров разного радиуса, но одинаковой массы. Алгорим, работающий за время $O(n^2)$, где $n$ — число шаров, подойдёт.

E: Столкновения — 3: общий случай

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

  • масса первого шара $m_1$,
  • масса второго шара $m_2$,
  • пара чисел — скорость первого шара $\vec$,
  • пара чисел — скорость второго шара $\vec$,
  • пара чисел — координаты вектора из точки контакта в центр первого шара $\vec$,
  • пара чисел — координаты вектора из точки контакта в центр второго шара $\vec$.

Вычислите скорости первого и второго шара после их упругого столкновения.

100 100 -5.944050560528412 -8.564544641233914 -3.126256984519088 7.602630729293933 0.6381731882506756 9.98254824345279 -0.6381731882506472 -9.982548243452786

900 100 6.275207281781148 1.8525669980804769 -2.4707106152583087 12.334611148103816 -15.828382139122077 25.495324853000263 5.276127379707354 -8.49844161766675

F: Столкновения — 4: общий случай

Реализуйте столкновение шаров произвольного радиуса и произвольной массы.

G: Броуновское движение

Сделайте модель броуновского движения.

H: Перегородки

I: Диффузия газов

J: Сопло ракетного двигателя

K: Сопло Лаваля

L: Распределение Максвелла

M: Атмосфера

Как найти конфликты между символами и изображениями в PyGame? Я нарисовал игрока из изображения и нарисовал стены из плиток, так как бы я обнаружил эти столкновения?

Если вы используете класс pygame Rect для представления границ вашего объекта, вы можете определить, сталкиваются ли два с помощью функции Rect.colliderect. Например:

a сталкивается с b, а b сталкивается с c, но a не сталкивается с c. Обратите внимание, что прямоугольники, разделяющие границу, не сталкиваются.

Pygame также поддерживает использование Rect в качестве позиции для изображения, когда вы хотите "разбить" его на экране.

Да, но обязательно используйте классы спрайтов для прямоугольников, информацию спрайта, доступную здесь. Итак, для каждого спрайта создайте прямоугольник:

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

затем создайте группы столкновений и используйте pygame.sprite.spritecollide()

Существует множество способов обнаружения столкновений в Pygame. Наиболее популярны методы Rect (@Darthfett) и spritecollide, но есть 2 других, которые вы можете использовать:

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

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

Этот метод, безусловно, мой любимый, и я использую его в 90% всех моих программ. Вы обнаруживаете столкновение путем вычисления. Здесь вы берете координаты объектов 1 и 2, смотрите, насколько они близки друг к другу, и из-за этого возникают столкновения. Этот метод допускает большую гибкость при столкновениях, не слишком неряшлив, если все сделано правильно, и довольно просто. Это может занять немного времени, и вам нужно будет тестировать программу много раз, но результаты ее стоит того. Этот метод также позволяет обнаруживать между невидимыми объектами и позволяет легко регулировать коллизионные поля, например. если есть волшебник или что-то еще, и вы умираете, когда вы на некоторое расстояние от него. Этот метод действительно не так популярен, как должен, но я заверяю вас, что он работает. Две самые простые формы столкновения с этим методом - с круговыми и с прямоугольным столкновением.

fig.blender01

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

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

Библиотека Pygame поддерживает спрайты. Этот код поможет с управлением анимацией, обнаружением столкновений, управлением групп спрайтов.

13.1 Обычные спрайты и столкновения

Библиотека pygame импортируется ради поддержки спрайтов. Библиотека random импортируется ради случайного размещения блоков. Установка цветов проходит по стандартной схеме. Пока в этом примере нет ничего нового.

Начинается определение класса Block . Заметьте, что на строке 15 этот класс является дочерним классом класса Sprite . pygame.sprite. обозначает библиотеку и пакет, которые будут обсуждены несколько позже. Вся стандартная функциональность класса Sprite отныне будет частью класса Block .

Конструктор класса Block , подобно конструкторам других классов, также берёт параметр self . Он также берёт параметр, определяющий цвет, высоту и ширину объекта.

Важно вызывать конструктор родительского класса Sprite для того, чтобы помочь инициализации спрайта. Это делается на строке 21.

Этот код создаёт картинку, которая в скором времени появится на экране. Строка 25 создаёт пустую картинку. Строка 26 заполняет её чёрным. Если программе нужно что-то кроме чёрного квадрата, нужно менять именно эти строки.

Например, посмотрите на нижеприведённый код:

Если бы в создание блока был поставлен этот код, то все блоки были бы в форме эллипсов. Эллипс рисуется на 25й строке, а 26 строка делает белый цвет прозрачным.

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

Атрибут rect - переменная, являющаяся инстанцией класса Rect , предоставляемого Pygame. Прямоугольник представляет размеры спрайта. Этот класс прямоугольник содержит настраиваемые атрибуты для x и y. Pygame нарисует спрайт там, куда указывают атрибуты x и y. Так что для передвижения спрайта программисту потребуется менять mySpriteRef.rect.x и mySpriteRef.rect.y , где mySpriteRef - переменная, указывающая на спрайт.

Этот код инициализирует pygame и создаёт окно для игры. В этом нет ничего отличного от других программ pygame.

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

Подобным образом целые группы спрайтов могут менять свою позицию а также быть перерисованы. Нужно просто скомандовать списку обновиться или перерисовать. Это автоматически обновит или нарисует каждый элемент из списка.

Вышеприведённый код создаёт два списка. Переменная all_sprites_list будет содержать в себе все спрайты в игре. Этот список будет использован для рисования всех спрайтов. Переменная block_list содержит каждый объект, с которым игрок может столкнуться. В этом примере он будет содержать в себе каждый объект игры, исключая объект игрока. Очевидно, в этом примере мы не хотим проверять столкновение объекта игрока с объектом игрока, поэтому программе нужен список, который не содержит самого игрока.

Цикл, начинающийся на 46й строке, добавляет 50 чёрных спрайтов-блоков на экран. Строка 48 создаёт новый блок, задаёт ему цвет, ширину, высоту. Строки 51 и 52 задают кординаты, где появится объект. Строка 55 добавляет блок в список блоков, с которыми может столкнуться игрок. Строка 56 добавляет его в список всех блоков.

Строка 59 создаёт красный блок, который будет представлять игрока. Этот блок добавляется в all_sprites_list , чтобы было возможным его нарисовать.

Это стандартный програмный цикл. Строка 68 ставит очки игрока на 0.

Строка 81 достаёт координаты мыши, подобно тому, как это было сделано в разобнанных ранее Pygame программах. Важная часть содержится в строка 86-87, где прямоугольник, содержащий спрайт игрока, переносится в новое местоположение. Помните, что прямоугольник был создан на строке 32 и этот код не будет работать без той строки.

Эта строка проверяет спрайт player на столкновение со всеми спрайтами из списка block_list . Код возвращает список пересекающихся спрайтов. Если ни один спрайт не пересекается со спрайтом игрока, возвращается пустой лист. Значение True уберёт все столкнувшиеся с игроком спрайты из списка. Если вместо него передать False , спрайты убраны не будут.

Это проверяет, есть ли какие-либо спрайты в списке столкновений. Если такие имеются, увеличиваем очки игрока на количество спрайтов, с которыми он столкнулся. Затем, выведем результаты на экран. Заметьте, что print на строке 95 не выведет значение в главное окно со спрайтами. Вместо этого, вывод будет показан в консольном окне.

Это заставлит нарисовать все спрайты в all_sprites_list .

Это обновляет экран, а также вызывает метод quit после завершения цикла.

13.2 Передвижение спрайтов

В данном примере двигается только спрайт игрока. Как может программа заставить двигаться все спрайты? Этого можно легко достигнуть, нужны только два шага.

Первым шагом будет добавить новый метод в класс Block . Этот метод будет называться update . Функция update будет вызываться автоматически, когда вызывается метод update для всего списка.

Разместите это в классе блока:

Затем поставьте следующую строку в главный цикл программы:

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

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

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

You are not logged in. Log in here and track your progress.

Copyright © 2017
English version by Paul Vincent Craven
Spanish version by Antonio Rodríguez Verdugo
Russian version by Vladimir Slav
Turkish version by Güray Yildirim
Portuguese version by Armando Marques Sobrinho and Tati Carvalho
Dutch version by Frank Waegeman
Hungarian version by Nagy Attila
Finnish version by Jouko Järvenpää
French version by Franco Rossi
Korean version by Kim Zeung-Il
Chinese version by Kai Lin

Я изучаю python, используя pygame, и я работаю над тем, что связано с спрайтами и коллизиями. Я посмотрел несколько примеров, но я до сих пор не совсем понимаю. Я пытаюсь сделать, чтобы иметь возможность добавлять спрайты (мяч), когда пользователь нажимает клавишу "=", а также удаляет последний спрайт, добавленный при нажатии "-". Я не могу удалить только последний, я смог удалить все из них.

До сих пор я мог добавить шары к окну и заставить их отскакивать от стен и друг друга (вроде). Когда 2 шара сталкиваются, они не полностью касаются, но они отскакивают. Иногда шары застревают и не будут двигаться, и иногда шары отскакивают от рамки, которую они не предполагают.

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

Удаление спрайтов на прессе

Проблема sprite.Group.remove(sprites) хочет, чтобы вы указали, какие спрайты следует удалить. sprites здесь должен быть спрайт/список спрайтов, которые вы хотите удалить из группы. Это означает, что для удаления последнего шара, добавленного при нажатии клавиши, вам нужно сохранить список спрайтов с шарами и pop() из последнего добавленного предмета из него, а затем использовать результат pop() в качестве спрайта для удаления из группа. sprite.Group имеет метод .sprites() который возвращает список всех спрайтов в группе, в том порядке, в котором они были добавлены. Этот список генерируется из группы и на самом деле не является интерфейсом, поэтому выполнение этого списка не влияет на группу. Однако мы можем использовать его для получения последнего добавленного спрайта. Вот как это выглядит:

Столкновения

Обратите внимание на то, что мы только разворачиваем скорость, если мы находимся над ребром И, и скорость движется глубже в этом направлении. Теперь для ваших спрайтов у вас есть два варианта, как и с границами:

    Только инициируйте новый ball в пустом пространстве, где он не может столкнуться. Выполните некоторый способ расчета правильной регулировки скорости и примените ее только в том случае, если скорость движется в противоположном направлении.

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

Наконец, для решения вашего другого вопроса о том, почему они сталкиваются, когда они, похоже, не трогают. sprite.spritecollide возвращает, какие спрайты имеют rectangles которые пересекаются. Если ваш ball.jpg имеет цветную ball.jpg для прозрачности, это не влияет на rect спрайта. Pygame, как представляется, имеет функциональные возможности, реализованные предназначено для решения этой проблемы в collided ключевого слова sprite.spritecollide :

pygame.sprite.spritecollide()

    Найдите спрайты в группе, которые пересекают другой спрайт.
    collide_rect collide_rect_ratio collide_circle collide_circle_ratio collide_mask

Это из документации pygame . В документации для функции collide_circle указано, что ваш спрайт должен иметь атрибут radius , иначе он будет рассчитан на весь прямоугольник внутри круга. Таким образом, в вашей функции Ball.__init__ я бы рекомендовал добавить:

Это заставит collide_circle использовать радиус, который приближается к вашему изображению ball , предполагая, что он центрирован и круговым, и занимает все изображение. Затем вы должны добавить спецификацию столкновений в свою проверку на столкновение, изменив:

Если вы решите проблему не порождать новые объекты ball друг в друге, все это будет хорошо работать. Если вы не можете заставить их появляться внутри друг друга, подумайте о другой структуре данных или о другом способе проверки столкновений, чтобы получить нужные результаты. Удачи!

Читайте также: