§ 17. Анимация

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);

}

Работающее приложение.