Topic 5 of 6

Course materials are currently available in Russian only.

Столкновения и лучи

Коллайдеры

Чтобы Entity участвовала в столкновениях, ей дают коллайдер — упрощённую форму для расчётов:

wall = Entity(model="cube", collider="box", position=(3, 1, 0), scale=(1, 2, 5))
ball = Entity(model="sphere", collider="sphere")
level = Entity(model="level.obj", collider="mesh")     # точная форма модели — медленнее

Проверить, пересекается ли объект с другими: hit = player.intersects() — hit.hit будет True, а hit.entity — с кем столкнулись.

Коробки

Для коробок (collider="box") проверка простая: две коробки пересекаются, если по каждой оси расстояние между центрами меньше полусуммы размеров:

def overlap(a, b):
    return all(abs(a.position[i] - b.position[i]) < (a.scale[i] + b.scale[i]) / 2 for i in range(3))

Хватит одной оси без перекрытия — и пересечения нет. Например, если игрок прыгнул выше ящика, по y они разошлись.

Скольжение вдоль стен

Если игрок шагает по диагонали в стену и шаг целиком отменяется, он «прилипает» к стене. Шаг делят по осям — так же, как в 2D:

if not is_wall(x + dx, z):
    x += dx
if not is_wall(x, z + dz):      # уже с новым x
    z += dz

Лучи

Луч — полупрямая из точки в заданном направлении. В Ursina:

hit = raycast(player.position, player.forward, distance=10, ignore=(player,))
if hit.hit:
    print(hit.entity, hit.distance, hit.world_point)

Лучом проверяют, стоит ли игрок на полу (луч вниз), есть ли стена впереди, видит ли враг игрока и куда попала пуля. FirstPersonController внутри использует именно лучи. raycast работает по коллайдерам — поэтому без collider= объект для лучей невидим.

Для коробок, выровненных по осям, пересечение с лучом ищут методом плит: по каждой оси коробка — «плита» между двумя плоскостями. Луч входит в плиту и выходит из неё в моменты t = (lo - o) / d и t = (hi - o) / d — меньшее из двух чисел — вход. Луч задевает коробку, если он побывал во всех трёх плитах одновременно: самый поздний вход раньше самого раннего выхода.

Итог

  • collider="box" / "sphere" / "mesh", entity.intersects();
  • коробки пересекаются, если перекрываются по всем трём осям; касание — нет;
  • скольжение: шаг по x, потом по z с новым x;
  • raycast(origin, direction, distance, ignore) → hit, entity, distance; метод плит для коробок.