Тема: Колізія та скалярний добуток між нормаллю та швидкістю
Надибав деякий код, котрий управляє фізичними об'єктами, колізія там також мається. І от там такий код
void Movement(Vector2 move, bool yMovement)
{
float distance = move.magnitude; // витягуємо з вектору його довжину
if (distance > minMoveDistance) // якщо довжина більша за мінімальну можливу дистанцію руху, то
{
// кастуємо колайдер в напрямку руху, на відстань - дастанція руху плюс відступ (shellRadius)
int count = rb2d.Cast(move, contactFilter, hitBuffer, distance + shellRadius);
hitBufferList.Clear(); // очищаємо список колізій
for (int i = 0; i < count; i++)
{
//заносимо в список колізії, котрі ми отримали після кастування колайдера
hitBufferList.Add(hitBuffer[i]);
}
for (int i = 0; i < hitBufferList.Count; i++) // для кожної колізії зі списку
{
// отримуємо нормаль поверхні, об котру стукнувся закастований колайдер
Vector2 currentNormal = hitBufferList[i].normal;
// якщо нормаль по Y більше за деяке значення, то
if (currentNormal.y > minGroundNormalY)
{
grounded = true; // ми на землі
if (yMovement) // якщо обраховуємо рух по Y
{
groundNormal = currentNormal; // встановлюємо нормаль поверхні землі
currentNormal.x = 0; // обнуляємо X поточної нормалі
}
}
// отримуємо скалярний добуток між поточним вектором руху та поточною нормаллю
float projection = Vector2.Dot(velocity, currentNormal);
//якщо скалярний добуток менше нуля, тобто, velocity та currentNormal дивляться в дещо протилежні сторони, тобто, кут між ними більше 90 градусів
if (projection < 0)
{
// віднімаємо від поточного вектора руху поточну нормаль помножену на скалярний добуток
velocity = velocity - projection * currentNormal;
}
// віднімаємо від дистанції між початком кастування колайдеру та точки колізії відступ
float modifiedDistance = hitBufferList[i].distance - shellRadius;
// якщо отримане вище значення менше довжини вектору move, використовуємо вище отримане значення в якості дистанції
distance = modifiedDistance < distance ? modifiedDistance : distance;
}
}
// обраховуємо нову позицію об'єкту на основі отриманих вище значень
rb2d.position = rb2d.position + move.normalized * distance;
}
Мене цікавить частина з обрахуванням скалярного добутка.
Ми там обраховуємо скалярний добуток між вектором руху та нормаллю до поверхні об котру стукнувся колайдер, після чого, якщо результат менше нуля, то ми віднімаємо від поточного вектора руху добуток між скалярним добутком та нормаллю, і от я не можу доперти, як це працює.
Наприклад, якщо ми пригаємо вверх, і вектор руху стає (0; 100), і б'ємося голівкою об стелю з нормаллю (0; -1), то скалярний добуток буде -100, після чого ми множимо його на нормаль і отримуємо (0; 100), і віднімаємо це від вектору руху, в результаті наша швидкість змінються на (0; 0). Тут зрозуміло.
Але якщо наша стеля під кутом 45 градусів, і має нормаль ~ (0.7, -0.7), то скалярний добуток буде -70, і в результаті від вектора руху треба буде відняти (0; -70 * -0.7) => (0 ; 49), тобто вектор руху змінюється на (0; 51).
Якщо в прикладі з першою стелею швидкість руху по Y просто обнуляється (що і має бути, коли ми стукаємося головою об стелю), то в другому випадку наша швидкість просто зменшується, і ми все одно впираємося головою в стелю деякий час.
Тут вже можна помітити, що найбільше швидкість спадає, коли вектор руху та нормаль поверхні протилежні, але чим ближче кут між ними до 90 градусів, тим менше значення віднімається від вектору руху, що не має сенсу, в моєму розумінні, адже в такому разі персонаж буде ще декілька моментів впиратися головою в стелю, після чого просто впаде вниз, хоча оте впирання головою в стелю тут непотрібне, він мав би відразу падати вниз, як тільки гепнеться головою о стелю.
Чому воно таке? Це проста забаганка автора коду, чи воно має якусь практичну користь?