Камера от первого лица
Камера
Камера в Ursina — тоже Entity: camera. Её можно двигать, поворачивать и делать дочерней.
Игра от первого лица устроена так: есть невидимый «игрок» на высоте глаз, камера — его ребёнок. Поворот налево-направо (rotation_y) крутят у игрока — вместе с ним поворачивается и направление ходьбы. Наклон вверх-вниз (rotation_x) — только у камеры, чтобы игрок не «ложился» на пол.
player = Entity(y=1.8) # высота глаз
camera.parent = player
camera.position = (0, 0, 0)
mouse.locked = True # курсор спрятан и не упирается в края
def update():
player.rotation_y += mouse.velocity[0] * 40
camera.rotation_x -= mouse.velocity[1] * 40
camera.rotation_x = clamp(camera.rotation_x, -90, 90)
mouse.velocity— насколько сдвинулась мышь за кадр;- положительный
rotation_x— взгляд вниз, поэтому движение мыши вверх вычитают; - наклон ограничивают [-90°, 90°], иначе камера перевернётся.
Ходьба по взгляду
Если игрок повёрнут на угол yaw, то по полу:
- «вперёд» — (sin yaw, cos yaw);
- «вправо» — (cos yaw, -sin yaw).
r = math.radians(player.rotation_y)
fwd = held_keys["w"] - held_keys["s"]
side = held_keys["d"] - held_keys["a"]
x = fwd * math.sin(r) + side * math.cos(r)
z = fwd * math.cos(r) - side * math.sin(r)
Направление нормализуют, чтобы по диагонали не идти быстрее. У каждой Entity есть и готовые векторы forward и right — с ними то же самое: player.position += (player.forward * fwd + player.right * side).normalized() * speed * time.dt.
Готовый контроллер
В Ursina уже есть игрок от первого лица — с ходьбой, мышью, прыжком и гравитацией:
from ursina.prefabs.first_person_controller import FirstPersonController
player = FirstPersonController(speed=5, mouse_sensitivity=Vec2(40, 40))
Ему нужен пол с коллайдером: Entity(model="plane", scale=40, collider="box"). В последних темах курса вы соберёте игру именно на нём, а пока полезно понимать, что у него внутри.
Поле зрения
Враг «видит» игрока, если тот близко и попадает в конус зрения:
- расстояние не больше дальности (сравнивают квадраты — без корня);
- угол на игрока:
math.degrees(math.atan2(dx, dz))— так же, какrotation_y; - разница с углом взгляда, приведённая к [-180, 180), по модулю не больше половины угла обзора.
Если ещё нужно, чтобы между врагом и игроком не было стен, добавляют луч — о нём в следующей теме.
Итог
- камера — Entity
camera; от первого лица:camera.parent = player,mouse.locked = True; yaw— у игрока,pitch— у камеры, ограничен [-90, 90]; мышь вверх —pitchуменьшается;- вперёд (sin, cos), вправо (cos, -sin);
forward,right; FirstPersonController; поле зрения — расстояние и угол с половинойfov.