Я реализовал простую процедуру обнаружения столкновений, используя AABB между моим основным игровым спрайтом и различными платформами (см. Код ниже). Это прекрасно работает, но сейчас я ввожу гравитацию, чтобы заставить моего персонажа упасть, и это выявило некоторые проблемы с моей программой на CD.
Я думаю, что суть в том, что моя подпрограмма на компакт-диске перемещает спрайт назад вдоль оси, в которой он проник наименьшее количество в другой спрайт. Так что, если он находится больше по оси Y, чем по X, то он переместит его обратно вдоль оси X.
Тем не менее, теперь мой (герой) спрайт теперь падает со скоростью 30 пикселей на кадр (это экран высотой 1504 - мне нужно, чтобы он падал так быстро, как я хочу, чтобы попытаться симулировать «нормальную» гравитацию, любой медленнее просто выглядит странно ) Я получаю эти проблемы. Я попытаюсь показать, что происходит (и что, я думаю, вызывает это - хотя я не уверен) с несколькими картинками: (Код ниже изображения).
Буду признателен за некоторые предложения о том, как обойти эту проблему.
Для пояснения, на приведенном выше правом рисунке, когда я говорю, что позиция исправлена «неправильно», это, возможно, немного вводит в заблуждение. Сам код работает правильно для того, как он написан, или, иначе говоря, сам алгоритм, если он ведет себя так, как я ожидал, но мне нужно изменить его поведение, чтобы остановить эту проблему, надеюсь, это прояснит мои комментарии на картинке ,
Мой код
public boolean heroWithPlatforms(){
//Set Hero center for this frame
heroCenterX = hero.xScreen+(hero.quadWidth/2);
heroCenterY = hero.yScreen+(hero.quadHeight/2);
//Iterate through all platforms to check for collisions
for(x=0;x<platformCoords.length;x+=2){
//Set platform Center for this iteration
platformCenterX = platformCoords[x]+(platforms.quadWidth/2);
platformCenterY = platformCoords[x+1]+(platforms.quadHeight/2);
// the Dif variables are the difference (absolute value)
// of the center of the two sprites being compared (one for X axis difference
//and on for the Y axis difference)
difX = Math.abs(heroCenterX-platformCenterX);
difY = Math.abs(heroCenterY-platformCenterY);
//If 1
//Check the difference between the 2 centers and compare it to the vector (the sum of
//the two sprite's widths/2. If it is smaller, then the sprites are pverlapping along
//the X axis and we can now proceed to check the Y axis
if (difX<vecXPlatform){
//If 2
//Same as above but for the Y axis, if this is also true, then we have a collision
if(difY<vecYPlatform){
//we now know sprites are colliding, so we now need to know exactly by how much
//penX will hold the value equal to the amount one sprite (hero, in this case)
//has overlapped with the other sprite (the platform)
penX = vecXPlatform-difX;
penY = vecYPlatform-difY;
//One sprite has penetrated into the other, mostly in the Y axis, so move sprite
//back on the X Axis
if (penX < penY){hero.xScreen-=penX*(heroCenterX-platformCenterX>=0 ? -1 : 1);}
//Sprite has penetrated into the other, mostly in the X asis, so move sprite
//back on the Y Axis
else if (penY < penX) {hero.yScreen-=penY*(heroCenterY-platformCenterY>=0 ? -1 : 1);}
return true;
}//Close 'If' 2
} //Close 'If' 1
}
//Otherwise, no collision
return false;
}
источник
//One sprite has penetrated into the other, mostly in the Y axis, so move sprite //back on the X Axis
Ответы:
во время моих экспериментов с HTML5 canvas и AABB я нашел именно то, что вы испытываете. Это произошло, когда я попытался сделать платформу из соседних блоков размером 32x32 пикселей.
Решения, которые я пробовал по порядку моего личного предпочтения
1 - Разделить движения по оси
Мой текущий, и я думаю, что я продолжу свою игру с ним. Но не забудьте проверить, что я называю Phantom AABB, если вам нужно быстрое решение без необходимости много менять ваш текущий дизайн.
В моем игровом мире очень простая физика. Нет понятия о силах (пока), только смещение. Гравитация - это постоянное смещение вниз. Нет ускорения (пока).
Смещения применяются по оси. И в этом состоит это первое решение.
Каждый игровой кадр:
move_x устанавливается в соответствии с обработкой ввода, если не нажата клавиша со стрелкой, то move_x = 0. Значения ниже нуля означают влево, в противном случае вправо.
Обработайте все остальные движущиеся спрайты и определите значения их компонента «ходы», у них тоже есть компонент «ходы».
Примечание: после обнаружения столкновения и реакции, компонент перемещения должен быть установлен в ноль, оба свойства x и y, потому что на следующем тике гравитация будет добавлена снова. Если вы не выполните сброс, вы закончите с ходами в два раза больше желаемой силы тяжести, а на следующем тике это будет три раза.
Затем введите код применения смещения и обнаружения столкновения.
Компонент move на самом деле не является текущей позицией спрайта. Спрайт еще не переместился, даже если Move.x или y изменились. Компонент «ходы» - это способ сохранить смещение, которое будет применено к позиции спрайта в правильной части игрового цикла.
Сначала обработайте ось y (move.y). Применить move.y к sprite.position.y. Затем примените метод AABB, чтобы увидеть, перекрывает ли обрабатываемый движущийся спрайт платформу AABB (вы уже поняли, как это сделать). Если он перекрывается, переместите спрайт обратно по оси y, применяя пенетрации.y. Да, в отличие от моего другого ответа, теперь этот метод развитого движения игнорирует проникающий.x для этого шага процесса. И мы используем пенетрация. Даже если она больше, чем пенетрация, мы даже не проверяем это.
Затем обработайте ось X (move.x). Примените move.x к sprite.position.x. И сделать наоборот, чем раньше. На этот раз вы будете перемещать спрайт только по горизонтальной оси, применяя пенетрации.x. Как и прежде, вам все равно, будет ли пенетрация.х меньше или больше, чем пенетрация.
Суть в том, что если спрайт не движется и изначально не сталкивался, то он останется таким же в следующем кадре (sprite.moves.x и y равны нулю). Особый случай, когда другой игровой объект волшебным образом телепортируется в положение, которое перекрывает обработанный спрайт, - это то, что я рассмотрю позже.
Когда спрайт начинает двигаться. Если он движется только по оси x, то нас интересует, проникает ли он куда-нибудь слева или справа. Поскольку спрайт не движется вниз, и мы знаем это, потому что свойство y компонента ходов равно нулю, мы даже не проверяем вычисленное значение для пенетрации.y, мы видим только в пенетрации.x. Если в оси движения существует проникновение, мы применяем коррекцию, в противном случае мы просто позволяем спрайту двигаться.
Как насчет диагонального смещения, не обязательно под углом 45 градусов?
Предлагаемая система справится с этим. Вам нужно только обработать каждую ось отдельно. Во время вашей симуляции. Различные силы применяются к спрайту. Даже если ваш двигатель еще не разбирается в силах. Эти силы приводят к произвольному смещению в 2D-плоскости, это смещение представляет собой 2D-вектор в любом направлении и любой длины. Из этого вектора вы можете извлечь ось y и ось x.
Что делать, если вектор смещения слишком велик?
Вы не хотите этого:
Поскольку наши новые движения с применением логики сначала обрабатывают ось, а затем другую, код столкновения может привести к тому, что большое смещение может закончиться, как большая буква L, это не будет правильно определять столкновения с объектами, которые пересекают ваш вектор смещения.
Решение состоит в том, чтобы разделить большие перемещения небольшими шагами, в идеале ширину или высоту спрайта, или половину ширины или высоты спрайта. Чтобы получить что-то вроде этого:
Как насчет телепортации спрайтов?
Если вы представляете телепортацию как большое смещение, используя тот же компонент перемещений, что и обычное перемещение, то проблем нет. Если вы этого не сделаете, то вам нужно придумать способ пометить телепортирующийся спрайт, который будет обработан кодом столкновения (добавление в список, предназначенный для этой цели?), Во время кода ответа вы можете напрямую сместить стоящий спрайт, который был в путь, когда произошла телепортация.
2 - Фантом ААББ
Имейте вторичную AABB для каждого спрайта, на который влияет сила тяжести, которая начинается у основания спрайта, имеет одинаковую ширину спрайта AABB и высоту в 1 пиксель.
Идея заключается в том, что этот фантом AABB всегда сталкивается с платформой ниже спрайта. Только когда этот фантомный AABB не перекрывает что-либо, гравитация может быть применена к спрайту.
Вам не нужно рассчитывать вектор проникновения для фантомного AABB и платформы. Вас интересует только простой тест на столкновение: если он перекрывает платформу, гравитация не может быть применена. Ваш спрайт может иметь логическое свойство, которое сообщает, находится ли он над платформой или в воздухе. Вы находитесь в воздухе, когда ваш фантом AABB не сталкивается с платформой под игроком.
Эта:
Становится что-то вроде этого:
Очень просто и надежно.
Вы оставляете свою реализацию AABB как есть.
Фантомная AABB может иметь специальный цикл, который их обрабатывает, который проверяет только простые столкновения и не тратит время на вычисление вектора проникновения. Этот цикл должен быть перед нормальным коллизией и кодом ответа. Вы можете применить гравитацию во время этой петли, для спрайтов в воздухе примените гравитацию. Если фантомный AABB сам по себе является классом или структурой, он может иметь ссылку на спрайт, которому он принадлежит.
Это очень похоже на другое предлагаемое решение, где вы проверяете, прыгает ли ваш игрок. Я дал возможный способ обнаружения, если игрок стоит над платформой.
источник
Я бы объединил плитки платформы в единую платформу.
Например, предположим, что вы берете 3 плитки с картин и объединяете их в одну сущность, у вашей сущности будет поле столкновения:
Использование этого для столкновения даст вам y как наименее проникающую ось на большей части платформы, в то же время позволяя вам иметь конкретные плитки внутри платформы.
источник
Подобные проблемы характерны для новых методов обнаружения столкновений.
Я не знаю слишком много о вашей текущей настройке столкновения, но вот как это должно происходить:
Прежде всего, сделайте эти переменные:
Во-вторых, сделайте таймер для расчета дельты, чтобы повлиять на скорость движения объекта.
В-третьих, сделайте это:
Если вы сделаете это, не должно быть никаких проблем. Надеюсь это поможет!
источник