Печатать книгуПечатать книгу

§ 17. Анімацыя

Сайт: Профильное обучение
Курс: Інфарматыка. 11 клас (Павышаны ўзровень)
Книга: § 17. Анімацыя
Напечатано:: Гость
Дата: Вторник, 7 Май 2024, 19:48

17.1. Кампанент Timer

Кампанент таймер — Timer дазваляе задаваць у дадатку інтэрвалы часу. На панэлі кампанентаў System кампанент таймер намаляваны ў выглядзе , імя аб'екта — Timer. Кампанент Timer, змешчаны на форму, атрымлівае імя TimerN, дзе N — нумар 1, 2, 3… (прыклад 17.1).

Таймер з'яўляецца не візуальным кампанентам, які можна размясціць у любым месцы формы. Таймер знаходзіць шматлікія прымяненні: кіраванне мультыплікацыяй, уключэнне захавальніка экрана, заданне часу на адказ у навучальных праграмах. Уласцівасці кампанента прыведзены ў прыкладзе 17.2.

Для таймера даступна адзіная падзея — OnTimer. У апрацоўшчыку гэтай падзеі запісваюцца неабходныя каманды. Падзея надыходзіць па заканчэнні рэальнага часу эквівалентнага значэнню ўласцівасці Interval для таймера. Да таго часу, пакуль таймер не будзе выключаны, гэта падзея будзе наступаць зноў і зноў.

Прыклад 17.3. Стварыць дадатак, які дазваляе ўводзіць бягучы час кожную секунду.

Этапы выканання задання

  1. Змясціць на форму кампаненты Timer, Label, і два кампаненты Button.
  2. Змяніць уласцівасць Caption у кампанента Button1 на «Старт».
  3. Змяніць уласцівасць Caption у кампанента Button2 на «Стоп».
  4. Ачысціць уласцівасць Caption у кампанента Label1.
  5. Змяніць уласцівасць Enabled у кампанента Timer1 на false.
  6. Напісаць апрацоўшчыкі падзей OnClick для кампанентаў Button1 і Button2, якія будуць запускаць і спыняць таймер.
Напісаць апрацоўшчык падзей OnTimer для кампанента Timer, які будзе выводзіць сістэмны час. Для вываду сістэмнага часу можна выкарыстоўваць функцыю TimeToStr(Time()), якая пераўтворыць сістэмны час Time() у радковае ўяўленне.

Прыклад 17.1. Кампанент Timer на форме:

Прыклад 17.2.  Уласцівасці кампанента Timer:

Уласцівасці 

Прызначэнне

Interval

Задае перыяд часу спрацоўвання таймера ў мілісекундах

Enabled

Значэнне true ў гэтай уласцівасці паказвае, што таймер запушчаны

Прыклад 17.3. Форма на этапе праектавання:

Апрацоўшчыкі падзеі OnClick для кампанентаў Button1 і Button2.

void __fastcall TForm1::
Button1Click(TObject *Sender)

{

  Timer1 -> Enabled = true;

}

//---------------------------

void __fastcall TForm1::
Button2Click(TObject *Sender)

{

  Timer1 -> Enabled = false;

} 

Апрацоўшчык падзеі OnTimer.

void __fastcall TForm1::
Timer1Timer(TObject *Sender)

{

  Label1 ->Caption = TimeToStr(Time());

}

Дадатак у час працы:

Для вываду часу можна скарыстацца іншай функцыяй[1]


[1] Са значэннямі параметраў гэтай функцыі можна пазнаёміцца ў дадатку

   FormatDateTime("hh:nn:ss",Time())   

Гэтая функцыя дазваляе змяніць фармат вываду даты/часу. Напрыклад, для таго каб вывесці час так:

трэба выкарыстоўваць функцыю з наступнымі параметрамі:

    FormatDateTime("dddd, dd mmmm yyyy, 
    hh:mm:ss:zz", Now());   

Час спрацоўвання таймера змяніць на 100.

17.2. Найпростая анімацыя

З курсу інфарматыкі 8-га класа вам вядома, што вылучаюць два спосабы стварэння камп'ютарнай анімацыі: пакадравая і разліковая (прыклад 17.4).

Пры стварэнні пакадравай (традыцыйнай, найпростай) анімацыі прамалёўваюцца ўсе фазы руху аб'екта. Пры разліковай анімацыі прамалёўваюцца толькі асобныя кадры, у якіх аб'ект размяшчаецца ў пачатку руху(або ў пачатку і канцы руху), малюнкі ў астатніх кадрах (прамежкавых) будуюцца з дапамогай камп'ютарных праграм.

Эфект руху аб'екта з'яўляўся ў выніку хуткай змены кадраў на экране.

Выкарыстоўваючы кампанент таймер, можна стварыць найпростую анімацыю ў асяроддзі C++Builder. Для гэтага трэба:

  1. Намаляваць аб'ект.
  2. Запусціць таймер.
  3. Пакуль таймер працуе:
    3.1. сцерці аб'ект;
    3.2. разлічыць новае становішча аб'екта;
    3.3. намаляваць аб'ект.

Прыклад 17.5. Стварыць секундамер з секунднай стрэлкай, якая рухаецца.

Этапы выканання задання

  1. Змясціць на форму кампаненты Timer, два кампаненты Image, і кампанент Button.
  2. Змяніць уласцівасць Caption у кампанента Button1 на «Пуск».
  3. Змяніць уласцівасць Enabled у кампанента Timer1 на false.
  4. Задаць памеры Image1 і Image2 такія як у малюнка з цыферблатам. Кампаненты размясціць так, каб Image2 размяшчаўся дакладна над Image1.
  5. Змяніць уласцівасць Transparent у кампанента Image2 на true.
  6. Напісаць апрацоўшчык падзей OnCreate для формы, у якім:
    6.1. Загрузіць малюнак з цыферблатам у Image1.
    6.2. На канве Image2 намаляваць стрэлку, якая ўказвае на 12.
  7. Напісаць апрацоўшчык падзеі OnClick для кампанента Button1, які будзе запускаць таймер.
  8. Напісаць апрацоўшчык падзей OnTimer для кампанента Таймер, які будзе сціраць стрэлку і прамалеўваць яе ў новым месцы кожную секунду. Кожную секунду стрэлка паварочваецца на вугал роўны 

Прыклад 17.4. Спосабы стварэння камп'ютарнай анімацыі:

 

Прыклад 17.5. Форма на этапе праектавання:

Пераменныя x, y, x_0, y_0 тыпу int і пераменныя ang і pi тыпу double апісаны як глабальныя.

Апрацоўшчык падзеі OnClick для кампанента Button1.

void __fastcall TForm1::
Button1Click(TObject *Sender)

{

  Timer1 ->Enabled = true;

}

Апрацоўшчык падзеі OnCreate для формы.

void __fastcall TForm1::FormCreate
               (TObject *Sender)

{

  Image1 -> Picture -> LoadFromFile
        
("stopwatch.bmp");

  x_0 = 123; y_0 = 181; ang = 0;

  //прамавугольнік белага колеру, для таго

  //каб празрыстым стаў белы колер

  Image2 -> Canvas -> Pen -> Color = clWhite;

  Image2 -> Canvas -> Rectangle

    (00, Image1 -> Width, Image1 -> Height);

  //стрэлка даўжынёй 75 пікселяў

  //у пачатковым становішчы

  x = x_0; y = y_0 - 75;

  Image2 -> Canvas -> Pen-> Color = clBlack;

  Image2 -> Canvas -> Pen -> Width = 2;

  Image2 -> Canvas -> MoveTo(x_0, y_0);

  Image2 -> Canvas -> LineTo(x, y);

  pi = acos(-1.);

}

 Апрацоўшчык падзеі OnTimer для таймера.

void __fastcall TForm1::Timer1Timer
                (TObject *Sender)

{

  //сціранне стрэлкі белым колерам

  Image2 -> Canvas -> Pen -> Color = clWhite;

  Image2 -> Canvas -> Pen -> Width = 3;

  Image2 -> Canvas -> MoveTo(x_0, y_0);

  Image2 -> Canvas -> LineTo(x, y);

  //вылічэнне каардынат канца стрэлкі

  ang = ang + pi / 30;

  x = x_0 + ceil(75 * sin(ang));

  y = y_0 - ceil(75 * cos(ang));

  //маляванне стрэлкі ў новым становішчы

  Image2 -> Canvas -> Pen -> Color = clBlack;

  Image2 -> Canvas -> Pen -> Width = 2;

  Image2 -> Canvas -> MoveTo(x_0, y_0);

  Image2 -> Canvas -> LineTo(x, y);

}

 Дадатак у час працы:

17.3. Анімацыя руху

Для стварэння анімацыі руху дастаткова загрузіць малюнак у Image і для кожнага спрацоўвання таймера мяняць каардынаты верхняга левага вугла кампанента. Для таго каб аб'ект рухаўся гарызантальна, дастаткова мяняць толькі значэнне каардынаты х. Для арганізацыі руху па больш складаных траекторыях неабходна вылічаць значэнні каардынат па формулах. Залежнасці могуць быць функцыянальнымі (значэнне y залежыць ад значэння х) або без (значэння як x і y залежаць ад значэнняў параметра t).

Прыклад 17.6. Стварыць праект, у якім самалёт будзе пралятаць над горадам. Пры выдаленні самалёт павінен змяншацца.

Этапы выканання задання

  1. Змясціць на форму два кампаненты Image, кампаненты Timer і Button.
  2. Змяніць уласцівасці Caption у кампанента Button1 на «Полетели!».
  3. Устанавіць у кампанента Image2 значэнне true для ўласцівасцяў Transparent і AutoSize.
  4. Устанавіць значэнне false ва ўласцівасці таймера Enabled.
  5. Устанавіць у інспектары аб'ектаў час спрацоўвання таймера роўным 10.
  6. Напісаць апрацоўшчык падзеі OnCreate для формы, у якім:
    6.1. Загрузіць з файла малюнак горада ў кампанент Image1.
    6.2. Загрузіць з файла малюнак самалёта ў Image2.
    6.3. Апісаць пачатковае становішча самалёта, паказаўшы каардынаты верхняга левага вугла Image2 за межамі формы.
  7. Напісаць апрацоўшчык падзеі OnClick для кампанента Button1, у якім запусціць таймер.
  8. Напісаць апрацоўшчык падзеі OnTimer, у якім неабходна змяніць значэнне ўласцівасцей кампанента Image2.
    8.1. Значэнне ўласцівасцей Left і Top. Значэнне Left — гэта велічыня каардынаты па x, якое змяняецца на 1 пры кожным спрацоўванні таймера. Значэнне Top — гэта велічыня каардынаты па y, якое вылічаецца па формуле y = –0.3x + 200. Калі самалёт вылецеў за мяжу, то вярнуць яго ў пачатковае становішча.

    8.2. Значэнне ўласцівасцей Width і Height змяняецца на 3 і 1 адпаведна (шырыня малюнка з самалётам прыкладна ў 3 разы большая, чым вышыня). Змены адбываюцца для кожнага x, якое кратна 30.

Прыклад 17.6. Форма на этапе праектавання:

Пераменные x_plane, y_plane, w, h тыпу int апісаны як глабальныя.

Апрацоўшчык падзеі OnClick для кампанента Button1.

void __fastcall TForm1::
Button1Click(TObject *Sender)

{

  Timer1 ->Enabled = true;

}

Апрацоўшчык падзеі OnCreate для формы.

void __fastcall TForm1::
FormCreate(TObject *Sender)

{

  Image1 -> Picture ->

    LoadFromFile("city.bmp");

  Image2 -> Picture ->

    LoadFromFile("plane.bmp");

  w = Image2 -> Width;

  h = Image2 -> Height;

  x_plane = -w;

  Image2 -> Left = x_plane;

  Image2 -> Top = y_plane;

  //змена ўласцівасцей для таго,

  //каб можна было мяняць памер

  Image2 -> AutoSize = false;

  Image2 -> Stretch = true;

}

Апрацоўшчык падзеі OnTimer для таймера.

void __fastcall TForm1::
Timer1Timer(TObject *Sender)

{

  //змена каардынаты x

  x_plane ++;

  if (x_plane > Image1 -> Width)

    x_plane = -w;

  //змена каардынаты y

  y_plane = -0.3 * x_plane + 200;

  //змена памеру

  if (x_plane % 30 == 0){

    Image2 -> Width -= 3;

    Image2 -> Height -= 1;

  }

  //вывад малюнка ў новай пазіцыі

  Image2 -> Left = x_plane;

  Image2 -> Top = y_plane;

}

Дадатак у час працы (у розныя моманты часу):

Калі пры працы дадатку ўзнікае мігаценне, то неабходна ўстанавіць значэнне true для ўласцівасцец формы AlphaBlend і DoubleBuffered.

17.4. Фрагменты малюнкаў

Мігаценне, якое можна было назіраць у прыкладзе 17.6 узнікае з прычыны таго, што малюнак даводзіцца перамалёваць на форме пры кожным спрацоўванні таймера. Чым больш будуць памеры аб'екта, які рухаецца, тым мацней будзе мігаценне. Выкарыстанне ўласцівасцей формы AlphaBlend і DoubleBuffered не заўсёды дае вынік.

Для арганізацыі анімацыі руху можна выкарыстоўваць бітавыя вобразы, якія былі разгледжаны ў §14. Для вываду бітавага вобраза на Image можна выкарыстоўваць розныя метады (прыклад 17.7). Выкарыстанне метаду Draw было разгледжана ў §14. Для прымянення іншых метадаў неабходна вылучаць на малюнку прамавугольную вобласць.

Пераменныя Pr1 і Pr2 з табліцы прыкладу 17.7 апісаны тыпам TRect. Тып TRect — гэта структура з 4 палямі (Left, Top, Right, Bottom), якая выкарыстоўваецца для захоўвання верхняй левай і ніжняй правай вяршынь прамавугольніка. Задаваць значэнні можна непасрэдным прысвойваннем значэнняў або з дапамогай функцый Rect або Bounds (прыклад 17.8).

Прыклад 17.9. Змяніць праект з прыкладу 14.5 так, каб жывёл можна было маляваць у абраным маштабе.

Этапы выканання задання

  1. Змясціць на форму два кампаненты Image, кампаненты LabeledEdit, Button і дыялог OpenPictureDialog.
  2. Змяніць уласцівасці Caption у кампанента Button1 на «Выбрать».
  3. Устанавіць у кампанента Image2 значэнне true для ўласцівасці AutoSize (малюнак будзе загружацца ў рэальным памеры).
  4. Змяніць у кампанента LabeledEdit1 уласцівасць Caption на «Коэффициент масштабирования», уласцівасць Text — на 1,3.
  5. Апісаць як глабальныя пераменныя два бітавых вобраза: Ris1 — для захоўвання зыходнага малюнка, Ris2 — для захоўвання малюнка з улікам маштабу;
  6. Напісаць апрацоўшчык падзеі OnCreate для формы, у якім загрузіць з файла малюнак лесу ў кампанент Image1.
  7. Напісаць апрацоўшчык падзеі OnClick для кампанента Button1.
    7.1. Загрузіць у бітавы вобраз Ris1 малюнак з абранага файла.
    7.2. Вывесці яго ў Image2 метадам Assign.
  8. Напісаць апрацоўшчык падзеі OnMouseDown для кампанента Image1, у якім
    8.1. Задаць прамавугольнік, даўжыні бакоў якога атрымліваюцца як даўжыні бакоў зыходнага малюнка, памножаныя на каэфіцыент маштабавання.

    8.2. Задаць памеры Ris2 у адпаведнасці з маштабам.
    8.3. Скапіраваць малюнак з Ris1 на Ris2, выкарыстоўваючы метад StretchDraw.
    8.4. Метадам Draw вывесці малюнак на Image1.

Пры выкарыстанні метаду CopyRect можна капіраваць прамавугольную вобласць, якая знаходзіцца на адной канве, у прамавугольную вобласць, якая знаходзіцца на іншай канве (прыклад 17.10).

Калі памеры прамавугольнікаў супадаюць, то атрымліваем дакладную копію. Калі памер крыніцы менш або больш, то копія маштабуецца так, каб упісацца ў прамавугольнік (прыклад 17.11). Празрыстасць фону пры гэтым не падтрымліваецца.

Пры выкарыстанні метаду CopyRect малюнкі можна выводзіць, адбіўшы іх злева-направа або зверху-уніз. Для гэтага трэба замяніць значэнні палёў у структуры, якая апісвае прамавугольнік:

  //зверху-ўніз
  Pr2 = Pr1;
  Pr2.bottom = Pr1.top;[1]
  Pr2.top = Pr1.bottom;
  //злева-направа
  Pr3 = Pr1;
  Pr3.left = Pr1.right;
  Pr3.right = Pr1.left;

Пры неабходнасці можна змяніць усе чатыры значэнні — у гэтым выпадку атрымаем люстэркавае адлюстраванне малюнка.

У прыкладзе 17.12. малюнкі капіраваліся з бітавага вобраза Pict1 ў бітавы вобраз Pict2 для таго, каб можна было затым вывесці малюнак з празрыстым фонам.



[1] Часам неабходна адабраць 1, паколькі пункт, які апісвае ніжні правы вугал, не належыць вобласці капіравання. З прычыны чаго, пры даданні празрыстасці, можа заставацца белая паласа таўшчынёй у 1 піксель. 

Прыклад 17.7. Метады вываду бітавага ладу (Ris1):

Патрабаваны вынік

Метад

Капіраванне графікі

Draw(x, y, Ris1)

Капіраванне з маштабаваннем

StretchDraw(Pr1, Ris1)

Капіраванне прамавугольнага ўчастка канвы

CopyRect(Pr1, 
Ris -> Canvas, Pr2)

Поўнае капіраванне ўсіх уласцівасцей (у тым ліку і памераў)

Picture->Assign(Ris1)

Прыклад 17.8. Вызначэнне прамавугольнай вобласці:

TRect Pr1, Pr2, Pr3;

//Вызначэнне прамавугольнай 
//вобласці указаннем значэнняў палёў

  Pr1.left = x1;

  Pr1.top = y1;

  Pr1.right = x2;

  Pr1.bottom = y2;

//Вызначэнне прамавугольнай
//вобласці па каардынатах

  Pr2 = Rect(x1, y1, x2, y2);

//Вызначэнне прамавугольнай
//вобласці шляхам задання

//верхняга левага вугла і

//даўжыні, шырыні прамавугольніка

  Pr3 = Bounds(x1, y1, w, h);

Прыклад 17.9. Форма на этапе праектавання:

Апрацоўшчык падзеі OnCreate для формы.

void __fastcall TForm1::
FormCreate(TObject *Sender)

{

  Image1 ->Picture ->
     LoadFromFile
("лес.bmp");

  OpenPictureDialog1 -> InitialDir =

      ExtractFilePath(ParamStr(0));

}

Апрацоўшчык падзеі OnClick для кампанента Button1.

void __fastcall TForm1::
Button1Click(TObject *Sender)

{

  if (OpenPictureDialog1 ->Execute()){

  Ris1 -> LoadFromFile
  
(OpenPictureDialog1 ->FileName);

  Image2 -> Picture -> Assign(Ris1);

  w = Ris1 -> Width;

  h = Ris1 -> Height;

  }

}

Апрацоўшчык падзеі OnMouseDown для кампанента Image1.

void __fastcall TForm1::
Image1MouseDown(TObject *Sender, 
TMouseButton Button, 
TShiftState Shift, 
int X, int Y)
{
  TRect Pr1;
  double k = StrToFloat
    (LabeledEdit1 -> Text);
  Pr1 = Bounds(00,
    int(k * w), int(k * h));
  Ris2 -> Width = int(k * w);
  Ris2 -> Height = int(k * h);
  Ris2 -> Canvas ->StretchDraw(Pr1, Ris1);
  Ris2 -> Transparent = true;
  Image1 -> Canvas -> Draw(X, Y, Ris2);
}

Дадатак у час працы:

Прыклад 17.10. Фармат каманды CopyRect:

Прыклад 17.11. Выкарыстанне метаду CopyRect:

Прыклад 17.12. Капіраванне з адлюстраваннем:

Апрацоўшчык падзеі для кнопкі:

void __fastcall TForm1::
Button1Click(TObject *Sender)

{

  TRect Pr1, Pr2, Pr3, Pr4;

  Pr1 = Bounds(00, w, h);

  Image1 -> Canvas -> Draw(250200, Pict1);

  //зверху-ўніз

  Pr2 = Pr1;

  Pr2.bottom = Pr1.top - 1;

  Pr2.top = Pr1.bottom;

  Pict2 -> Canvas -> CopyRect(Pr2,

      Pict1 -> Canvas, Pr1);

  Image1 -> Canvas -> Draw(50300, Pict2);

  //злева-направа

  Pr3 = Pr1;

  Pr3.left = Pr1.right;

  Pr3.right = Pr1.left;

  Pict2 -> Canvas -> CopyRect(Pr3, 

      Pict1 -> Canvas, Pr1);

  Image1 -> Canvas -> Draw(100200, Pict2);

  //зверху-уніз, злева-направа

  Pr4 = Pr1;

  Pr4.left = Pr1.right;

  Pr4.right = Pr1.left;

  Pr4.top = Pr1.bottom;

  Pr4.bottom = Pr1.top;

  Pict2 -> Canvas -> CopyRect(Pr4,

     Pict1 -> Canvas, Pr1);

  Image1 -> Canvas -> Draw(350300, Pict2);

}

17.5. Анімацыя руху з выкарыстаннем бітавых вобразаў

Выкарыстанне бітавых вобразаў дазваляе абыходзіцца толькі адным кампанентам Image на форме. Пры спрацоўванні таймера аб'ект, які захоўваецца ў выглядзе бітавага вобраза, будзе прамалёўвацца па-над фону.

Аднак прамалёўка паверх наяўнага малюнка сапсуе малюнак фону. Таму перад прамалёўкай варта скапіраваць тую частку фону, паверх якой будзе выведзены аб'ект, у бітавы вобраз. А пры наступным спрацоўванні таймера варта аднавіць фон.

Алгарытм для рэалізацыі такой анімацыі будзе наступным:

  1. Захаваць частку фону.
  2. Намаляваць аб'ект.
  3. Запусціць таймер.
  4. Пакуль таймер працуе:
    4.1. аднавіць фон;
    4.2. разлічыць новае становішча аб'екта;
    4.3. захаваць частку фону;
    4.4. намаляваць аб'ект.

Прыклад 17.13. Стварыць праект, у якім месяц будзе лётаць вакол Зямлі.

Этапы выканання задання

  1. Змясціць на форму кампанент Image, кампаненты Timer і Button.
  2. Змяніць уласцівасці Caption у кампанента Button1 на «Старт».
  3. Устанавіць значэнне false ва ўласцівасці таймера Enabled.
  4. Устанавіць у інспектары аб'ектаў час спрацоўвання таймера роўным 100.
  5. Апісаць тры пераменных для захоўвання бітавых вобразаў: fon — для захоўвання фонавага малюнка, kadr — для захоўвання малюнка месяца, fon_kadr — для захоўвання часткі фону.
  6. Напісаць апрацоўшчык падзеі OnCreate для формы, у якім:
    6.1. Загрузіць з файла малюнак Зямлі ў бітавы вобраз fon і кампанент Image1.
    6.2. Загрузіць з файла малюнак месяца ў бітавы вобраз kadr. Апісаць пачатковае становішча месяца.
    6.3. Вызначыць прамавугольныя вобласці для бягучага становішча на фоне і для кадра.
  7. Напісаць апрацоўшчык падзеі OnClick для кампанента Button1, у якім неабходна запусціць таймер.
  8. Напісаць апрацоўшчык падзеі OnTimer, у якім варта рэалізаваць анімацыю руху месяца. Месяц рухаецца па эліптычнай арбіце.

Прыклад 17.14*. Стварыць праект, у якім матылёк будзе лётаць вакол кветкі па траекторыі ў выглядзе астроіды і мяняць свой напрамак палёту.

Этапы выканання задання

  1. Анімацыя запускаецца адразу пры загрузцы формы (без кнопкі запуск).
  2. Калі не ўлічваць неабходнасць змены напрамку матылька пры палёце, то заданне аналагічна прыкладу 17.13 (досыць змяніць формулы, якія вызначаюць пункты акружнасці на формулы, якія вызначаюць пункты астроіды — прыклад 15.11). Таму разгледзім толькі змену напрамка палёту.
  3. Напрамак будзем мяняць, калі значэнне пераменнай t зменіцца на значэнне  , г. зн. у значэннях: . Пераменная будзе вызначаць, дзе ў бягучы момант часу знаходзіцца матылёк. Значэнне пераменнай ρ вызначым наступным чынам:
    =
  4. З дапамогай лагічнай пераменнай f будзем вызначаць ці патрэбны паварот малюнка ў бягучым пункце.
  5. Калі паварот патрэбен, то для цотных значэнняў p матылёк адлюстроўваецца злева-направа, а для няцотных — зверху-ўніз.

Прыклад 17.13. Форма на этапе праектавання:

Глабальныя пераменныя:

Graphics::TBitmap  

  *kadr = new Graphics::TBitmap(),

  *fon = new Graphics::TBitmap(),

  *fon_kadr = new Graphics::TBitmap();

int w, h, x_luna, y_luna, 

  x_c = 180, y_c =130,

  R_x = 155, R_y =130;

TRect pr_kadr, pr_fon;

double t = 0

Апрацоўшчык падзеі OnClick для кампанента Button1.

void __fastcall TForm1::
Button1Click(TObject *Sender)

{

  Timer1 ->Enabled = true;

}

Апрацоўшчык падзеі OnCreate для формы.

void __fastcall TForm1::
FormCreate(TObject *Sender)

{

  //загрузка малюнкаў

  fon -> LoadFromFile("земля.bmp");

  Image1 -> Picture -> Assign(fon);

  kadr -> LoadFromFile("луна.bmp");

  w = kadr -> Width;

  h = kadr -> Height;

  kadr -> Transparent = true;

  //вызначэнне бітавага вобраза

  //для захоўвання часткі фону

  fon_kadr -> Width = w;

  fon_kadr -> Height = h;

  pr_kadr = Bounds(00, w, h);

  //пачатковае становішча месяца

  x_luna = 180;

  y_luna = 2;

  //захаванне частцы фону пад месяцам

  pr_fon = Bounds(x_luna, y_luna, w, h);

  fon_kadr -> Canvas -> CopyRect

    (pr_kadr, fon -> Canvas, pr_fon);

  //маляванне месяца

  Image1 -> Canvas -> Draw

    (x_luna, y_luna, kadr);

  pi = acos(-1);

}

Апрацоўшчык падзеі OnTimer для таймера.

void __fastcall TForm1::
Timer1Timer(TObject *Sender)

{

  //аднаўленне фону

  Image1 -> Canvas -> Draw

    (x_luna, y_luna, fon_kadr);

  //разлік каардынат эліпса

  t += 0.1;

  if (t > 2 * pi)

    t = 0;

  x_luna = x_c + ceil(R_x * sin(t));

  y_luna = y_c - ceil(R_y * cos(t));

  //захаванне часткі фону

  pr_fon = Bounds(x_luna, y_luna, w, h);

  fon_kadr -> Canvas -> CopyRect

     (pr_kadr, fon -> Canvas, pr_fon);

  //маляванне месяца

  Image1 -> Canvas -> Draw

       (x_luna, y_luna, kadr);

}

 Дадатак у час працы:

Прыклад 17.14. Апрацоўшчык падзеі OnTimer для таймера.

void __fastcall TForm1::Timer1Timer
(TObject *Sender)

{

  //аднаўленне фону

  Image1 -> Canvas -> Draw

    (x_fly, y_fly, fon_kadr);

  += 0.1;

  if (t > 2 * pi)

    t = 0;

  //вызначэнне чвэрці, у якой

  //знаходзіцца матылёк і

  //змена яе пры неабходнасці

  if (t < pi / 2){

    if ( p != 0){

      p = 0;

      f = true;

    }

  }

  else

    if (t < pi) {

      if (p != 1 ){

        p = 1;

        f = true;

      }

    }

    else

      if (t < 3 * pi /2){

        if (p != 2){

           p = 2;

           f = true;

        }

      }

      else

        if (p != 3){

          p = 3;

          f = true;

        }

  //адлюстраванне матылька

  if (f){

    f = false;

    pr_fly = pr_kadr;

    switch (p){

      case 0: case 2:{

        pr_fly.left = pr_kadr.right;

        pr_fly.right = pr_kadr.left;

        break;

      }

      case 1: case 3:{

        pr_fly.top = pr_kadr.bottom;

        pr_fly.bottom = pr_kadr.top;

        break;

      }

    }

    kadr -> Canvas -> CopyRect

      (pr_fly, kadr -> Canvas, pr_kadr);

  }

  //разлік каардынат астроіды

  x_fly = x_c + R_x * cos(t) * cos(t) * cos(t);

  y_fly = y_c - R_y * sin(t) * sin(t) * sin(t);

  //захаванне часткі фону

  pr_fon = Bounds(x_fly, y_fly, w, h);

  fon_kadr -> Canvas -> CopyRect

     (pr_kadr, fon -> Canvas, pr_fon);

  //маляванне матылька

  Image1 -> Canvas -> Draw

     (x_fly, y_fly, kadr);

}

Дадатак у час працы:

17.6. Спрайтавая анімацыя

Выкарыстоўваючы бітавыя вобразы, можна ствараць спрайтавую анімацыю.

Пад спрайтам разумеюць некаторы графічны аб'ект, які можа перамяшчацца па экране без скажэння задняга плана.

Спрайты з'яўляюцца адным з самых папулярных спосабаў стварэння вялікіх і складаных сцэн, так як дазваляюць маніпуліраваць і кіраваць кожным малюнкам паасобку.

Спрайты выкарыстоўваліся пры стварэнні анімацыі ў прыкладах 17.13 (месяц) і 17.14 (матылёк). У гэтых прыкладах спрайт утрымліваў фіксаваны малюнак. Сучасным падыходам да ўжывання спрайтаў у анімацыі з'яўляецца выкарыстанне раскадровак (spritesheet — атлас спрайтаў) — графічных файлаў, якія змяшчаюць некалькі фаз руху якога-небудзь аб'екта (прыклад 17.15). Хуткая змена асобных кадраў дазваляе стварыць ілюзію руху. Такую анімацыю называюць спрайтавай.

Зручна выкарыстоўваць раскадроўкі, у якіх усе кадры маюць аднолькавую шырыню (вышыню).

Алгарытм стварэння анімацыі з выкарыстаннем атласа спрайтаў будзе аналагічны алгарытму, разгледжанаму ў прыкладзе 17.5:

  1. Захаваць частку фону.
  2. Намаляваць першы кадр спрайта.
  3. Запусціць таймер.
  4. Пакуль таймер працуе:
    4.1. аднавіць фон;
    4.2. разлічыць новае становішча аб'екта;
    4.3. захаваць частку фону;
    4.4. перайсці да наступнага кадра;
    4.5. намаляваць кадр.

Прыклад 17.16. Стварыць праект, у якім матылёк будзе лётаць вакол кветкі. Для палёту матылька выкарыстоўваць спрайт з раскадроўкай.

Этапы выканання задання

  1. Анімацыя запускаецца адразу пры загрузцы формы (без кнопкі запуск).
  2. Калі не ўлічваць неабходнасць змены кадраў з атласа спрайтаў, то заданне аналагічна прыкладу 17.13. Разгледзім змену кадраў.
  3. Пры кожным спрацоўванні таймера будзем вылучаць новы кадр з раскадроўкі. Калі гарызантальная каардыната бягучага кадра стала большая, чым шырыня атласа спрайтаў, то вылучэнне пачынаецца спачатку.
  4. Атлас спрайтаў для матылька складаецца з 18 кадраў, таму пры кожным спрацоўванні таймера значэнне пераменнай t будзем змяняць на   .

Слова «спрайт» было прыдумана ў 1970-я гады супрацоўнікам кампаніі Texas Instruments: іх новая мікрасхема TMS9918 магла апаратна адлюстроўваць невялікія карцінкі, якія рухаюцца паверх нерухомага фону.

Прыклад 17.15. Прыклады раскадровак[1]:


[1] Малюнкі бясплатных раскадровак, якія былі узяты з сайта https://ru.freepik.com/

Прыклад 17.16. Раскадроўка матылька (першыя чатыры кадры):

Апісанне пераменных:

Graphics::TBitmap

  *kadr = new Graphics::TBitmap(),

  *spritesheet = new Graphics::TBitmap(),

  *fon = new Graphics::TBitmap(),

  *fon_kadr = new Graphics::TBitmap();

int w, h, x_fly, y_fly, x_k, y_k;

int x_c = 150, y_c =120, R = 120;

//колькасць кадраў

int k = 18;

bool f = false;

TRect pr_kadr, pr_fon, pr_fly;

double t = 0, pi;

Апрацоўшчык падзеі FormCreate для формы.

void __fastcall TForm1::

FormCreate(TObject *Sender)

{

  //загрузка малюнкаў

  fon -> LoadFromFile("podsolnuh.bmp");

  Image1 -> Picture -> Assign(fon);

  spritesheet -> LoadFromFile("fly.bmp");

  w = spritesheet -> Width / 18;

  h = spritesheet -> Height;

  //вызначэнне бітавага вобраза

  //для захоўвання кадра матылька

  kadr -> Width = w;

  kadr -> Height = h;

  kadr -> Transparent = true;

  pr_kadr = Bounds(0, 0, w, h);

  pr_fly  = pr_kadr;

  //пачатковы кадр

  x_k = 0;

  y_k = 0;

  kadr -> Canvas -> CopyRect(pr_kadr,

      spritesheet -> Canvas, pr_fly);

  //вызначэнне бітавага вобраза

  //для захоўвання часткі фону

  fon_kadr -> Width = w;

  fon_kadr -> Height = h;

  //пачатковае становішча матылька

  x_fly = x_c;

  y_fly = y_c - R;

  //захаванне часткі фону пад матыльком

  pr_fon = Bounds(x_fly, y_fly, w, h);

  fon_kadr -> Canvas -> CopyRect

    (pr_kadr, fon -> Canvas, pr_fon);

  //маляванне матылька

  Image1 -> Canvas -> Draw

     (x_fly, y_fly, kadr);

  pi = acos(-1.);

}

Апрацоўшчык падзеі OnTimer для таймера:

void __fastcall TForm1::
Timer1Timer(TObject *Sender)

{

  //аднаўленне фону

  Image1 -> Canvas -> Draw

      (x_fly, y_fly, fon_kadr);

  //новы кадр матылька

  x_k += w;

  if (x_k > w * k)

    x_k = 0;

  pr_fly = Bounds(x_k, y_k, w, h);

  kadr -> Canvas -> CopyRect (pr_kadr,

     spritesheet -> Canvas, pr_fly);

  //новае становішча матылька

  += 0.349;

  if (> 2 * pi)

    t = 0;

  x_fly = x_c + ceil(* sin(t));

  y_fly = y_c - ceil(* cos(t));

  //захаванне часткі фону

  pr_fon = Bounds(x_fly, y_fly, w, h);

  fon_kadr -> Canvas -> CopyRect

    (pr_kadr, fon -> Canvas, pr_fon);

  //маляванне матылька

  Image1 -> Canvas -> Draw

       (x_fly, y_fly, kadr);

}

Пытанні да параграфа

1. Які кампанент выкарыстоўваецца для адліку часу?

2. Як запраграміраваць найпростую анімацыю?

3. Якая функцыя дазваляе скапіраваць фрагмент малюнка з адной канвы на іншую?

4. Як здзяйсняецца адлюстраванне малюнкаў?

5. Што такое спрайт?

6. Як арганізавана спрайтавая анімацыя?


Практыкаванні

1. Дадайце ў праект з прыкладу 17.5 хвілінную стрэлку (для гэтага зручна выкарыстоўваць яшчэ адзін кампанент timer, стрэлку маляваць на асобным кампаненце Image). Дадайце кнопку «Стоп» для прыпынку таймера.

2. Стварыце анімацыю руху шарыка (мяча) у прамавугольнай «скрынцы». Удары аб сценку абсалютна пругкія. Шарык можна прамалёўваць пры кожным спрацоўванні таймера або загрузіць яго ў асобны кампанент Image. *Дадайце тэкставыя палі для ўводу пачатковага становішча шарыка (мяча) і вугла (вугал паміж левай сценкай скрынкі і лініяй траеторыі руху).

        

3. Стварыць праект, у якім над малюнкам будуць праводзіцца аперацыі адлюстравання і маштабавання.

4. Стварыце праект, у якім неабходна аніміраваць падзенне яблыка з вежы. Рух ажыццяўляецца па парабале. Яблык павялічваецца пры набліжэнні да зямлі.

5. Стварыце праект з выкарыстаннем спрайтавай анімацыі. Фон і персанажаў выберыце самастойна.