Додавання реалістичних поворотів
Наступний крок - додати реалістичні вигнуті повороти для наших одиниць, щоб вони не з'являлися різко змінювати напрямок кожного разу, коли потрібно повернути. Просте рішення передбачає використання шпонки, щоб згладити круті кути на повороти. Хоча це вирішує деякі естетичні проблеми, все-таки призводить до фізично дуже нереального руху для більшості одиниць. Наприклад, це може змінити різкий поворот танка на тугу криву, але вигнутий поворот все-таки буде набагато жорсткішим, ніж реально виконати танк.
Для кращого рішення перше, що нам потрібно знати, - це радіус повороту для нашого пристрою. Повернути радіус - досить проста концепція: якщо ви знаходитесь на великій парковці в автомобілі, і поверніть колесо вліво, наскільки воно піде, і продовжуйте рух по колу, радіус цього кола - ваш поворот. радіус. Радіус повороту Volkswagen Beetle буде істотно меншим, ніж у великого позашляховика, а радіус повороту людини буде значно меншим, ніж у великого, що лежить в лісі.
Скажімо, ви знаходитесь в якійсь точці (початок) і вказували в певному напрямку, і вам потрібно дістатися до якоїсь іншої точки (пункту призначення), як проілюстровано на малюнку 5. Найкоротший шлях можна знайти, повернувши ліворуч, наскільки ви можна, йдучи по колу, поки вас прямо не вкажуть на пункт призначення, а потім рухаєтесь вперед, або повернувши праворуч і зробивши те саме.
На малюнку 5 найкоротшим маршрутом є чітко зелена лінія внизу. Цей шлях виявляється досить простим для обчислення через деякі геометричні відношення, проілюстровані на рисунку 6.
Спочатку обчислюємо розташування точки P, яка є центром нашого поворотного кола і завжди радіусом r від початкової точки. Якщо ми повертаємо направо від нашого початкового напрямку, це означає, що P знаходиться під кутом (початкове_направлення - 90) від початку, так що:
angleToP = initial_direction - 90
P.x = Origin.x + r * cos(angleToP)
P.y = Origin.y + r * sin(angleToP)
Тепер, коли ми знаємо розташування центральної точки P, ми можемо обчислити відстань від P до пункту призначення, показане як h на діаграмі:
dx = Destination.x - P.x
dy = Destination.y - P.y
h = sqrt(dx*dx + dy*dy)
У цей момент ми також хочемо перевірити, чи не місце призначення не в колі, бо якби ми ніколи не могли його дістати:
if (h < r)
return false
Тепер ми можемо обчислити довжину відрізка d, оскільки ми вже знаємо довжини інших двох сторін правильного трикутника, а саме h і r. Ми також можемо визначити кут за відношенням прямокутного трикутника:
d = sqrt(h*h - r*r)
theta = arccos(r / h)
Нарешті, щоб визначити точку Q, в якій слід залишити коло і почати по прямій, нам потрібно знати загальний кут +, і його легко визначити як кут від P до місця призначення:
phi = arctan(dy / dx) [offset to the correct quadrant]
Q.x = P.x + r * cos(phi + theta)
Q.y = P.y + r * sin(phi + theta)
Наведені вище розрахунки представляють шлях, що повертає праворуч. Лівий шлях можна обчислити точно так само, за винятком того, що ми додаємо 90 до початкового_направлення для обчислення angleToP, а пізніше використовуємо - замість +. Обчисливши обидва, ми просто бачимо, який шлях коротший, і використовуємо той.
Під час реалізації цього алгоритму та подальших алгоритмів ми використовуємо структуру даних, яка зберігає до чотирьох чітких "ліній сегментів", кожен з яких є прямим або кривим. Для описаних тут кривих шляхів використовуються лише два сегменти: дуга, за якою пряма лінія. Структура даних містить члени, які визначають, чи є сегмент дугою чи прямою, довжина сегмента та його вихідне положення. Якщо відрізок є прямою, структура даних також визначає кут; для дуг вказується центр кола, початковий кут на колі та загальний радіан, охоплений дугою.
Після того, як ми обчислили кривий шлях, необхідний для проходження між двома точками, ми можемо легко обчислити наше положення та напрямок у будь-який момент часу, як показано у Лістингу 2.
СПИСОК 2. Обчислення положення та орієнтації в певний час.
distance = unit_speed * elapsed_time
loop i = 0 to 3:
if (distance < LineSegment[i].length)
// Unit is somewhere on this line segment
if LineSegment[i] is an arc
//determine current angle on arc (theta) by adding or
//subtracting (distance / r) to the starting angle
//depending on whether turning to the left or right
position.x = LineSegment[i].center.x + r*cos(theta)
position.y = LineSegment[i].center.y + r*sin(theta)
//determine current direction (direction) by adding or
//subtracting 90 to theta, depending on left/right
else
position.x = LineSegment[i].start.x
+ distance * cos(LineSegment[i].line_angle)
position.y = LineSegment[i].start.y
+ distance * sin(LineSegment[i].line_angle)
direction = theta
break out of loop
else
distance = distance - LineSegment[i].length