БлогNot. C++ Builder: 3 способа сделать анимацию

C++ Builder: 3 способа сделать анимацию

В этой заметке, на примере учебного С++ Builder 6 со стандартной библиотекой компонентов VCL, покажем 3 основных способа организовать в программе цикл отрисовки с движением объектов по графической канве.

1. Сначала сделаем плавно двигающийся в море (окне формы) кораблик. Для этого подготовим 2 картинки, обычные BMP-шки, которые скопируем в папку проекта:

sea.bmp
sea.bmp
ship.bmp
ship.bmp

Здесь рисунки лежат в формате GIF, полагаю, их легко конвертировать.

Создав новый проект, размеры клиентской части окна формы ClientWidth и ClientHeight установим равными размерам фонового рисунка с морем, 327 на 245 пикселов, также запретим изменять размеры формы (свойство BorderIcons->biMaximize=false, а BorderStyle=bsSingle). На форму добавим обычный рисунок TImage с вкладки компонентов Additional и "растянем" его на всю форму, поставив свойство Align=alClient.

На форму также добавим объект TTimer с вкладки компонентов System. В его свойствах установим нужный интервал обновления в миллисекундах (1000 мс = 1 сек). Например, подойдёт Interval=50.

В классе формы или вне его опишем нужные глобальные данные:

Graphics::TBitmap *Background, *Bitmap, *Buf;
 //картинки для фона, кораблика и буфер для хранения части рисунка под корабликом
int W,H,x,y; //ширина и высота кораблика, его координаты
TRect BufRct,BackRct; //прямоугольные области, понадобятся для передачи данных в метод CopyRect

Создадим нужные графические объекты в обработчике события OnCreate нашей формы:

// создать три объекта - битовых образа
Background = new Graphics::TBitmap(); // фон
Bitmap = new Graphics::TBitmap(); // картинка
Buf = new Graphics::TBitmap(); // буфер
// загрузить и вывести фон
Background->LoadFromFile("sea.bmp");
Form1->Image1->Canvas->Draw(0,0,Background);
// загрузить картинку, которая будет двигаться
Bitmap->LoadFromFile("ship.bmp");
// определим "прозрачный" цвет 
Bitmap->Transparent = true;
Bitmap->TransparentColor = Bitmap->Canvas->Pixels[1][1];
// создать буфер для сохранения копии области фона,
// на которую накладывается картинка
W= Bitmap->Width;
H= Bitmap->Height;
Buf->Width= W;
Buf->Height=H;
Buf->Palette=Background->Palette; // Чтобы обеспечить соответствие палитр
Buf->Canvas->CopyMode=cmSrcCopy;
// определим область буфера, которая будет использоваться
// для восстановления фона
BufRct=Bounds(0,0,W,H);
// начальное положение картинки
x = 0;
y = 100;
// определим сохраняемую область фона
BackRct=Bounds(x,y,W,H);
// и сохраним ее
Buf->Canvas->CopyRect(BufRct,Background->Canvas,BackRct);
Form1->DoubleBuffered = true;

Теперь нужно выбрать на форме таймер и запрограммировать единственный имеющийся у него обработчик события OnTimer:

// восстановлением фона (из буфера) удалим рисунок
Form1->Image1->Canvas->Draw(x,y,Buf);
x++; //Поплывет вправо
if (x>Form1->Image1->Width) x=-W;  //потом снова приплывет слева
// определим сохраняемую область фона
BackRct=Bounds(x,y,W,H);
// сохраним ее копию
Buf->Canvas->CopyRect(BufRct,Background->Canvas,BackRct);
// выведем рисунок
Form1->Image1->Canvas->Draw(x,y,Bitmap);

Всё готово, кораблик движется плавно и красиво, попробуйте собрать этот проект :)

2. Во втором примере (новом проекте) для отрисовки будем использовать системное событие Application->OnIdle, которое "происходит" всегда, когда процессор не занят другими делами. Создадим новый проект и опишем в виде структуры простейший объект - прямоугольный спрайт. Из этих спрайтов определим массив объектов размерности Num. Будет даже чем-то похоже на реальное приложение :)

const int Num = 60;
struct { 
 int x,y; //координаты на экране
 int dx,dy; //шаг по осям X и Y
 int w,h; //размеры
 int color; //цвет
} Objects[Num];

В конструкторе формы (это единственная функция, которая автоматически создаётся в пустом модуле) назначим обработчик события:

Application->OnIdle = &WhenIdle;

Переключившись на заголовочный файл Unit1.h, добавим в пустой список методов класса Form1 заголовок функции WhenIdle:

void __fastcall TForm1::WhenIdle (TObject *Sender, bool &Done);

Вернёмся обратно к файлу Unit1.cpp и запрограммируем тело новой функции:

void __fastcall TForm1::WhenIdle (TObject *Sender, bool &Done) {
 Done = false; //Говорим"рисовать немедленно"
 Func(); //А рисовать будет код из Func
}

Саму Func() я, для разнообразия, членом класса не сделал:

void Func() {
 TRect *R;
 for (int i=0; i<Num; i++) { //цикл по всем объектам
  Form1->Canvas->Brush->Color = Objects[i].color;
  R=new TRect(Objects[i].x,Objects[i].y,Objects[i].x+Objects[i].w,Objects[i].y+Objects[i].h);
   //место, занимаемое объектом на экране
  Form1->Canvas->FillRect(*R); //нарисовали
  Objects[i].x+=Objects[i].dx;
  //отражаем от краев формы по оси x
  if (Objects[i].x<0) { Objects[i].x=0; Objects[i].dx=1; }
  else if (Objects[i].x>Form1->ClientWidth-Objects[i].w) {
   Objects[i].x=Form1->ClientWidth-Objects[i].w;
   Objects[i].dx=-1;
  }
  //Такой же код для обработки оси Y 
  Objects[i].y+=Objects[i].dy;
  if (Objects[i].y<0) { Objects[i].y=0; Objects[i].dy=1; }
  else if (Objects[i].y>Form1->ClientHeight-Objects[i].h) {
   Objects[i].y=Form1->ClientHeight-Objects[i].h;
   Objects[i].dy=-1;
  }
 }
}

Здесь объекты не сохраняют экран под собой, так что при запуске приложения мы увидим пересекающиеся цветные полосы. Все Num объектов надо инициализировать, например, всё в том же обработчике события OnCreate формы:

randomize();
for (int i=0; i<Num; i++) {
 Objects[i].w = 20+random(21);
 Objects[i].h = 20+random(21);
 Objects[i].x=random(Form1->ClientWidth-Objects[i].w);
 Objects[i].y=random(Form1->ClientHeight-Objects[i].h);
 Objects[i].dx=random(2)==0?-1:1;
 Objects[i].dy=random(2)==0?-1:1;
 Objects[i].color=(TColor)RGB(random(256),random(256),random(256));
}

Если ругается на randomize(), подключите к модулю стандартную библиотеку C++:

#include <stdlib.h>

Приложение можно запускать, оно закончено.

3. Наконец, третий проект покажет задержку с помощью системной функции Sleep (мс), её параметр - опять-таки время в миллисекундах. Для простоты сделаем прыгающий "мячик" на основе компонента TShape с вкладки инструментов Additional. Удобство в том, что TShape сам умеет сохранять под собой экран, а неудобство - он может принимать только одну из предустановленных простейших форм.

На этот раз мы запустим процесс по щелчку на форме, значит, этот код нужно вставить в обработчик события OnClick формы. Также используем свойство Tag, которое есть у очень многих компонентов, для контроля выхода из цикла. Выход будет по нажатию любой клавиши.

Shape1->Tag = 0;
Shape1->Top = 0;
Shape1->Shape = stCircle; 
int dt=10; //шаг 10 пикселов
do {
 Shape1->Top += dt;
 if (Shape1->Top < 1 || Shape1->Top + Shape1->Height >= Form1->ClientHeight) dt=-dt;
 Application->ProcessMessages ();
  //Вызов этой функции приложение, что-то перерисовывающее на канве, должно 
  //периодически делать - это просмотр очереди событий
 Sleep (20);
} while (Shape1->Tag==0);

Чтобы приложение прореагировало на нажатие клавиши, запишем одну строчку кода в обработчик события OnKeyDown формы:

Shape1->Tag = 1;

"Мячик" должен двигаться вверх и вниз, отражаясь от краёв формы.

02.10.2013, 11:32 [31276 просмотров]


теги: программирование c++ графика builder

показать комментарии (3)