Отражение от стены: формула зеркала

Любой отскок — от стены, ракетки или наклонной плиты — это одна и та же формула отражения через нормаль.

Отражение вектора от поверхности — преобразование, при котором составляющая скорости вдоль нормали меняет знак, а вдоль поверхности сохраняется.

Формула отражения

Пусть мяч летит со скоростью $\vec v$ и ударяется о стену с единичной нормалью $\hat n$ (перпендикуляр к поверхности). Отражённая скорость $\vec v\,'$ вычисляется через скалярное произведение:

$$\vec v\,' = \vec v - 2(\vec v \cdot \hat n)\,\hat n.$$

Смысл прозрачен: $(\vec v\cdot\hat n)\,\hat n$ — это составляющая скорости вдоль нормали (та, что «вбивается» в стену). Мы вычитаем её дважды: первый раз убираем, второй — добавляем в обратную сторону. В итоге нормальная компонента меняет знак (мяч отскакивает), а касательная (вдоль стены) остаётся прежней (мяч скользит вдоль). Это закон «угол падения равен углу отражения», записанный одной строкой и работающий для стены под любым наклоном.

Проверим на простых стенах

def dot(a, b): return a[0]*b[0] + a[1]*b[1]
def reflect(v, n):           # n — единичная нормаль
    d = dot(v, n)
    return (v[0] - 2*d*n[0], v[1] - 2*d*n[1])

print("от вертикальной стены, нормаль (1,0):")
print("  (2,-3) ->", reflect((2, -3), (1, 0)))
print("от пола, нормаль (0,1):")
print("  (2,-3) ->", reflect((2, -3), (0, 1)))

Вывод:

от вертикальной стены, нормаль (1,0):
  (2,-3) -> (-2, -3)
от пола, нормаль (0,1):
  (2,-3) -> (2, 3)

От вертикальной стены инвертируется горизонтальная компонента ($v_x$ из $2$ стал $-2$), вертикальная не тронута. От пола наоборот: $v_y$ из $-3$ стал $+3$. Формула сама «понимает», какую компоненту разворачивать, потому что нормаль указывает направление удара.

Мяч в коробке

Соберём классическую сцену: мяч летает внутри квадрата $10\times10$ и отскакивает от стенок. Для осевых стен отражение сводится к смене знака соответствующей скорости.

x, y = 2.0, 3.0
vx, vy = 1.5, 1.0
dt = 1.0
for t in range(1, 9):
    x += vx*dt; y += vy*dt
    hit = ""
    if x < 0 or x > 10:
        vx = -vx; x = max(0, min(10, x)); hit += "x"
    if y < 0 or y > 10:
        vy = -vy; y = max(0, min(10, y)); hit += "y"
    print(f"t={t}  pos=({x:5.2f},{y:5.2f})  vel=({vx:+.1f},{vy:+.1f}) {hit}")

Вывод:

t=1  pos=( 3.50, 4.00)  vel=(+1.5,+1.0) 
t=2  pos=( 5.00, 5.00)  vel=(+1.5,+1.0) 
t=3  pos=( 6.50, 6.00)  vel=(+1.5,+1.0) 
t=4  pos=( 8.00, 7.00)  vel=(+1.5,+1.0) 
t=5  pos=( 9.50, 8.00)  vel=(+1.5,+1.0) 
t=6  pos=(10.00, 9.00)  vel=(-1.5,+1.0) x
t=7  pos=( 8.50,10.00)  vel=(-1.5,+1.0) 
t=8  pos=( 7.00,10.00)  vel=(-1.5,-1.0) y

На шаге $6$ мяч достиг правой стены ($x = 10$), и $v_x$ сменил знак — мяч пошёл влево. На шаге $8$ он коснулся потолка, и развернулась уже вертикальная скорость. Это и есть отражение в действии — сердце аркад вроде Pong и Breakout.

Как работает под капотом

Для осевых стен мы могли просто инвертировать одну компоненту, но общая формула через нормаль работает для любой ориентации стены — наклонной плиты, борта пинбола, грани полигона. Достаточно знать единичную нормаль поверхности. Заметьте важную деталь: после разворота скорости мы ещё и «возвращаем» мяч внутрь (max/min), потому что за шаг он мог чуть выйти за стену. Без этой коррекции мяч мог бы «прилипнуть» к стене и дребезжать, разворачиваясь каждый кадр. Аккуратная обработка проникновения — типичная забота коллизий, к которой мы вернёмся в разделе про столкновения.

Частые ошибки

  • Нормаль не единичная. Формула $\vec v - 2(\vec v\cdot\hat n)\hat n$ предполагает $|\hat n| = 1$; иначе масштаб отражения исказится.
  • Забыть вернуть тело из стены. Без коррекции позиции мяч «застревает» и дребезжит у стены.
  • Разворачивать обе компоненты сразу. При ударе о вертикальную стену меняется только $v_x$; разворот обеих — это отскок «назад», а не отражение.

Итог

  • Отражение: $\vec v\,' = \vec v - 2(\vec v\cdot\hat n)\hat n$, нормаль $\hat n$ единичная.
  • Нормальная компонента инвертируется, касательная сохраняется (угол падения = угол отражения).
  • Формула работает для стены любой ориентации, не только осевой.
  • После отскока нужно вернуть тело внутрь, иначе оно «прилипнет» к стене.
Проверьте себя
1. Какая компонента скорости меняется при отражении от стены?
AКасательная (вдоль стены)
BНормальная (вдоль нормали) — она инвертируется
CОбе одинаково
DНикакая
2. Что требуется от нормали в формуле v' = v − 2(v·n)n?
AЧтобы она была нулевой
BЧтобы она была единичной (|n|=1)
CЧтобы она совпадала со скоростью
DЧтобы она была горизонтальной
3. Зачем после отскока возвращать тело внутрь границ (max/min)?
AДля красоты
BИначе тело, выйдя за стену, будет дребезжать, разворачиваясь каждый кадр
CЧтобы сохранить энергию
DЧтобы ускорить цикл