Исправление простого обнаружения столкновений с помощью двумерного движения на основе скорости - PullRequest
0 голосов
/ 21 февраля 2019

В этой демонстрации codePen вы можете перемещать квадрат "игрока" со стрелками, размещать свет с пробелом и должны быть остановлены, пересекая синие линии с любого направления, если их толкнуть в противоположном направлении.«player» использует переменные скорости x и y, чтобы создать движение и умножить их на -1 (+ некоторое значение), если обнаружено столкновение.
Проблема в том, что после отталкивания от стены «игрок» застревает в положении, когда возможно только движение назад от стены, при этом он застрял на перпендикулярной оси.(например, если стена находится сверху игрока, вы можете перемещаться только вниз, а не влево или вправо после удара о стену)
Теоретически, я бы хотел плавное обнаружение скользящего столкновения, когда игрок, застрявший в стене, будет медленно скользитьвниз влево или вправо в зависимости от того, нажата ли стрелка влево или вправо.(играя вокруг, я могу добиться этого, но всегда одно направление будет «течь», заставляя игрока скользить вниз в определенном направлении)
Я думал об использовании лучей или какого-то другого способа обнаружения попаданий, но, похоже, им требуется больше вычислительного времени, чем простопростой подход.Буду признателен за любой вклад и любые рекомендации по созданию масштабируемых обнаружений столкновений. Вот мой основной код для обнаружения движения и столкновений из демо-версии:

let xVelocity = 0;
let yVelocity = 0;
var blockedMapGrid = [[0,30],[0,50],[0,100],[0,150],[0,200],[0,250],
                     [50,0],[100,0],[150,0],[200,0],[250,0],[300,0]];


var animate = function() {



if (keyState[37]) {

    xVelocity -= 1;

}
if (keyState[38]) {

    yVelocity += 1;
}
if (keyState[39]) {
    xVelocity += 1;

}
if (keyState[40]) {
    yVelocity -= 1;

}

 for (var i = 0; i < blockedMapGrid.length; i++) {
     if (Math.abs(player.position.x - blockedMapGrid[i][0]) + 
     Math.abs(player.position.y - blockedMapGrid[i][1]) < 36) {
        xVelocity = -xVelocity * 1.2;
        yVelocity = -yVelocity * 1.2;


        console.log("Blocked by " + blockedMapGrid[i][0])
    };
}


player.position.x = player.position.x + xVelocity;
player.position.y = player.position.y + yVelocity;
yVelocity *= 0.80;
xVelocity *= 0.80;

camera.position.x = player.position.x;
camera.position.y = player.position.y;

requestAnimationFrame(animate);


renderer.render(scene, camera);
};

1 Ответ

0 голосов
/ 21 февраля 2019

Эта часть вашего детектора неверна:

Math.abs(player.position.x - blockedMapGrid[i][0]) + 
   Math.abs(player.position.y - blockedMapGrid[i][1]) < 36

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

Похоже, что вы делаете обнаружение с помощью Оси-ориентированной ограничительной рамки (AABB).В Интернете есть множество из ресурсов , как оптимизировать его.

Но общий подход был бы таким.Ваш сеточный массив должен состоять из блоков с (x,y,w,h) мерами.Может быть худым, длинным, квадратным, чем угодно.Предположим также, что у вашего игрока есть ограничивающий прямоугольник (player.x, player.y, player.w, player.h), затем

for (var i = 0; i < grid.length; i++) {
   if (player.x            < grid[i].x + grid[i].w && 
       player.x + player.w > grid[i].x             &&
       player.y            < grid[i].y + grid[i].h && 
       player.y + player.h > grid[i].y) {
   //collision detected! move player to previous known position
    break;
  }
}

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

Обновление

Другая проблема, возникающая из-за кода в вопросе, - это «отскок» или «застревание» после обнаружения столкновения.

Какэмпирическое правило, вы никогда не должны использовать velocity = -velocity после столкновения, также не убедившись, что персонаж возвращается в «чистоту», то есть ограничивающий прямоугольник игрока не перекрывается никакими препятствиями.В противном случае вы застрянете в бесконечном цикле collision? -> vel = -vel, pos += vel*t -> collision -> ... со скоростью, отскакивающей от отрицательной к положительной и обратно, не позволяя игроку выбраться из стены.

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

Другой способ - запомнить последнюю известную «хорошую» позицию и вернуть контроль над персонажем, только когда он возвращается на эту предыдущую позицию, возможно, после анимации или группы неконтролируемых движений.

Существуют более сложные способы, в основном включающие какую-то физическую эмуляцию, позволяющую персонажу отскочить от нескольких препятствий, при условии, что управляющие входы не преодолевают инерцию - например, автомобиль на скользкой дороге или лодка, которая бьет по нескольким деревьям.Но в любом случае, после того, как вы обнаружите столкновение и перед вызовом render () вы должны будете поместить персонажа в физически возможную позицию, иначе он будет классно «застрял в текстурах».

...