ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 23.12.2021

Просмотров: 110

Скачиваний: 2

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

Лекція 11. Анімація

Це, напевно, сама цікава тема. Під анімацією ми з вами будемо розуміти зображення екрану, яке швидко змінюється, нарисовані об’єкти якого змінюють свої координати і тим самим, рухаються по екрану. Ми можемо задати їм визначену траєкторію руху або примусити їх підкорятись деяким законам, які, можливо, будуть динамічно змінюватись в ході виконання програми.

Поміркуємо, як можна організувати анімацію. Нам потрібен цикл, в тілі якого будуть послідовно формуватись кадри зображення і швидко виводитись на екран. Перед рисуванням кожного кадра, треба очищати всю область екрана і потім рисувати уже змінену картинку. Вихід із циклу — натискання будь-якої клавіші. Всередині циклу поставимо невелику затримку, щоб людське око встигало сприймати кожний нарисований кадр.

Приклад. Написати програму, в якій кулька рухається зліва направо. При досягненні правої межі вона перестрибує на своє початкове положення, і все повторюється спочатку.

Для рисування кульки (замість кульки може бути будь-яка інша фігура), ми визначимо функцію Draw(x,y), при виклику якої буде відбуватись рисування зображення.

/* рухаюча кулька */

# include <conio.h>

# include <graphics.h>

# include <dos.h>

void Draw (int x1, int y1)

{

circle( x1,y1,20 );

}

void main( )

{

int x,y;

int gd=DETECT, gm;

x=20; // ініціалізація початкових

y=200; // координат кульки

initgraph ( &gd, &gm,” ” );

while ( !kbhit( ) ) // цикл: поки не натиснута

{ // будь-яка клавіша, виконувати

cleardevice( ); // очищення екрану

Draw (x,y); // рисуємо кульку по координатах (x,y)

delay (10); // затримка

х++; // зміщуєм кульку вправо на один піксель

if (x>620) x=20; // якщо дойшли до правої межі, то х=20

}

getch( );

closegraph( );

}

Головний цикл програми — while. Функція kbhit() повертає не нуль, якщо була натиснута будь-яка клавіша і нуль, якщо буфер клавіатури пустий. Функція описана в файлі conio.h. За допомогою функції dalay (мілісек.) із бібліотеки dos.h, ми створюємо затримку на 10 мілісекунд для кожного кадру.

В написаній програмі є один суттєвий недолік — зображення блимає. Чому так відбувається? Річ в тому, що функція cleardevice( ) працює не дуже швидко, тому що потрібно зафарбовувати весь екран, наше око не встигає впіймати момент, коли на екрані нічого немає. Що ж робити? Перше, що приходить в голову — це спробувати очищати не весь екран, а тільки ту область, де намальований об’єкт. Давайте перепишемо програму так, щоб спочатку рисувалась кулька кольором фону за старими координатами, а потім, іншим кольором, на новому місці.

/* рухаюча кулька */

# include <conio.h>

# include <graphics.h>

# include <dos.h>

void Draw (int x1, int y1)

{

circle( x1,y1,20 );

}

void main()

{

int x,y;

int gd=DETECT, gm;

x=20;

y=200;

initgraph( &gd, &gm, ” ” );

setbkcolor(1); // встановлює колір фону 1 - синій

while (!kbhit())

{

setcolor(1); // рисуємо кольором

Draw (x,y); // фону кульку по старим

х++; // координатам (х,у)


if (x>620) x=20;

setcolor(10); // рисуємо зеленим

Draw (x,y); // кольором кульку по

delay (10); // обновленим координатам (х,у)

}

getch();

closegraph();

}

Якщо у вас комп’ютер достатньо швидкий, то ви помітите невелике поліпшення, але блимання повністю не зникне. І чим складніше фігура в функції Draw, тим довше працює замальовування і стирання зображення, і тим більше помітно блимання. Тобто такий засіб підходить тільки для анімації простих рисунків.

Для того, щоб можна було звільнитись від блимання, необхідно зрозуміти принцип роботи монітора і механізм формування зображення. Всередині кінескопу є так називаєма електронно-струменева пушка, яка “стріляє” електронами (зарядженими частинками) по внутрішній поверхні екрана. Ця поверхня покрита спеціальною люмінісцентною речовиною, яка світиться при зіткненні з електронами. Пучок електронів можна відхиляти за допомогою магнітного поля і при необхідності виключати. В кожний момент часу на екрані світиться тільки одна точка — те місце, куди летять електрони. Ця світящася пляма починає рухатись з лівого верхнього кута по горизонталі. Доходячи до кінця екрану, промінь вимикають і переводять на початок слідуючого рядка. Цей процес має назву горизонтальної ретрасировки. Так продовжується доти, поки промінь не пройде всі рядки екрана, і не опиниться в правому нижньому куту. Потім промінь знову вимикають і переводять в початок екрану. Це — вертикальна ретрасировка. Промінь “бігає” настільки швидко, що людське око сприймає всі точки, як одне ціле. За одну секунду екран монітора оновлюється 60 разів. В цьому випадку говорять, що вертикальна розгортка екрана 60Гц. Чим вище це число, тим менше стомлюється око. Безпечним вважається значення, яке дорівнює 75Гц і вище. Зараз існують монітори з частотою вертикальної розгортки до 120Гц.

Дані про зображення беруться з відеопам’яті. Це є ніщо інше, як масив пікселів. І коли ми “ рисуємо ” щось на екрані, ми насправді записуємо нові значення в цей масив. При проходженні променя по екрану, зчитується значення відповідного пікселя з відеопам’яті, і піксель виводиться на екран потрібним кольором (інтенсивністю). Що ж буде, якщо ми запишемо в відеопам’ять нові значення пікселів в той момент, коли у нас вже частина попереднього зображення сформована? Пам’ять оновлюється (швидкість читання/запису в пам’ять набагато більша, ніж швидкість переміщення променя по екрану) і далі, промінь уже буде малювати вже новий кадр.

В прикладі приводиться функція, яка чекає початку вертикальної ретрасировки. Вона перевіряє четвертий біт порта 0х3DA. Якщо він дорівнює 1, то буде вертикальна ретрасировка, якщо 0, то зображення кадру ще не до кінця сформовано. Як тільки промінь досяг правого нижнього кута, виставляється 1 і функція завершує роботу, даючи можливість швидко відновити відеопам’ять.


Функцію доцільно ставити безпосередньо перед перемалюванням відеосторінок.

void WaitWerticalRetrace( )

{

while (inportb(0x3DA)&0x08);

while (!inportb(0x3DA)&0x08);

}

Приклад: Написати програму, в якій кулька, яка має початкову швидкість рухається рівномірно по екрану, відскакуючи від стінок.

/* Міні більярд */

#include <graphics.h>

#include <stdio.h>

#include <conio.h>

#include <dos.h>

void WaitWerticalRetrace( )

{

while (inportb( 0x3DA )&0x08);

while (!inportb( 0x3DA )&0x08);

}

void main( void )

{

int gd=DETECT, gm;

int x,y,dx,dy;

initgraph(&gd, &gm, "" );

setbkcolor(1); // колір фону 1 - синій

x=100;

y=100; // задаємо початкові координати

dx=2; // і швидкість по горизонталі

dy=2; // і по вертикалі

while( !kbhit ( ) )

{

setcolor(1);

WaitWerticalRetrace( ); // ждемо початку

circle(x,y,15); // вертикальної ретрасировки

x+=dx; // збільшуємо х

y+=dy; // збільшуємо у

if( x>625 || x<15 )

dx=-dx; // якщо край екрану, то

if( y>465 || y<15 ) // змінюємо знак у dx

dy=-dy;

setcolor(14);

circle( x,y,15 );

}

closegraph( );

}


Функції для роботи з зображенням

Для збереження зображень і їх послідуючого відтворення використовують функції getimage, imagesize, putimage.

Для того, щоб запам’ятати бітове зображення повинен бути виділений необхідний об’єм пам’яті прямокутника екрану за допомогою функції imagesize.

Її прототип unsigned far imagesize(int left, int top, int right, int bottom); де left, top – координати лівого верхнього кута прямокутника;

right, bottom – координати правого нижнього кута прямокутника.

Функція getimage запам’ятовує в ОП бітове зображення прямокутної частини екрана. Її прототип:

void far getimage(int left, int top, int right, int bottom, void far *bitmap);

Перші 4 параметри подібні попереднім, а bitmap – безтиповий (void) покажчик на область ОП, в якій запам’ятовується зображення прямокутника. Функція putimage призначена для відтворення в заданому місці екрану зображення прямокутника, який запам’ятала функція getimage.

Її прототип:

void far putimage(int left, int top, void far *bitmap, int op);

де left, topкоординати лівого верхнього кута прямокутника;

bitmap – покажчик на область ОП, де зберігається зображення;

ор – умова виведення кольорів пікселів;

Можливі такі значення:

COPY_PUT 0 виведення копії зображення на екран

Для коду кольорів зображення:

XOR_PUT 1 виключне АБО

OR_PUT 2 об’єднуюче АБО

AND_PUT 3 логічне І

NOT_PUT 4 інверсія коду

При використанні значення ор=0 на екран виводиться копія зображення. При виведенні на те ж саме місце екрана того ж зображення з параметром ор=1 старе зображення стирається з екрану.

Приклад формування зображення білого квадрату, потім воно запам’ятовується по адресу, який містить покажчик ptr, після чого екран очищується і виводиться зображення цього ж квадрату в іншому місці екрану.

В програмі використовуються стандартні змінні для графічного режиму, n, і — кількість і номер чергових координат; x, dx, y, dy – координати і їх приріст; size – розмір необхідної ОП; ptr – покажчик на ОП для розміщення копії.

#include <graphics.h>

#include <stdio.h>

#include <conio.h>

#include <stdlib.h>

#include <dos.h>

void main()

{

int gdriverЮTECT, gmode, errorcode;


int n ,x,dx,y,dy,i;

unsigned int size;

void *ptr;

initgraph(&gdriver,&gmode,"");

errorcode=graphresult();

if(errorcode!=grOk)

{

printf("Помилка");

getch();

exit(1);

}

// орнамент – суцільне заповнення білим кольором

setfillstyle(1,15);

bar(10,30,40,60); // малювання білого квадрату

// запрос ОП для квадрату

size=imagesize(10,30,40,60); //розмiр ОП

ptr=malloc(size); // запрос ОП

// запам’ятовування в буфері бітового образу частини пам’яті

getimage(10,30,40,60,ptr);

setbkcolor(0); // колір фону – чорний

cleardevice();

dx=getmaxx()/n; // крок зміщення по х

dy=getmaxy()/n;

x=0;y=0;

for(i=1;i<=n;i++)

{

putimage(x,y,ptr,0); // виведення білого квадрату

delay(300); // затримка зображення

putimage(x,y,ptr,1); // стирання квадрату

x=x+dx; y=y+dy; // нові координати

}

free(ptr); // вивільнення ОП для квадрату

closegraph(); // закрити графічний режим

}


/* Кулька ударяється об стінки */

#include<graphics.h>

#include<stdio.h>

#include<conio.h>

#include<dos.h>

void WaitWerticalRetrace()

{

while(inportb(0x3DA)&0x08);

while(!inportb(0x3DA)&0x08);

}

void main(void)

{

int gd=DETECT,gm;

int x,y,dx,dy;

initgraph(&gd,&gm,"");

setbkcolor(1);

x=100;

y=100;

dx=2;

dy=2;

while(!kbhit())

{

setcolor(1);

WaitWerticalRetrace();

circle(x,y,15);

x+=dx;

y+=dy;

if(x>625||x<15)

dx=-dx;

if(y>465||y<15)

dy=-dy;

setcolor(14);

circle(x,y,15); delay(5);

}

closegraph();

}


7