Topic 3 of 6

Course materials are currently available in Russian only.

Часы и движение

Часы pyglet

Игровой цикл в pyglet крутит pyglet.app.run(). Чтобы что-то происходило каждый кадр, функцию ставят в расписание:

def update(dt):
    ball.x += 200 * dt          # 200 пикселей в секунду

pyglet.clock.schedule_interval(update, 1 / 60)   # примерно 60 раз в секунду

dt — сколько секунд на самом деле прошло с прошлого вызова. Компьютер может подтормаживать, и тогда dt больше. Если всегда умножать скорость на dt, объекты движутся с одинаковой скоростью на любом компьютере.

Разовые и повторяющиеся события

pyglet.clock.schedule_once(start_level, 3)       # один раз через 3 секунды
pyglet.clock.schedule_interval(blink, 0.5)       # каждые полсекунды
pyglet.clock.unschedule(blink)                   # снять с расписания

Функция, которую вызывают часы, всегда получает первым аргументом dt — даже если он ей не нужен.

Внутри функции можно снять её саму с расписания — так делают «мигни 5 раз и остановись».

Свои часы

pyglet.clock.Clock — отдельные часы. Им можно передать свою функцию времени — это удобно для проверки: время идёт только тогда, когда вы его двигаете.

now = [0.0]
clock = pyglet.clock.Clock(time_function=lambda: now[0])
clock.schedule_interval(blink, 0.5)
now[0] = 1.0
clock.tick()          # выполнить всё, чему пора

Основные часы программы — pyglet.clock.get_default(); функции pyglet.clock.schedule_… работают именно с ними.

Фиксированный шаг

Для физики удобнее шаг постоянной длины, например 20 мс. Время кадра копят, а потом «тратят» целыми шагами:

STEP = 20      # мс
acc = 0

def update(dt):
    global acc
    acc += round(dt * 1000)
    steps = 0
    while acc >= STEP and steps < 5:      # не больше 5 шагов за кадр
        physics_step()
        acc -= STEP
        steps += 1
    if acc >= STEP:                       # не успели — лишнее отбрасываем
        acc %= STEP

Предел шагов защищает от «спирали смерти»: если физика не успевает, кадры становятся длиннее, шагов нужно ещё больше — и игра зависает навсегда.

Итог

  • pyglet.clock.schedule_interval(f, interval), schedule_once(f, delay), unschedule(f);
  • функция получает dt; движение — скорость * dt;
  • pyglet.clock.Clock(time_function=...) и tick() — свои часы;
  • фиксированный шаг: копить время, тратить целыми шагами, ограничить число шагов за кадр.