C++ Builder: 3 способа сделать анимацию
В этой заметке, на примере учебного С++ Builder 6 со стандартной библиотекой компонентов VCL, покажем 3 основных способа организовать в программе цикл отрисовки с движением объектов по графической канве.
1. Сначала сделаем плавно двигающийся в море (окне формы) кораблик. Для этого подготовим 2 картинки, обычные BMP-шки, которые скопируем в папку проекта:
sea.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 [31597 просмотров]