Движение и повороты
Каждый кадр — update()
Если в программе есть функция update(), Ursina вызывает её каждый кадр. То же — метод update(self) у своего класса-наследника Entity.
from ursina import *
app = Ursina()
player = Entity(model="cube", color=color.orange)
def update():
speed = 5 * time.dt # 5 единиц в секунду
player.x += (held_keys["d"] - held_keys["a"]) * speed
player.z += (held_keys["w"] - held_keys["s"]) * speed
def input(key): # один раз в момент нажатия
if key == "space":
player.y += 1
app.run()
time.dt— длительность кадра в секундах. Скорость ×time.dt— шаг, одинаковый по скорости на любом компьютере.held_keys["w"]— 1, если клавиша зажата, иначе 0.input(key)получает нажатия:"space","a","left mouse down", а отпускание —"a up".
Векторы
Vec3 — тройка чисел: положение или направление.
d = target.position - player.position # вектор «от игрока к цели»
print(d.length()) # расстояние
direction = d.normalized() # то же направление, длина 1
player.position += direction * 5 * time.dt
print(distance(player, target)) # расстояние между Entity
Чтобы не проскочить цель, сравнивают расстояние с длиной шага: если ближе — встают ровно в цель.
Повороты
rotation_y — поворот вокруг вертикальной оси в градусах. Угол «закольцован»: 370° — то же, что 10°. Чтобы повернуться по кратчайшему пути, разницу углов приводят к отрезку [-180, 180):
diff = (target - current + 180) % 360 - 180 # от −180 до 180
Знак diff подсказывает сторону поворота, а модуль — сколько осталось. Угол на цель по горизонтали: math.degrees(math.atan2(dx, dz)).
Повернуть Entity лицом к точке можно и готовым методом: tower.look_at(target). А «вперёд» для объекта — entity.forward, «вправо» — entity.right.
Маршруты
Патруль по точкам — это движение к цели, только целей несколько и они идут по кругу. Если за кадр объект проходит больше, чем осталось до точки, остаток шага нужно потратить на путь к следующей — иначе объект будет «спотыкаться» на каждой точке.
Итог
update()каждый кадр,time.dt;held_keys[...]— зажата ли клавиша;input(key)— нажатия;Vec3:length(),normalized(),distance(a, b); не проскакивать цель;- углы:
(target - current + 180) % 360 - 180, итог —% 360; - маршрут по кругу: остаток шага переносится на следующий отрезок.