box2d: Как запретить персонажу подпрыгивать в воздухе?

#box2d

#box2d

Вопрос:

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

По большей части это работает. За исключением того, что если я нажму, когда игрок находится в воздухе, ApplyLinearImpulse вызывается снова, и игрок снова «прыгает», но уже в воздухе. Было бы неплохо сделать это в реальной жизни, но вы не можете, и я бы предпочел, чтобы мой игрок тоже не мог этого делать.

Итак, я пытался придумать достойный способ предотвратить прыжки, когда игрок уже прыгает, и я не уверен, куда идти дальше — моя лучшая мысль — попробовать что-то вроде этого:

  • Прикрепите корпус, который является просто датчиком, к нижней части моего плеера с помощью фиксированного шарнира.
  • Когда игрок прыгает, отключите прыжки до тех пор, пока вышеупомянутый датчик не обнаружит столкновение (т. е. когда игрок «приземляется» на что-то, что может быть землей, а может и не быть).
  • Как только столкновение было обнаружено, снова включите прыжки.

Я не уверен, насколько мне нравится эта идея, например, я не уверен, что прикрепление датчика к нижней части моего плеера является «самым умным» способом обнаружения столкновений в нижней части моего плеера sprite. Я еще не пробовал это, я хотел вместо этого получить информацию от SO и посмотреть, может ли кто-нибудь предложить лучшую альтернативу. Есть идеи?

Редактировать: у mafutrct было хорошее предложение: попробуйте какой-нибудь тест на попадание вниз, используя луч, направленный прямо вниз от тела моего игрока, и посмотрите, пересекает ли этот луч другой объект на небольшом расстоянии (т. Е. прямо под моим игроком) — возможен ли такой тип теста на попадание луча с box2d?

Комментарии:

1. iforce2d.net/b2dtut/jumpability — iforce2d для меня — это универсальное хранилище знаний box2d.

Ответ №1:

Мой голос отдается методу «датчик у ног игрока». Вы могли бы сделать сенсорное крепление ребром, если вам действительно нужна проверка типа луча, но в целом я думаю, что более полезно иметь возможность указывать область, особенно если ваш персонаж сможет стоять на нескольких предметах одновременно, и если вы заинтересованы в поддержании актуального списка того, на чем стоит. Если вы создаете тело игрока в графическом редакторе, то также будет более интуитивно понятно размещать сенсор и смотреть на него вместо того, чтобы запускать все это программно с помощью raycasts. Я считаю, что метод датчика стопы также требует меньших затрат процессора в целом и меньше работы, чем самостоятельное выполнение проверок raycast. Я написал подробное руководство по этому поводу, надеюсь, оно полезно:http://www.iforce2d.net/b2dtut/jumpability

Комментарии:

1. Я воспользовался вашим руководством (хотя нашел ссылку в другом месте) и считаю, что этот подход кажется надежным и сработал для меня.

Ответ №2:

Вам нужно проверить, касаются ли ноги земли в момент прыжка.

В зависимости от вашей модели, измерьте расстояние между вашими ногами и землей. Либо примените вектор из центра модели и проверьте попадание в вершину, либо (более сложный способ) проверьте первое вертикальное попадание в многоугольник, описывающий область ваших ног с землей.

Простая проверка на любое столкновение завершается неудачей, поскольку вы можете упереться в стену, которая все равно считается воздушной (если вы не играете в Unreal Tournament, в котором это прыжок со стены).

Я не знаком с вашим программным обеспечением, так что это всего лишь идея — было бы здорово, если бы это действительно было полезно. Удачи 🙂

Комментарии:

1. Хм, проблема с проверкой только по земле заключается в том, что игрок вполне может приземлиться на что-то, что находится над землей, то есть на коробку, которая стоит на земле. Я не уверен, что этого будет достаточно.

2. Да, вам нужно выполнить тест попадания вниз. Если расстояние до удара небольшое, вы касаетесь земли. Если он большой, вы все еще в воздухе.

3. Вопрос, как описано в ответе выше, заключается в том, как выполнить тест попадания — вы хотите протестировать только одну точку, обычно луч, направленный прямо вниз от центра вашей модели, или вы хотите использовать более сложный тест (т. Е. полигон)? Я предполагаю, что первого достаточно, поскольку вы все еще можете расширить и улучшить его позже, если это необходимо.

4. Да — я думаю, это имеет гораздо больше смысла. Вы знаете, поддерживает ли box2d какой-либо тест попадания, подобный тому, что вы описываете? Похоже, это именно то, что мне нужно.

5. Извините, но я этого не знаю. Я надеюсь, вы сможете найти способ (возможно, в Google?) — или, может быть, создать другой вопрос по SO 🙂

Ответ №3:

Способ решить эту проблему — создать класс, который реализует ContactListener, а затем, когда вы создаете Мир, вы .setContactListener() присваиваете своему классу. Теперь ваш класс будет уведомляться обо всех столкновениях. Оттуда вы создаете флажок, показывающий, находится ли игрок в контакте с какими-либо наземными объектами.

Предположим, что вы установили пользовательские данные фигур в соответствующие GameObjects ( shapeDef.userData = this; в конструкторе GameObject). Предположим, у вас есть перечисление, которое определяет типы GameObject в вашей игре, тогда у вас есть контактный прослушиватель в соответствии с этими строками:

импортируйте org.jbox2d.dynamics.ContactListener;
импортируйте org.jbox2d.dynamics.contacts.Точка контакта;
импортируйте org.jbox2d.dynamics.contacts.ContactResu<
импортируйте android.util.Log;

импортируйте GameObject.GAME_OBJECT_TYPE;

открытый класс CollisionChecker реализует ContactListener {

 частное логическое столкновение с землей = false;

 @Переопределить 
 добавить общедоступную пустоту (точка контакта arg0) {
 // Вызывается при добавлении точки контакта. Это включает в себя геометрию и силы.
 }

 @Переопределить 
 сохраняется общедоступная пустота (точка контакта arg0) {
 // Вызывается, когда точка контакта сохраняется. Это включает в себя геометрию и силы.
 }

 @Переопределить 
 удаление общедоступной пустоты (точка контакта arg0) {
 // Вызывается, когда точка контакта удалена. Это включает в себя последнюю вычисленную геометрию и силы.
 }

 @Переопределить 
 результат public void (ContactResult arg0) {
 // Это вызывается после разрешения коллизии 

 GAME_OBJECT_TYPE a = ((GameObject) arg0.shape1.getUserData()).GetType();
 GAME_OBJECT_TYPE b = ((GameObject) arg0.shape2.getUserData()).GetType();
 // проверьте наличие земли 
 groundCollision = проверка(a, b, GAME_OBJECT_TYPE.ground); 
 }

 частная логическая проверка (ТЕСТ GAME_OBJECT_TYPE a, ТЕСТ GAME_OBJECT_TYPE b, тест GAME_OBJECT_TYPE){
 если (a == test amp;amp; b == GAME_OBJECT_TYPE.player){
 верните true;
 } 
 иначе, если (a == GAME_OBJECT_TYPE.player amp;amp; b == тест){
 верните true;
 } 
 возвращает false;
 }

 общедоступное логическое значение - groundcollision(){ 
 вернуть столкновение с землей;
 }

 public void step() {
 /* 
 если (столкновение с землей)
 Log.d("COLLSN", "У нас столкновение с землей");*/
 //после логического завершения сбросьте переменные состояния 
 сброс ();
 }

 сброс приватной пустоты (){ 
 Столкновение с землей = false;
 }
}

Теперь вы создаете логическое значение прыжка в вашем плеере. Когда он подпрыгнет, установите для него значение true. Он больше не может прыгать, когда флаг имеет значение true. Проверяйте каждый кадр на предмет столкновения с землей с помощью collisionChecker.isGroundCollision()’. При возникновении столкновения снова установите player.jump = false.

Проблема здесь в том, что если у вас вертикальная площадка, игрок сможет прыгать по стене. Если игрок ударится головой о землю, он также сможет прыгать больше. Чтобы решить эти проблемы, вы проверяете ‘arg0.position’, чтобы узнать, где находилась точка соприкосновения по отношению к объекту игрока. Если контакт находится не под игроком, то он ударяется головой или ударяется о стену.

Комментарии:

1. Я вижу здесь две проблемы. Во-первых, приспособления Box2D могут одновременно касаться нескольких других приспособлений, поэтому сохранения логического значения недостаточно — вам нужно увеличивать и уменьшать количество соприкасающихся в данный момент. Кроме того, remove () следует использовать для уменьшения количества — в приведенном выше коде кажется, что groundCollision никогда не будет возвращено значение false .

2. Это решение работает, и вам не нужно считать столкновения. Перед вызовом ‘world.step()’ предположим, что ‘groundCollision’ имеет значение false. ‘ContactListener’ должен проверить, не столкнется ли игрок в этом цикле. Я делаю это в методе ‘preSolve’, чтобы установить для ‘groundCollision’ значение true. Теперь вы можете отключить прыжки для этого цикла и начать все сначала.