Этот вопрос является «дополнительным» из моего предыдущего вопроса об обнаружении и разрешении столкновений, который вы можете найти здесь .
Если вы не хотите читать предыдущий вопрос, вот краткое описание того, как работает мой физический движок:
Каждый физический объект хранится в классе с именем SSSPBody.
Поддерживаются только AABB.
Каждый SSSPBody хранится в классе под названием SSSPWorld, который обновляет каждое тело и обрабатывает гравитацию.
Каждый кадр, SSSPWorld обновляет каждое тело.
Каждое обновленное тело ищет близлежащие тела в пространственном хеше, проверяет, нужно ли им обнаруживать столкновения с ними. Если да, они вызывают событие «столкновения» и проверяют, нужно ли им разрешать столкновения с ними. Если да, они рассчитывают вектор проникновения и направленное перекрытие, затем меняют свое положение, чтобы разрешить проникновение.
Когда тело сталкивается с другим, оно передает свою скорость другому, просто устанавливая скорость тела на свою собственную.
Скорость тела установлена в 0, если она не изменила положение с последнего кадра. Если он также сталкивается с движущимся телом (например, лифтом или движущимися платформами), он рассчитывает разницу движения лифта, чтобы увидеть, не было ли тело перемещено из своего последнего положения.
Кроме того, тело вызывает «раздавленное» событие, когда все его углы AABB перекрывают что-либо в кадре.
Это ПОЛНЫЙ исходный код моей игры. Он разделен на три проекта. SFMLStart - это простая библиотека, обрабатывающая ввод, отрисовку и обновление сущностей. SFMLStartPhysics является наиболее важным, где классы SSSPBody и SSSPWorld. PlatformerPhysicsTest - игровой проект, содержащий всю игровую логику.
И это метод "update" в классе SSSPBody, прокомментированный и упрощенный. Вы можете взглянуть только на это, если вам не хочется смотреть на весь проект SFMLStartSimplePhysics. (И даже если вы это сделаете, вы все равно должны взглянуть на это, поскольку он прокомментирован.)
.Gif показывает две проблемы.
- Если тела размещены в другом порядке, получаются разные результаты. Ящики слева идентичны ящикам справа, размещены только в обратном порядке (в редакторе).
- Оба ящика должны быть продвинуты к верхней части экрана. В ситуации слева ящики не приводятся в движение. Справа только один из них. Обе ситуации являются непреднамеренными.
Первая проблема: порядок обновления
Это довольно просто понять. В ситуации слева самый верхний ящик обновляется раньше другого. Даже если ящик снизу «передает» скорость другому, ему нужно подождать, пока следующий кадр не сдвинется. Поскольку он не двигался, скорость нижнего ящика установлена на 0.
Я понятия не имею, как это исправить. Я бы предпочел, чтобы решение не зависело от «сортировки» списка обновлений, потому что я чувствую, что делаю что-то не так во всей конструкции физического движка.
Как основные физические движки (Box2D, Bullet, Chipmunk) обрабатывают порядок обновления?
Вторая проблема: только один ящик движется к потолку
Я пока не понимаю, почему это происходит. То, что делает «пружинная» сущность, это установить скорость тела на -4000 и переместить ее поверх самой пружины. Даже если я отключу код повторного позиционирования, проблема все равно возникает.
Моя идея состоит в том, что когда нижний ящик сталкивается с верхним, его скорость устанавливается равной 0. Я не уверен, почему это происходит.
Несмотря на возможность выглядеть как кто-то, кто сдается из-за первой проблемы, я разместил весь исходный код проекта выше. У меня нет ничего, чтобы доказать это, но, поверьте мне, я изо всех сил пытался это исправить, но я просто не мог найти решение, и у меня нет никакого предыдущего опыта с физикой и столкновениями. Я пытался решить эти две проблемы больше недели, и теперь я в отчаянии.
Я не думаю, что смогу найти решение самостоятельно, не убрав из игры многие функции (например, передачу скорости и пружины).
Большое спасибо за время, потраченное на чтение этого вопроса, и еще больше спасибо, если вы даже попытаетесь найти решение или предложение.
Ответы:
На самом деле, порядок задач обновления довольно распространен для обычных движков импульсной физики, вы не можете просто отложить применение силы, как предполагает Vigil, вы в конечном итоге нарушите сохранение энергии, когда объект одновременно сталкивается с двумя другими. Обычно им удается создать нечто, что кажется довольно реальным, даже если другой порядок обновлений привел бы к существенно другому результату.
В любом случае, для вашей цели в импульсной системе достаточно икоты, и я бы посоветовал вам вместо этого создать модель «масса-пружина».
Основная идея состоит в том, что вместо того, чтобы пытаться разрешить столкновение за один шаг, вы применяете силу к объектам столкновения, эта сила должна быть эквивалентна величине перекрытия между объектами, это сравнимо с тем, как реальные объекты во время столкновения преобразуют свои объекты. движение энергии в деформацию, а затем обратно в движение, замечательная особенность этой системы в том, что она позволяет силе проходить через объект, не заставляя этот объект подпрыгивать назад и вперед, и это может быть разумно сделано полностью независимо от обновления.
Чтобы объекты останавливались, а не колебались бесконечно, вам придется применить некоторую форму демпфирования, вы можете сильно повлиять на стиль и ощущение вашей игры в зависимости от того, как вы это делаете, но самый базовый подход заключается в том, чтобы применить силу к двум соприкасающимся объектам, эквивалентным их внутреннему движению, вы можете применить его только тогда, когда они движутся навстречу друг другу, или также когда они отходят друг от друга, последний может использоваться для полного предотвращения отскока объектов назад когда они упадут на землю, но также сделают их немного липкими.
Вы также можете создать эффект трения, затормозив объект в перпендикулярном направлении столкновения, величина торможения должна быть равна величине перекрытия.
Вы можете легко обойти понятие массы, если все объекты будут иметь одинаковую массу, а неподвижные объекты будут работать как бесконечная масса, если вы просто пренебрегаете их ускорением.
Некоторый псевдокод, на случай, если вышеизложенное не достаточно ясно:
Смысл свойств addXvelocity и addYvelocity заключается в том, что они добавляются к скорости их объекта после того, как вся обработка столкновений выполнена.
Редактировать:
Вы можете делать вещи в следующем порядке, где каждая пуля должна быть выполнена на всех элементах, прежде чем будет выполнена следующая:
Кроме того, я понимаю, что следующее может быть не совсем ясным в моем первоначальном посте: под влиянием гравитации объекты будут перекрываться, когда лежат друг на друге, это говорит о том, что их поле столкновения должно быть немного выше, чем их графическое представление, чтобы избежать наложения визуально. Эта проблема будет меньше, если физика работает с более высокой частотой обновления. Я предлагаю вам попробовать работать на частоте 120 Гц для разумного компромисса между временем процессора и точностью физики.
Edit2:
очень простой физический движок:
acceleration = [Complicated formulas]
velocity += acceleration
position += velocity
источник
Ну, вы, очевидно, не тот, кто легко сдается, вы настоящий железный человек, я бы поднял руки в воздух гораздо раньше, так как этот проект сильно напоминает лес ламинарии :)
Прежде всего, позиции и скорости устанавливаются повсеместно, с точки зрения подсистемы физики, это рецепт катастрофы. Кроме того, при изменении целых вещей различными подсистемами создайте закрытые методы, такие как «ChangeVelocityByPhysicsEngine», «ChangeVelocityBySpring», «LimitVelocity», «TransferVelocity» или что-то в этом роде. Это добавит возможность проверки изменений, сделанных определенной частью логики, и придаст дополнительное значение этим изменениям скорости. Таким образом, отладка будет проще.
Первая проблема
На сам вопрос. Теперь вы просто применяете фиксированные положения и скорости «по ходу» в порядке появления и логики игры. Это не будет работать для сложных взаимодействий без тщательного кодирования физики каждой сложной вещи. Отдельный физический движок тогда не нужен.
Чтобы выполнять сложные взаимодействия без взломов, вам необходимо добавить дополнительный шаг между обнаружением столкновений на основе позиций, которые были изменены начальными скоростями, и окончательными изменениями позиций на основе «последующей скорости». Я предполагаю, что это пойдет так:
Могут появиться дополнительные вещи, такие как борьба с рывками, отказ от суммирования, когда FPS маленький, или другие подобные вещи, будьте готовы :)
Вторая проблема
Вертикальная скорость обоих этих «дедвейтов» ящиков никогда не меняется от нуля. Странно, но в цикле обновления PhysSpring вы назначаете скорость, но в цикле обновления PhysCrate она уже равна нулю. Можно найти линию, где скорость идет не так, как надо, но я перестал отлаживать здесь, потому что это ситуация «пожнешь то, что ты шьешь». Пришло время прекратить кодирование и начать переосмысливать все, когда отладка становится сложной. Но если доходит до того, что даже автору кода невозможно понять, что происходит в коде, то ваша кодовая база уже мертва без вашего осознания этого :)
Третья проблема
Я думаю, что что-то не так, когда вам нужно воссоздать часть Farseer, чтобы сделать простой платформер на основе плиток. Лично я бы подумал о вашем нынешнем движке как об огромном опыте, а затем полностью отказался бы от него для более простой и понятной физики на основе тайлов. При этом было бы разумно воспользоваться такими вещами, как Debug.Assert и, может быть, даже, о ужас, юнит-тестами, так как можно было бы ловить неожиданные вещи раньше.
источник
Ваша проблема в том, что это в корне неверные предположения о движении, поэтому то, что вы получаете, не похоже на движение, как вы с ним знакомы.
Когда тело сталкивается с другим, импульс сохраняется. Думать об этом как «A бьет B» по сравнению с «B бьет A» - значит применять переходный глагол к непереходной ситуации. А и Б сталкиваются; результирующий импульс должен быть равен начальному импульсу. То есть, если A и B имеют одинаковую массу, они оба теперь путешествуют со средним значением их исходных скоростей.
Вам также, вероятно, понадобится некоторое столкновение и итеративный решатель, иначе вы столкнетесь с проблемами стабильности. Возможно, вам следует прочитать некоторые презентации Эрин Катто на GDC.
источник
Я думаю, что вы приложили действительно благородные усилия, но, похоже, существуют фундаментальные проблемы с тем, как структурирован код. Как и предполагали другие, это может помочь разделить операции на отдельные части, например:
Разделяя фазы, все объекты обновляются синхронно, и у вас не будет зависимостей порядка, с которыми вы в настоящее время боретесь. Код также, как правило, оказывается проще и легче изменить. Каждый из этих этапов является довольно общим, и часто можно заменить лучшие алгоритмы после того, как у вас есть работающая система.
Тем не менее, каждая из этих частей является наукой сама по себе, и может занять много времени, пытаясь найти оптимальное решение. Может быть, лучше начать с некоторых из наиболее часто используемых алгоритмов:
Хорошее (и очевидное) место для начала - это законы движения Ньютона .
источник