Прежде всего, обнаружение столкновений с использованием масок очень трудоемко. Независимо от того, вошла ли ваша игра в бесконечный цикл, требования к обработке проверки перекрытия битовой маски и битовой маски заставят вашу игру работать слишком медленно.
Однако существует простая оптимизация:
Любой объект, который может столкнуться с вещами, должен иметь некоторый максимальный размер - то есть вы можете найти прямоугольник, который всегда будет содержать вашего игрока, и ваш валун может поместиться внутри другого. Поэтому, если ящик вашего игрока не сталкивается с ящиком валуна, они не могут перекрываться. Поскольку вы настаиваете на маскировании коллизий (что может добавить реалистичности любой пиксельной игре), вы можете вычислить коллизию за пиксель всякий раз, когда (и только всякий раз) сталкивающиеся ограничивающие рамки.
Теперь перейдем к вашему стилю кодирования:>: O
Никогда не стоит помещать потенциально бесконечный цикл в функцию, которая в идеале должна вычислять мгновенную проверку на столкновение. В лучшем случае (который, безусловно, достижим), у вас будет одна функция, чтобы проверить, сталкиваются ли два объекта, и сообщить вам более полезную информацию (положение одного относительно другого и т. Д.); в то время как отдельный метод каждого движущегося объекта будет фиксировать столкновения.
Это будет означать что-то вроде:
class CollidingObject:
#This class controls an object which can move and fix collisions
def __init__(self):
self.x = 0 #add relevant initialization code here
self.y = 0
self.xVel = 0 # X and Y velocity (for movement)
self.yVel = 0
self.xSize = 0 # the width of the object in pixels
self.ySize = 0 # the height of the object in pixels
def iscolliding(self, other):
# using x and y as the center of the object,
# this returns an empty tuple if they don't collide
if ((self.xSize + other.xSize) / 2 <= abs(self.x - other.x)) and
((self.ySize + other.ySize) / 2 <= abs(self.y - other.y)): return ()
"""
use pygame collidemask here to compute whether they collide.
if they do, return a vector of 'other's' position relative to self.
(this can be used to decide how to separate the objects)
"""
def restitute(self, overlaps_with, distances):
"""
Given some objects which overlap, and a list of 2D vectors of their
relative distances, this separates them however much you like.
"""
pass
Относительно того, где выполняется проверка коллизий, это зависит от вашей реализации основ управления объектами. До этого я буду предполагать, что все ваши внутриигровые объекты содержатся внутри итерируемого; и что в каждом кадре вы перебираете свои объекты, один раз визуализируете, один раз перемещаете их - структура примерно такая:
while NOT_QUIT:
for object in objects:
object.draw_to_screen()
object.move() # moves the object -- collisions, etc in here
event_handling_stuff() # handles events
В этом случае каждый объект может вычислить проверку столкновения для всего, что следует за ним в объектов . При этом каждый объект может определить, как далеко он должен отойти от каждого. После этого каждый объект может двигаться как можно дальше от каждого коллайдера.
В нескольких играх, которые я написал, я бы заставлял объекты двигаться дальше друг от друга, делая их более перекрытыми, придавая коллизиям упругое качество, что делает даже очень грубые алгоритмы восстановления очень сексуальными. Как правило, вы можете поработать с константами, как только у вас будет рабочая проверка, и это будет вашим наименьшим беспокойством.
Надеюсь, это немного поможет вам двоим (теперь я понимаю, что немного ушел в тупик, но вы спрашивали о том, как сделать неправильные вещи более эффективно: P).
tl; dr: Не пытайтесь исправлять коллизии в вашей функции проверки коллизий. Вместо этого разделите его на один, который находит все столкновения с другими объектами, и другой, который фиксирует все столкновения для объекта одновременно.
Добавьте другие вопросы, и я обновлю (:.
ОБНОВЛЕНИЕ 1
Расширение здесь на "vector of other to self"
бит (который был объяснен немного грубо: /)
Как правило, когда два объекта сталкиваются в реальной жизни, они несколько отскакивают назад в том направлении, откуда они пришли (когда вы бросаете резиновый упругий шар на пол, он отскакивает от того, откуда он пришел - он не просто проходит через фазу этаж). В большинстве программных приложений вы бы хотели, чтобы бодрые шары (и другие сталкивающиеся вещи) вели себя одинаково (ну, иногда вы могли бы хотеть шарик фазировать хотя бы по полу, но это даже проще, чем подпрыгивая ИМХО: P).
Чтобы знать, каким образом объект должен прийти в норму, вы должны знать направление, из которого он пришел. Точнее, вы должны знать угол, под которым он столкнулся. Это очень легко найти, если сравнить расстояние и направление между центрами каждого объекта во время столкновения. Это обеспечит довольно точное представление о двух подпрыгивающих объектах, если используемые вами центры достаточно близки к их центрам масс (в большинстве игр середина объекта является простым и хорошим приближением).
Итак, поскольку нам не нужно беспокоиться о центре масс и всем этом, мы просто измеряем векторное расстояние между позициями объектов:
#Continuing the previous example, we put some code like this in 'iscolliding' :)
if they collide (pygame mask stuff) :
x_distance = self.x - other.x
y_distance = self.y - other.y
return (x_distance, y_distance)
Этот код может дать вам линию, вдоль которой каждыйобъект должен двигаться, чтобы разрешить столкновение как можно быстрее.Остальное - это ускорение каждого объекта вдоль этой линии, чтобы они не сближались (обратите внимание на знаки), и подстройка констант для создания реалистичного эффекта.