Шаг 78.
Microsoft Visual C++ 2010. Начала.
Программирование. Графика. Графические примитивы. Анимация "на лету"

    На этом шаге мы рассмотрим общие принципы создания такой анимации.

    В программе Digital Clock предыдущего шага для формирования динамической картинки использовался "классический" подход создания анимации, предполагающий наличие заранее подготовленной серии картинок (кадров), последовательное отображение которых и создает эффект анимации. Возможен и другой подход к созданию динамических изображений, когда картинка (кадр) формируется из заранее подготовленных фрагментов "на лету", во время работы программы.

    Типичным примером такой анимации является перемещение объекта на фоне какой-либо картинки. Чтобы у наблюдателя сложилось впечатление, что объект движется, надо вывести изображение объекта, затем, через некоторое время, стереть его и снова вывести, но уже на некотором расстоянии от первоначального положения. Подбором времени между удалением и выводом изображения, а также расстояния между новым и предыдущим положением объекта (шага перемещения) можно добиться эффекта равномерного движения.

    Программа "Полет" (ее форма и окно приведены на рисунках 1 и 2) демонстрирует принципы создания анимации "на лету", показывает, как заставить объект двигаться.


Рис.1. Форма программы "Полет"


Рис.2. Окно программы "Полет"

    Текст приложения.

.    .    .    .    .
// конструктор 
Form1(void)
{
	InitializeComponent();
	//
	//TODO: добавьте код конструктора
	//
	try 
	{
		sky = gcnew System::Drawing::Bitmap (Application::StartupPath 
				+ "\\sky.bmp"); 
		plane = gcnew System::Drawing::Bitmap (Application::StartupPath 
				+ "\\plane1.jpg"); 
	}
	catch (System::Exception^ e)
	{
		MessageBox::Show( "Ошибка загрузки битового образа: " + e->Message, 
			"Полет",MessageBoxButtons::OK,MessageBoxIcon::Error); 
		this->Paint += nullptr; 
		return; 
	}
	// сделать прозрачным фон 
	plane->MakeTransparent(); 
	// установить размер формы равным 
	// размеру фонового рисунка 
	this->ClientSize = sky->Size; 
	// задать фоновый рисунок формы 
	this->BackgroundImage = gcnew Bitmap(sky); 
	// g - графическая поверхность, на которой будем формировать рисунок 
	// определяем графическую поверхность 
	g = this->CreateGraphics(); 
	// инициализация генератора случайных чисел 
	rnd = gcnew System::Random(); 
	// исходное положение самолета 
	rct.X = -40; 
	rct.Y = 20 + rnd->Next(20);
	rct.Width = plane->Width; 
	rct.Height = plane->Height; 
	/* скорость полета определяется периодом следования 
	сигналов от таймера и величиной приращения координаты X 
	*/ 
	dx = 2; // скорость полета - 2 пиксела/тик_таймера 
	timer1->Interval = 20; 
	timer1->Enabled = true; 
}
.    .    .    .    .
private: 
	System::Drawing::Bitmap^ sky; 
	System::Drawing::Bitmap^ plane; 
	// графическая поверхность, на которой формируется изображение 
	Graphics^ g; 
	// приращение координаты X, 
	// определяет скорость полета 
	int dx; 
	// область, в которой находится объект 
	Rectangle rct; 
	// генератор случайных чисел 
	System::Random^ rnd;

// сигнал от таймера 
private: System::Void timer1_Tick(System::Object^  sender, System::EventArgs^  e) {
	 // Стереть изображение объекта - вывести фрагмент фона 
	 // в ту область графической поверхности, 
	 // в которой сейчас находится объект 
	 g->DrawImage(sky, Rectangle(rct.X,rct.Y,rct.Width,rct.Height), 
		 Rectangle(rct.X,rct.Y,rct.Width,rct.Height), 
		 GraphicsUnit::Pixel); 
	 // вычислить новое положение объекта 
	 if (rct.X < this->ClientRectangle.Width) 
		 rct.X += dx; 
	 else 
	 {
		 // объект достиг правой границы, 
		 // перемещаем его к левой границе 
		 rct.X = -40; 
		 rct.Y = 20 + rnd->Next(40); 
		 // скорость полета от 2 до 5 пикселов/тик_таймера 
		 dx = 2 + rnd->Next(4); 
	 }
	 // вывести изображение объекта на новом месте 
	 g->DrawImage(plane,rct.X,rct.Y);
 }
Архив проекта можно взять здесь.

    В рассматриваемой программе фоновый рисунок и изображение объекта загружаются из файлов. Конструктор формы обеспечивает загрузку битовых образов, задает начальное положение объекта, выполняет настройку и запуск таймера. Следует обратить внимание на то, что начальное значение поля X структуры rec, которое определяет положение левого верхнего угла битового образа (движущейся картинки) — отрицательное число, равное ширине битового образа. Поэтому в начале работы программы самолет не виден, картинка отрисовывается за границей видимой области. С каждым событием Tick значение координаты X увеличивается, и на экране появляется та часть битового образа, координаты которой больше нуля. Таким образом, у наблюдателя создается впечатление, что самолет вылетает из-за левой границы окна. Процедура обработки сигнала от таймера (события Timer) выполняет основную работу. Сначала она стирает изображение объекта (восстанавливает ''испорченный" фоновый рисунок), затем выводит изображение объекта на новом месте. Восстановление фона выполняет метод DrawImage путем вывода фрагмента фонового рисунка (битового образа sky) в область графической поверхности, в которой находится в данный момент объект (рисунок 3).


Рис.3. Метод DrawImage позволяет вывести в указанную область графической поверхности фрагмент битового образа

    Следует обратить внимание на то, что в функции обработки события Timer для доступа к графической поверхности используется переменная g. Эта переменная хранит ссылку на графическую поверхность окна программы. Ее значение устанавливает конструктор формы: метод CreateGraphics возвращает ссылку на графическую поверхность объекта this, т. е. формы. Такой "хитрый" способ используется потому, что доступ к графической поверхности (через параметр e) есть у функции обработки события Paint и, в принципе, выводить графику должна процедура обработки именно этого события. В нашем примере картинка обновляется несколько раз в секунду и перерисовка окна (а именно это делает функция обработки события Paint) привела бы к заметному мерцанию картинки.

    На следующем шаге мы начнем рассматривать работу с базами данных.




Предыдущий шаг Содержание Следующий шаг