$$ \newcommand{\floor}[1]{\left\lfloor{#1}\right\rfloor} \newcommand{\ceil}[1]{\left\lceil{#1}\right\rceil} \renewcommand{\mod}{\,\mathrm{mod}\,} \renewcommand{\div}{\,\mathrm{div}\,} \newcommand{\metar}{\,\mathrm{m}} \newcommand{\cm}{\,\mathrm{cm}} \newcommand{\dm}{\,\mathrm{dm}} \newcommand{\litar}{\,\mathrm{l}} \newcommand{\km}{\,\mathrm{km}} \newcommand{\s}{\,\mathrm{s}} \newcommand{\h}{\,\mathrm{h}} \newcommand{\minut}{\,\mathrm{min}} \newcommand{\kmh}{\,\mathrm{\frac{km}{h}}} \newcommand{\ms}{\,\mathrm{\frac{m}{s}}} \newcommand{\mss}{\,\mathrm{\frac{m}{s^2}}} \newcommand{\mmin}{\,\mathrm{\frac{m}{min}}} \newcommand{\smin}{\,\mathrm{\frac{s}{min}}} $$

Prijavi problem


Obeleži sve kategorije koje odgovaraju problemu

Još detalja - opišite nam problem


Uspešno ste prijavili problem!
Status problema i sve dodatne informacije možete pratiti klikom na link.
Nažalost nismo trenutno u mogućnosti da obradimo vaš zahtev.
Molimo vas da pokušate kasnije.

Час 11 - Анимације

Вероватно већ знаш да цртани филм настаје тако што се на екрану брзо смењују сличице, при чему је свака следећа слика веома слична претходној (ликови на слици су само мало померени у односу на претходни положај).

На пример, од наредних осам сличица лика у различитим положајима:

../_images/liktrci1.png ../_images/liktrci2.png ../_images/liktrci3.png ../_images/liktrci4.png ../_images/liktrci5.png ../_images/liktrci6.png ../_images/liktrci7.png ../_images/liktrci8.png

настаје следећа анимација трчања:

../_images/liktrci.gif

Анимације подразумевају брзу промену слике на екрану (на пример, 20 пута у секунди), обично у правилним временским интервалима (на пример, на сваких 50 милисекунди). Свака тако кратко приказана слика назива се оквир или фрејм анимације (енгл. frame). У овом делу приручника видећемо како можемо направити програме у којима се приказују неке анимације.

У програмима које смо до сада сретали слика се није мењала током извршавања и цртање смо вршили само једном, пре главне петље програма у којој смо чекали да корисник искључи прозор (у програмима заснованим на библиотеци PyGameBg, та петља се остварује позивом функције pygamebg.wait_loop).

У програмима са анимацијом цртање ћемо вршити обично унутар тела те главне петље или, још боље, у засебној функцији коју ћемо на том месту позивати (у програмима заснованим на библиотеци PyGameBg, таква петља се остварује позивом функције pygamebg.frame_loop).

Програмирање анимација на сајту „Петља”

Све анимације које ћемо у наставку приказати разликоваће се само по томе који подаци одређују оно што се на слици налази, коду који извршава цртање и коду који мења податке када се прелази на наредни фрејм. Да бисмо ти олакшали сналажење са кодом који већ постаје дугачак и компликован, цртање и прелазак са тренутног на наредни фрејм ћемо издвојити у две помоћне функције које ти треба да напишеш, док ћемо главну петљу програма која те две функције позива ми писати уместо тебе (и она ће бити у „сивом делу кода”). Осим те две функције твој задатак ће бити и да дефинишеш променљиве које описују оно што се налази на екрану током анимације. То ће бити обично променљиве које описују положај (координате) објеката тј. ликова који се током анимације померају, њихову брзину, али и неки други подаци који се мењају током анимације.

Постоји неколико начина да се анимације реализују и у зависности од тога „сиви кôд” може бити реализован на различите начине. Ако програмираш анимације само на сајту „Петља”, тада не мораш уопште да читаш и анализираш сиви кôд (он ће увек бити унапред припремљен за тебе). Ако желиш да правиш програме са анимацијама и ван овог сајта, тада ипак треба да разумеш како сиви кôд функционише. За то ти онда препоручујемо да прочиташ овај текст.

Прикажимо сада кроз неколико примера технику коју ће вам олакшати прављење анимација у примерима који следе. Сви ће бити засновани на библиотеци PyGameBg, тако да ће и „сиви кôд” бити прилично једноставан.

Посебна функција за цртање у програму без анимације

За почетак прикажимо како се цртање може издвојити у посебну функцију (и то у прво у програму без анимација, а затим у програму са анимацијама). Кренимо од програма који смо раније већ срели, који црта три круга у разним бојама.

Цртање кругова је део главног програма. Исти ефекат можемо постићи ако дефинишемо функцију crtaj коју ћемо позвати из главног програма.

Насумично одређивање боје позадине током анимације

Наредни програм ће приказивати једноставну анимацију у којој ћемо четири пута у секунди на насумичан начин одређивати боју позадине. Одређивање насумичне боје вршићемо помоћу посебне функције nasumicna_boja. Дефинисаћемо функцију crtaj у којој ће се насумично одређивати боја и затим ће се позадина прозора бојити том бојом. Пошто ћемо користити библиотеку PyGameBg, анимацију ћемо на крају програма покретати позивом pygamebg.frame_loop(4, crtaj), чиме ћемо постићи да се функција crtaj аутоматски изнова позива 4 пута у секунди.

Подијум за игру

По истом принципу можемо прилагодити програм који је исцртавао подијум за игру, тако да подијум стварно „оживи”. Пронађи кôд који исцртава подијум и на основу тога допуни функцију crtaj.

Промена боје позадине у круг

У многим анимацијама оно што се црта зависи од података који се мењају током анимације. Променимо програм у ком се мења боја позадине екрана тако да се боје не мењају насумично, него да се редом смењују црвена, зелена и плава.

Најједноставнији начин да се задатак реши је да боје држимо у листи (или торци) и да уз листу одржавамо и позицију текуће боје (њен индекс у листи). Након коришћења боје са те позиције, позицију ћемо увећавати за 1, при том проверавајући да се након последње боје поново вратимо на прву (да индекс постане 0). Најједноставнији начин да се то уради је да се након увећавања индекса за 1 израчуна његов остатак при дељењу са дужином листе (укупним бројем боја). Програм поново можемо реализовати издвајањем цртања у помоћну функцију, која ће се она током анимације аутоматски позивати два пута у секунди.

Приметимо да кôд који се налази у функцији користи променљиве boje и broj_boje које су дефинисане ван функције. Такве променљиве се називају глобалне променљиве и њихова се вредност може без икаквих проблема очитати из функције. Међутим, промена вредности глобалних у функцији је компликованија. Наиме, ако желимо да глобалној променљивој променимо вредност у функцији, на почетку те функције морамо нагласити да је та променљива глобална (помоћу кључне речи global иза које следи листа глобалних променљивих, раздвојених запетама, којима ћемо у тој функцији мењати вредност). Ако у функцији не бисмо навели реч global, добили бисмо поруку о грешци.

UnboundLocalError: local variable 'broj_boje' referenced before assignment

Нагласимо да глобалне променљиве нису најбољи начин за организовање сложенијих програма и постоје бољи начини да се подаци организују, међутим, у кратким програмима какве ћемо ми писати глобалне променљиве представљају најједноставније решење и стога ћемо их у наставку користити.

Функција crtaj је у претходном програму извршавала два задатка. Једно је цртање сцене на основу тренутних вредности глобалних променљивих, а друго је промена вредности променљивих чиме се са текућег прелази на наредни фрејм. И наредни програми са анимацијама ће имати те две функционалности, па програм постаје лепши ако их раздвојимо у две функције (иако овај корак можда делује сувишан код овако кратких програма, код дужих програма ће бити веома корисно да поступак цртања одвојимо од поступка измене стања програма).

Анализирајмо још једном претходни пример.

  • Стање програма је једнозначно одређено глобалном целобројном променљивом broj_boje која одређује редни број боје којом се прозор боји. Почетна вредност ове променљиве је нула, што значи да боје крећу да се приказују од почетка листе. Боје су смештене у глобалној листи boje која се не мења током извршавања програма.

  • Функција crtaj боји прозор бојом одређеном вредношћу променљиве broj_boje.

  • Функција novi_frejm ажурира вредност стања тј. променљиве broj_boje тако што јој увећава вредност и враћа је на нулу када вредност достигне укупан број боја. Пошто се мења вредност променљиве broj_boje која је глобална, у функцији novi_frejm морамо да променљиву broj_boje означимо помоћу кључне речи global.

Сви програми са анимацијама које ћемо у наставку писати биће организовани на овај начин и твој задатак ће бити да дефинишеш променљиве које одређују стање програма и функције crtaj и novi_frejm. На пример, програм који насумично мења боју позадине можемо написати на следећи начин.

Анализирајмо још једном претходни пример.

  • Стање програма је једнозначно одређено глобалном целобројном променљивом boja која одређује тренутну боју позадине екрана. Почетна вредност ове променљиве се одређује насумично.

  • Функција crtaj боји прозор бојом одређеном вредношћу променљиве boja.

  • Функција novi_frejm ажурира вредност стања тј. променљиве boja тако што јој насумично додељује нову вредност. Пошто се мења вредност променљиве boja која је глобална, у функцији novi_frejm морамо да променљиву boja означимо помоћу кључне речи global. Ова се функција позива аутоматски у правилним временским итервалим и након ажурирања променљивих она позива функцију crtaj да би се прозор поново исцртао.

Кретање лоптице

Прикажимо још један пример анимације организоване на начин који смо управо описали. Написаћемо програм који анимира лоптицу која се креће са леве ка десној ивици екрана.

  • Потребно је да дефинишемо променљиве које ће описивати стање објеката који се анимирају. У нашем примеру то је једна црвена лоптица и пошто се она креће хоризонтално, по средини екрана довољно је да памтимо само њену координату x (то може, на пример, бити координата њеног центра, а могла би бити и, на пример, координата горњег левог темена квадрата описаног око ње). Пошто лоптица своје кретање започиње на левом крају екрана, променљиву x ћемо иницијализовати на нулу.

  • Функција crtaj се сада реализује веома једноставно. У њој бојимо позадину екрана у бело и затим исцртавамо лоптицу коришћењем вредности њеног положаја x.

  • При преласку на сваки нови фрејм потребно је да лоптицу померимо мало (на пример, за 1 пиксел) удесно. Дакле, у функцији novi_frejm потребно је само да увећамо вредност променљиве x за 1. Пошто се мења вредност променљиве x која је глобална, у функцији novi_frejm морамо променљиву x означимо помоћу кључне речи global. Након померања лоптице позивамо функцију crtaj.

Општи облик програма са анимацијама

Видели смо неколико примера и можеш приметити да смо у свима њима анимације остваривали по истом принципу, који ћеш ти примењивати и у наредним задацима.

1. Потребно је да дефинишеш глобалне променљиве којима се представљају подаци о ликовима и објектима на сцени (ти ће се подаци мењати током анимације).

2. Потребно је да дефинишеш функцију crtaj која коришћењем тих података црта сцену. У тој функцији нећемо вршити никакву промену података.

3. Потребно је да дефинишеш функцију novi_frejm која ажурира податке о ликовима и објектима на сцени (при чему све променљиве којима се у тој функцији мења вредност морају на њеном почетку бити експлицитно означене као глобалне коришћењем кључне речи global). Након ажурирања променљивих, потребно је да позовеш функцију crtaj, да би се промењена сцена исцртала.

Неке једоставне анимације

Сада знаш општи принцип по ком ћемо програмирати анимације. Провежбај ово кроз наредних неколико задатака.

Насумично цртање кругова

Напиши програм који четири пута у секунди мења положај и боју круга који се приказује на прозору. Положај и боју одређивати насумично, тако да се круг у сваком тренутку налази унутар прозора.

Приступимо решавању овог задатка на начин који смо описали. Размисли прво који подаци описују стање наше анимације.

    Q-1: Које променљиве одређују стање анимације?

  • Координата x центра лоптице.
  • Тачно.
  • Брзина лоптице.
  • Покушај поново.
  • Координата y центра лоптице.
  • Тачно.
  • Боја лоптице.
  • Тачно.
  • Број лоптица у прозору.
  • Покушај поново.
  • Дакле, тренутно стање програма одређено је координатама центра x и y, полупречником r и бојом круга boja и њих ћемо представити глобалним променљивама (при чему се полупречник неће мењати).

Размисли сада шта је задатак функције crtaj.

    Q-2: Шта функција crtaj треба да уради?

  • Да насумично одреди положај лоптице тако што ће одредити вредности променљивих ``x`` и ``y``.
  • Покушај поново.
  • Да пре цртања обоји позадину у бело, да би се обрисао претходни цртеж.
  • Тачно.
  • Да нацрта круг са центром у тачки одређеној координатама ``x`` и ``y``.
  • Тачно.
  • Да обоји позадину прозора у боју ``boja``.
  • Покушај поново.
  • Да помери лоптицу један пиксел удесно увећавајући вредност променљиве ``x``.
  • Покушај поново.
  • Дакле, функција crtaj ће бојити позадину у бело (како би се обрисао претходни круг) и цртаће круг на основу тренутних вредности глобалних променљивих.

Размисли сада шта је задатак функције novi_frejm.

    Q-3: Шта функција novi_frejm треба да уради?

  • Да помери лоптицу један пиксел удесно увећавајући вредност променљиве ``x``.
  • Покушај поново.
  • Да увећа полупречник лоптице ``r`` за 1.
  • Покушај поново.
  • Да нацрта круг са центром у тачки одређеној координатама ``x`` и ``y``.
  • Покушај поново.
  • Да на насумичан начин одреди нову вредност променљиве ``boja``.
  • Tачно.
  • Да насумично одреди положај лоптице тако што ће одредити вредности променљивих ``x`` и ``y``.
  • Тачно.
  • Дакле, функција novi_frejm ће насумично одредити боју новог круга и одредиће x и y координате центра, тако да круг не испадне ван екрана. То ће важити ако x координата буде у интервалу \([r, sirina-r]\), а y координата буде у интервалу \([r, visina-r]\).

Покушај да самостално напишеш цео програм. Ако не успеш, не брини, јер тек почињеш да учиш како се реализују апликације. Потражи помоћ и затим допуни започети програмски кôд.

Срце које куца

Напиши програм који приказује анимацију срца које куца. Срце можеш приказати коришћењем слике srce.png.

../_images/srce.png
  • Једини податак који се мења од фрејма до фрејма је то да ли треба или не треба приказати срце. Можемо увести логичку променљиву treba_crtati.

  • У функцији crtaj слику ћемо приказивати само ако променљива treba_crtati има вредност тачно (тј. True).

  • У функцији novi_frejm мењаћемо вредност променљиве treba_crtati. Ако јој је тренутно вредност True, тада треба да се промени на False, а ако јој је тренутно вредност False, тада треба да се промени на True. Најлакши начин да се то уради је да текућу вредност негирамо помоћу оператора not (наравно, можемо употребити и гранање).

Покушај да на основу овога допуниш започети програмски кôд.

Смајлић и тужић

Микица често мења расположење. Час је срећна, час је тужна. Напиши програм који приказује слике смајлића и тужића које се наизменично смењују. Можеш употребити слике smajlic.png и tuzic.png.

../_images/smajlic.png ../_images/tuzic.png

Овај задатак је поново веома сличан претходном.

  • Логичка променљива smajlic ће одређивати стање програма.

  • Ако је у функцији crtaj вредност те променљиве True, приказаћемо слику смајлића, а ако је вредност False, приказаћемо слику тужића.

  • У функцији novi_frejm негираћемо вредност променљиве smajlic.

Покушај да на основу овога потпуно самостално напишеш програм. Ако не успеш, онда потражи помоћ и допуни започети програмски кôд.

Домаћи задатак - сијалице

Напиши програм који приказује дисплеј на коме је поређано 10 сијалица, тако да се наизменично укључује једна по једна.

  • Стање програма ће у потпуности бити одређено вредношћу променљиве редним бројем сијалице која је укључена.

  • У функцији crtaj исцртаваћемо само укључену сијалицу у облику белог круга. Пречник сваке сијалице ћемо одредити тако што ћемо ширину екрана поделити редним бројем сијалице. Координата x центра те сијалице биће одређена редним бројем укључене сијалице (центар сијалице 0 налази се на растојању r од леве ивице екрана, центар сијалице 1 на растојању r + 2r, центар сијалице 2 на растојању r+4r и тако даље).

  • У функцији novi_frejm увећаваћемо редни број укључене сијалице, при чему ћемо га враћати на нулу када достигне укупан број сијалица.

Покушај да на основу претходне дикусије потпуно самостално решиш задатак. Ако не успеш, онда потражи помоћ, па допуни започети кôд.

Домаћи задатак - цртани филм

Напиши програм који приказује цртани филм тако што наизменично приказује пет слика на екрану на којима је приказан дечак који се шета у различитим положајима. Слике носе називе setanje1.png до setanje5.png.

../_images/setanje1.png ../_images/setanje2.png ../_images/setanje3.png ../_images/setanje4.png ../_images/setanje5.png

Овај задатак личи на задатак у ком смо током анимације циклично мењали боје позадине, једино што ћемо уместо листе која садржи називе боја чувати листу која садржи слике које чине цртани филм. Слике је пожељно учитати на почетку програма, пре почетка анимације (подсетимо се, слику можемо учитати помоћу функције pg.image.load).

  • Стање анимације биће одређено искључиво редним бројем слике која се тренутно приказује. Променљива slika представљаће позицију слике у листи која се приказује у текућем фрејму.

  • Функција crtaj има задатак само да обрише екран (бојећи позадину у бело) и да затим прикаже слику из листе slike која се налази на позицији одређеној променљивом slika (подсетимо се, слику приказујемо помоћу функције prozor.blit).

  • Функција novi_frejm ће имати задатак да увећа вредност променљиве slika, тако да се вредност врати на нулу када превазиђе последњу вредност у листи.

На основу претходне дискусије допуни наредни програм.

Пројекат - дигитални сат

Напиши програм који приказује дигитални сат.

Приказ тренутног времена потребно је извршити сваке секунде. Задатак ћемо решити помоћу анимације која је подешена тако да се сваке прелази на нови фрејм и исцртава садржај прозора.

Ова анимација је необична јер нема потребе за представљањем стања прозора. Дакле, нећемо имати нити глобалне променљиве нити функцију за прелазак на нови фрејм. У функцији crtaj очитаваћемо тренутно време и приказивати га на средини екрана.

Језик Python нам пружа подршку да очитамо тренутно време системског сата рачунара на ком се програм извршава. Функција datetime.now() из модула datetime (који увозимо на почетку програма помоћу import datetime) враћа тренутно време у облику структуре чије поље hour садржи број сати (од 0 до 23), поље minute садржи број минута (од 0 до 59) и second садржи број секунди (од 0 до 59). На основу тога поставићемо вредности променљивих sati, minuti, sekunde.

На основу броја сати, минута и секунди градимо текст који ћемо приказати. Желимо да и сати и минути и секунде увек имају тачно две цифре (ако су једноцифрени, желимо да се допуне водећим нулама). У језику Python ниску str можемо допунити водећим нулама до дужине n позивом str.rjust(n, "0"). Подсетимо се да ниске надовезујемо применом оператора +.

Када смо направили ниску која садржи тренутно време приказујемо је на средини прозора. То радимо на потпуно исти начин као у примерима које смо приказали у поглављу о цртању (можемо да дефинишемо посебну функцију која исписује дати текст тако да му центар буде у датој тачки).

Допуни наредни програм на основу претходне дискусије.