25.3. За оне који желе да знају више - Како раде анимације?¶
Постоје два суштински различита начина да се анимација оствари.
У једном је главна петља заснована на фрејмовима (енг. frame-based loop) и њено тело се извршава одређени фиксирани број пута у секунди (обично од 20 до 50). Између свака два фрејма програм се заустави. Фреквенцију промене фрејмова је пожељно пажљиво одабрати. Ако је она превелика, објекти се пребрзо крећу по екрану или се иста сцена исцртава већи број пута. Ако је премала, програм је дуго блокиран и не реагује на догађаје, што корисник може осетити (на пример, може да прође одређено време између тренутка када корисник кликне на дугме за искључивање прозора и тренутка када се прозор заиста искључи).
У другом је главна петља заснована на догађајима (енг. event-based loop) и њено тело се изврши по једном за сваки догађај који наступи, чим догађај наступи. Уводи се тајмер (eнг. timer) који генерише догађаје у правилним временским интервалима и на сваки откуцај тајмера приказује се наредни фрејм. У овом поглављу ћемо се бавити само анимацијама у којима је главна петља заснована на фрејмовима, док ћемо анимације засноване на догађајима описати у наредним поглављима.
Временско растојање између два суседна фрејма¶
Најједноставнији начин да се анимација оствари јесте да се следећи облик петље засноване на фрејмовима.
У склопу главне петље прво цртамо. Након тога обрађујемо све
догађаје који су наступили током цртања и чекања, тј. који су
наступили од претходне обраде догађаја (пре свега проверавамо да ли је
корисник искључио прозор, тј. да ли је наступио догађај
pg.QUIT
). Ако јесте, прекидамо петљу (било постављањем променљиве
kraj
на True
, било наредбом break
). Затим чекамо одређено
време (на пример, 50 или 100 милисекунди). Чекање можемо остварити
позивом функције pg.time.wait(???)
, чији је параметар дати број
милисекунди (1000 милисекунди је једна секунда).
Док програм чека, сви догађаји који се у међувремену одиграју смештају се
у једну листу (такозвани ред догађаја). Листа свих догађаја који су
регистровани у реду од тренутка претходног читања тог реда може се
добити функцијом pg.event.get()
. Током обраде догађаја пролазимо
кроз ту листу анализирајући један по један догађај (подсетимо се,
пролазак кроз елементе листе остварује се наредбом облика for
element in lista: ...
). Дакле, у овом сценарију имамо петљу у
петљи. У унутрашњој петљи for
можемо анализирати све догађаје
и реаговати на њих о чему ће више бити речи у поглављу о догађајима.
Коришћење сата¶
Претходно решење не узима у обзир ни трајање исцртавања ни обраде
догађаја. Пауза је увек иста, без обзира на то колико су исцртавање и
обрада догађаја трајали. Пошто сваки фрејм траје онолико времена колико
је потребно за обраду догађаја, паузу и рачунање новог фрејма, једнаке
паузе доводе до неједнаког трајања фрејмова. То се може поправити ако се
уместо pg.time.wait
употреби сат који креирамо помоћу pg.Clock
.
На овај начин смо рекли да желимо да се тело петље (тј. цртање) изврши
тачно 25 пута током једне секунде. Заправо, потпуно прецизно говорећи,
овим смо поставили горње ограничење на учесталост извршавања тела
главне петље - оно сигурно неће бити извршено чешће од броја који је
наведен као аргумент функције tick
. Наиме, у неким изузетним
ситуацијама (на пример, ако поставимо велику учесталост, а цртање је
компликовано и захтева много времена), систем неће успети да постигне
наведену учесталост и тело петље ће се извршити ређе од онога што је
наведено.
Коришћење библиотеке PygameBg¶
Уз коришћење ове библиотеке, креирање анимација заснованих на фрејмовима је много једноставније (иако „испод хаубе“ користи сат и функционише исто како је приказано у претходном примеру). Програми имају наредни облик.
Као и сви претходни, и ови програми почињу отварањем прозора, тј. позивом функције pygamebg.open_window. Анимација се започиње на самом крају програма позивом функције pygamebg.frame_loop којој се прослеђује број фрејмова у секунди и функција која се позива при преласку на сваки наредни фрејм. Њен задатак је да промени стање сцене (положај објеката, боје и слично) и да изврши цртање сцене. Да би програми били јаснији, ова два задатка ћемо често раздвајати тако што ћемо у функцији novi_frejm извршавати ажурирање стања сцене, а у посебној функцији crtaj ћемо вршити цртање (ту ћемо функцију позивати из функције novi_frejm, обично на самом њеном крају, након ажурирања вредности свих променљивих).