Гузариш ба мундариҷа
Мавзӯи 3 аз 6

Соат ва ҳаракат

Чаро вақтро чен мекунем

Ҳаракати тӯб, ишораи чашмакзан ва оғози саҳнаи навбатӣ аз вақт вобастаанд. Агар объектро дар ҳар кадр ба миқдори доимии пиксел кӯчонем, дар компютери тез бозӣ ҳам тезтар мешавад. Вақт имкон медиҳад рафторро бо воҳидҳои фаҳмо баён кунем: пиксел дар сония ва сонияҳои интизорӣ.

Дар pyglet давраи рӯйдодҳо дар дохили pyglet.app.run() кор мекунад ва соат муайян мекунад, ки кадом функсияро бояд даъват кард. Кашидан ва нав кардани ҳолатро ҷудо кардан қулай аст: on_draw саҳнаи тайёрро нишон медиҳад, функсияи навсозӣ бошад координатаҳо ва ҳисобкунакҳоро тағйир медиҳад.

1. Маънои dt

dt фосилаи вақт бо сония аст, ки соат ба функсия медиҳад. Барои функсияи такрорӣ он вақти аз даъвати пешинаи худи функсия гузашта аст, на ҳатман давомнокии як кадри кашидашуда. Дархости 60 даъват дар сония фосилаи дақиқи 1 / 60-ро кафолат намедиҳад.

Масофа ба суръат зарби вақт баробар аст. Ҳаракатро бе равзана месанҷем: суръат 80 пиксел дар сония, фосилаҳо гуногун ва вақти умумӣ ним сония аст.

speed = 80
x = 10
for dt in (0.1, 0.25, 0.15):
    x += speed * dt
    print(f"{x:.1f}")

Натиҷа:

18.0
38.0
50.0

Новобаста аз тақсими ним сония ба даъватҳо объект 40 пиксел гузашт. Координатаҳои касриро байни навсозиҳо нигоҳ доред. Агар ҳар бор x = int(x + speed * dt) иҷро кунед, ҳаракатҳои хурд гум мешаванд; беҳтар аст танҳо натиҷаи намоиширо гирд кунед.

2. Ҷадвал ва соати алоҳида

Ҷадвал функсияҳо ва вақти даъвати навбатии онҳоро нигоҳ медорад. schedule_interval(update, 0.5) такрорро тақрибан ҳар ним сония сабт мекунад. Худи функсияи update бе қавс дода мешавад: онро баъдтар соат иҷро мекунад.

Барои санҷиш Clock-и алоҳида месозем. time_function вақти ҷориро бармегардонад ва рӯйхати now имкон медиҳад онро дастӣ тағйир диҳем. tick() соатро нав карда, рӯйдодҳои омодаро иҷро мекунад. Ин соат аз вақти воқеии компютер мустақил аст.

import pyglet
now = [0.0]
clock = pyglet.clock.Clock(time_function=lambda: now[0])
def update(dt):
    print(f"{now[0]:.2f} {dt:.2f}")
clock.schedule_interval(update, 0.5)
for t in (0.25, 0.5, 0.75, 1.0):
    now[0] = t
    clock.tick()

Натиҷа:

0.50 0.50
1.00 0.50

Tick ҳар 0.25 сония мешавад, вале функсия танҳо дар 0.5 ва 1.0 даъват мешавад. Сутуни аввал вақти ҷории мо, дуюм dt-и гирифташуда аст. Бозии одӣ соати асосиро истифода мебарад: pyglet.clock.schedule_interval(...) бо pyglet.clock.get_default() кор мекунад ва давраи барнома онро хизмат мерасонад.

3. Даъвати ба таъхир гузошташуда

schedule_once(f, delay) баъди таъхир як рӯйдод илова мекунад. Он барои оғози сатҳ, пинҳон кардани огоҳӣ ё иваз кардани саҳна қулай аст. Функсия ҳатто дар ҳолати истифода набурдан ҳам dt мегирад.

import pyglet
now = [0.0]
clock = pyglet.clock.Clock(time_function=lambda: now[0])
calls = []
def finish(dt):
    calls.append(round(dt, 2))
clock.schedule_once(finish, 0.75)
for t in (0.5, 0.75, 1.0):
    now[0] = t
    clock.tick()
print(calls)

Натиҷа:

[0.75]

Дар рӯйхат як унсур аст, гарчанде tick() се бор даъват шудааст. Ин вақти ба функсияи якдафъаина додашуда аст, на шумораи иҷрои он. Агар барнома дертар бедор шавад, даъват ҳам дертар мешавад: ҷадвал таймери алоҳидаи дақиқи низоми амалкунанда нест.

4. Бекор кардани рӯйдод

unschedule(f) функсияро аз ҷадвал мегирад. Ҳамон объекти функсия лозим аст, ки сабт шуда буд. Бекоркунӣ дар дохили худи коркардкунанда ҳам иҷозат аст; баъд аз он дигар даъватҳои ин функсия ба нақша гирифта намешаванд.

import pyglet
now = [0.0]
clock = pyglet.clock.Clock(time_function=lambda: now[0])
calls = []
def update(dt):
    calls.append(now[0])
clock.schedule_interval(update, 0.25)
now[0] = 0.25
clock.tick()
clock.unschedule(update)
now[0] = 0.5
clock.tick()
print(calls)

Натиҷа:

[0.25]

Дар tick-и дуюм рӯйхат дигар пур нашуд. Агар lambda dt: update(dt)-ро сабт карда, баъд unschedule(update) гӯед, lambda бекор намешавад: инҳо объектҳои гуногунанд. Lambda-ро дар тағйирёбанда нигоҳ доред ё бевосита функсияи номдорро сабт кунед.

5. Қадами собит ва бақияи вақт

Барои аниматсияи одӣ speed * dt кофист. Физикаи ададӣ ба дарозии қадам ҳассостар аст: қадами калон метавонад бархӯрдро аз даст диҳад. Қадами собит маънои онро дорад, ки симулятсия ҳамеша бо фосилаи яксон, масалан 10 мс, пеш меравад, вале басомади кашидан тағйир ёфта метавонад.

Вақти истифоданашуда дар аккумулятор ҷамъ мешавад. Дар зер танҳо ҳисоб аст: чанд қадами пурра ҷамъ шудааст ва чанд миллисония мондааст. divmod ҳосили тақсим ва бақияро бармегардонад; мисол худи физикаро иҷро намекунад.

acc = 0
for elapsed_ms in (7, 18, 8):
    acc += elapsed_ms
    count, acc = divmod(acc, 10)
    print(count, acc)

Натиҷа:

0 7
2 5
1 3

Баъди кадри аввал 7 мс ҳанӯз кофӣ нест. Кадри дуюм 18 илова мекунад: аз 25 мс ду қадам ва бақияи 5 ҳосил мешавад. Бақия ба кадри навбатӣ мегузарад. Дар симулятсияи воқеӣ кори ҳар кадрро низ маҳдуд мекунанд: баъди таваққуфи дароз набояд беохир вақти гузаштаро ҷуброн карда, интерфейсро бе ҷавоб гузошт.

Дар дохил чӣ мешавад

Соат вақти ҷориро бо муҳлати наздиктарин муқоиса карда, рӯйдодҳои омодаро интихоб мекунад, dt-ро ҳисоб карда, функсияҳоро даъват мекунад. Баъди коркардкунандаҳо идора ба давраи барнома бармегардад. Кори тӯлонӣ ё time.sleep() дар дохили коркардкунанда рӯйдодҳои дигар ва кашиданро ба таъхир меандозад; соат функсияҳоро ҳамзамон иҷро намекунад.

Интизор нашавед, ки пас аз ҷаҳиши калони вақт schedule_interval ҳатман ҳар даъвати гузаштаро аз нав иҷро мекунад. dt-и воқеиро коркард кунед; дар ҷойҳое, ки рафтори идорашавандаи симулятсия лозим аст, қадамҳои собитро алоҳида ҳисоб кунед.

Хатоҳои маъмул ва ислоҳи онҳо

  1. Дар ҳар навсозӣ x += 3 навиштан. Бо басомади гуногуни даъват суръат фарқ мекунад. Суръатро бо пиксел дар сония дода, ба dt зарб кунед.
  2. schedule_once(start(), 1) навиштан. start фавран иҷро шуда, натиҷааш ба соат дода мешавад. start-ро бе қавс диҳед ва ба параметрҳояш dt илова кунед.
  3. Барои соат def update(): таъриф кардан. Ҳангоми даъват бо аргумент TypeError мешавад. Параметри dt-ро илова кунед, ҳатто агар лозим набошад.
  4. Аккумуляторро танҳо дар дохили update ҳамчун acc = 0 нигоҳ доштан. Дар ҳар кадр бақия гум мешавад. Ҳолатро байни даъватҳо дар объект, луғат ё closure нигоҳ доред.
  5. Барои ним сония 500 додан. Соат сонияҳоро ҳисоб карда, 500 сония интизор мешавад. Аввал миллисонияро ба 1000 тақсим кунед.

Ёддошти кӯтоҳ

Амал Навишт
Ҳаракат бо суръати додашуда x += speed * dt
Такрор clock.schedule_interval(f, interval)
Як даъвати дертар clock.schedule_once(f, delay)
Бекоркунӣ clock.unschedule(f)
Идораи вақт дар санҷиш Clock(time_function=...), сипас tick()
Табдили сония ба миллисония round(dt * 1000)
Ҳисоби қадамҳои пурра ва бақия divmod(acc, step_ms)