Столкновения и лучи
Коллайдеры
Чтобы 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; метод плит для коробок.