Druhá časť seriálu je venovaná práci s grafikou. Vzhľadom na to, že prevažná časť knižnice Allegro je venovaná práve práci s grafikou, nie je v tomto článku možné prebrať všetky aspekty a ponúkané možnosti. Preto ho treba chápať len ako stručný úvod do problematiky.
Článok bude opäť obohatený o príklady. Budeme pokračovať tam, kde sme v predchádzajúcom článku skončili. Čitateľov však nabádam k tomu, aby si všetky príklady vyskúšali naprogramovať samostatne a taktiež skúšali aj funkcie, ktoré nebudú v tomto článku použité. Tréning robí majstra a programovať sa nedá naučiť inak ako samotným programovaním. Na konci článku nájdete odkazy na všetky relevantné manuálové stránky.
Princíp práce s grafikou
Všetky grafické operácie v Allegre, vrátane vstupu a výstupu, pracujú s bitmapovou (alebo rastrovou) grafikou zloženou z pixelov. Pixel je základná jednotka bitmapovej grafiky, pričom každý z nich má jedinú vlastnosť - farbu. Od tejto farby sa dajú odvodiť niektoré dodatočné vlastnosti, ako napríklad priesvitnosť. Počet rôznych farieb (hodnôt), ktoré môže nadobúdať, určuje farebná hĺbka, ktorá je ekvivalentná počtu bitov, ktoré jeden pixel obsadzuje v pamäti. Allegro podporuje nasledovné farebné hĺbky (v bitoch): 8, 15, 16, 24 a 32. Samotná číselná hodnota farby však nemá ešte žiadnu výpovednú hodnotu. Farba v našom ponímaní, ktorú ľudské oko vníma, je totiž zložená z viacerých zložiek. Teória o farbách je už mimo témy tohto článku, ale v prípade záujmu môžete začať svoju púť za hlbším poznaním na wikipedii. Dôležité je spomenúť, že Allegro využíva trojzložkový RGB model, kde každá zložka môže nadobúdať maximálne 256 rôznych hodnôt (8-bit). Plnú možnú informáciu teda nesie pri 24-bitoch (RGB) a pri 32-bitoch (RGBA), kde okrem farebných zložiek nájdeme aj alfa zložku (v Allegre využívanú napríklad na priesvitnosť). Pri 16-bitovom režime sú zložky RGB rozdelené po 5-6-5 bitoch, keďže na zmeny zelenej farby je ľudské oko najcitlivejšie. Pri 15-bitovom je to 5-5-5 a pri 8-bitovom nie sú farebné zložky zakódované priamo vo farbe pixelu, ale používa sa dodatočná tabuľka obsahujúca 256 32-bitových RGB záznamov, nazývaná paleta. V dnešnej dobe je však používanie 8-bitového režimu v útlme a tak sa mu ani nebudeme bližsie venovať. Režimy, ktoré budeme prakticky používať, sú 16, 24 a 32-bit. Je dôležité si pamätať, že farebná hĺbka je globálna premenná, ktorá sa nastavuje volaním funkcie void set_color_depth(int depth); a od momentu nastavenia bude používaná pri volaní všetkých grafických funkcií.
Pixely sú teda jednotky tvoriace obraz. Pristupovať k nim (kvôli zmene alebo čítaniu) je možné zadaním ich súradníc priamo v pixeloch (označuje sa px). Súradnicová sústava (jej orientácia) je možno iná, na akú ste zvyknutí z matematiky. Keďže prvé obrazovky boli určené hlavne na textový výstup a v západnom svete sa číta zľava doprava a zhora dole, súradnicová sústava využívaná pri vykresľovaní má počiatok v ľavom hornom rohu, pričom súradnica X rastie smerom k pravému hornému rohu a súradnica Y smerom k ľavému dolnému rohu.
Na obrázku je naznačená aj os Z, ktorá vyjadruje "hĺbku". Stretneme sa s ňou až neskôr, pri článkoch o 3D grafike.
V Allegre sa na "uskladnenie" grafiky používa štruktúra BITMAP. Výťah dôležitých premenných:
int w, h; /* width and height in pixels */
int clip; /* flag if clipping is turned on */
int cl, cr, ct, cb; /* clip left, right, top and bottom values */
void *dat; /* the memory we allocated for the bitmap */
Táto obsahuje samotné pixely (resp. ich hodnoty) po riadkoch, rozmery a informácie o prípadnom orezaní (regióne, do ktorého je možné kresliť). Po inicializácii grafického režimu sa automaticky nastaví globálna premenná extern BITMAP *screen;, ktorá reprezentuje obrazovku. Je teda možné do nej ihneď začať kresliť. Orezávanie je zapnuté a nastavené na jej aktuálne rozmery a tým pádom sa nič nestane, ak sa pokúsite vykresliť niečo mimo, príkaz sa proste nevykoná. Ak by ste deaktivovali kontrolu orezávania, program by Vám pri pokuse vykresliť niečo mimo obrazovky skoro určite spadol. Prejdime si teda prvé príkazy:
- int makecol(int r, int g, int b); - Skonvertuje farbu zadanú ako RBG zložky na hodnotu pre aktuálnu farebnú hĺbku.
- int getr(int c); - Vyextrahuje červenú zložku zo zadanej hodnoty.
- int getg(int c); - Vyextrahuje zelenú zložku zo zadanej hodnoty.
- int getb(int c); - Vyextrahuje modrú zložku zo zadanej hodnoty.
- void putpixel(BITMAP *bmp, int x, int y, int color); - Nakreslí jeden pixel danej farby na zadanú pozíciu.
- int getpixel(BITMAP *bmp, int x, int y); - Prečíta farbu pixelu na zadanej pozícii.
Niektoré z týchto funkcií sú použité v nasledujúcom príklade (celý zdrojový kód).
while(!close_button_pressed){ int farba=makecol(rand()%256,rand()%256,rand()%256); putpixel(screen,rand()%screen->w,rand()%screen->h,farba); rest(10); }
Primitívy
Po vykreslení prvých pixelov prichádzajú na rad primitívy. Allegro ich pozná niekoľko, pričom sa snaží využívať aj možnú akceleráciu grafickej karty (napríklad pri úsečkách). Tu je zoznam niektorých funkcií na ich vykresľovanie:
- void line(BITMAP *bmp, int x1, int y1, int x2, int y2, int color); - Nakreslí úsečku v zadanej farbe.
- void circle(BITMAP *bmp, int x, int y, int radius, int color); - Nakreslí kružnicu v zadanej farbe.
- void rect(BITMAP *bmp, int x1, int y1, int x2, int y2, int color); - Nakreslí obdĺžnik v zadanej farbe.
- void floodfill(BITMAP *bmp, int x, int y, int color); - Vyplní región okolo zadaných súradníc farbou.
Zoznam všetkých funkií nájdete v manuáli.
Niektoré z týchto funkcií sú použité v nasledujúcom príklade (celý zdrojový kód).
int x=screen->w/2; int y=screen->h/2; int ax,ay; while(!close_button_pressed){ int farba=makecol(rand()%256,rand()%256,rand()%256); line(screen,x,y,ax=rand()%screen->w,ay=rand()%screen->h,farba); x=ax; y=ay; if(rand()&1){ circle(screen,x,y,5,farba); } else{ rect(screen,x-5,y-5,x+5,y+5,farba); } rest(300); }
V príklade je náhodne vybranou farbou nakreslená úsečka z bodu [X,Y] do bodu [AX,AY] a následne je okolo tohto bodu vykreslená kružnica alebo štvorec.
Schopnosť vykresliť bodky (príklad 1) alebo čarbanice (príklad 2) síce poteší, ale teraz je už načase načrieť trochu viac do programátorských a matematických schopností v nasledujúcom príklade (celý zdrojový kód).
#define DLZKA 512 struct XY{ int x,y; }; int x=screen->w/2; int y=screen->h/2; XY xy[DLZKA],a; float uhol=0; for(int i=0;i<DLZKA;i++){ xy[i].x=x; xy[i].y=y; } int pos=0; while(!close_button_pressed){ for(int i=0;i<DLZKA;i++){ a=xy[(pos+i)%DLZKA]; if(i+pos>=DLZKA){ circlefill(screen,a.x,a.y,10,makecol((i+1)*255/DLZKA,0,0)); } } a.x+=(int)(cos(uhol)*8); a.y+=(int)(sin(uhol)*8); uhol+=(float)(rand()%101-50)/100; if(a.x<0||a.y<0||a.x>=screen->w||a.y>=screen->h) uhol+=1; xy[(pos++)%DLZKA]=a; rest(20); }
Pred očami sa nám začal chaoticky hadiť červený had Nehad, pričom jeho chvost postupne slabne, až sa stráca úplne. Samotný Nehad je tvorený určitým počtom kruhov, pričom kruh na začiatku hada má sýtu červenú farbu a všetky ďalšie kruhy majú postupne farbu menej sýtu. Začiatok má teda červenú zložku na maxime a ostatné zložky na nule. Budeme to zapisovať ako (255,0,0)
. Úplne posledný krúžok hada má farbu už úplne čiernu, teda (0,0,0)
. Krúžky sú vykresľované pri každom pohybe a kreslia sa od chvosta (od najtmavšieho) po hlavu (po najsvetlejší). Všetky sa vykresľujú priamo na obrazovku, čo môže spôsobovať škaredé blikanie, ak sa jednotlivé krúžky navzájom prekrývajú. Ako to len vyriešiť?
Pamäťové bitmapy
Ako sme videli v predchádzajúcom príklade, vykresľovanie priamo na obrazovku nie je to pravé. Preto sa využíva niekoľko techník. Prvou je takzvaný double- alebo triple buffering. Ide o techniku, kedy existuje v pamäti grafickej karty viac obrazovkových bufferov súčasne, pričom viditeľný je vždy len jeden z nich (aktívny), kým do druhého (alebo tretieho) sa kreslí. Keď je kreslenie dokončené, grafickej karte sa prikáže, aby okamžite začala vykreslovať druhý buffer. Kým karta zobrazuje druhý buffer, kreslí sa opäť do prvého a takto stále dookola. Táto technika je už veľmi stará no používa sa až dodnes. V minulosti jej uplatneniu prekážali hlavne grafické karty s nízkym množstvom pamäte (napríklad 640x480 16-bit zaberá 600KiB, na double buffering bolo teda treba až 1,2MiB grafickej pamäte). Dnes už našťastie tento problém neexistuje. Ďalšou prekážkou je pomerne pomalý prístup ku grafickej pamäti cez zbernicu (PCI, AGP alebo najnovšia PciExpress) oproti rýchlemu prístupu do systémovej RAM. Preto je vhodnejšie, v prípade ak sa vykonáva kreslenie na CPU, aby bola celá scéna pripravovaná v systémovej RAM a až po jej dokončení, bola celá naraz presunutá na grafickú kartu. Tento príncíp budeme využívať aj my, keďže väčšinu nášho kreslenia bude vykonávať CPU. Ku kresleniu priamo na grafickej karte sa vrátime v pokračovaní tohto seriálu pri AllegroGL (Allegro + OpenGL).
Ako kresliace plochy (nazývané aj canvas alebo plátno) fungujú v Allegre BITMAP
y. Jednu z nich, screen
, sme už využili. Môžeme si však vytvoriť aj vlastné. Následne môžeme do nich kresliť alebo ich kopírovať medzi sebou. Tu je zoznam niektorých funkcií na manipuláciu s nimi:
- BITMAP *create_bitmap(int width, int height); - Alokuje systémovú pamäť potrebnú pre uloženie obrázka so zadanými rozmermi.
- void destroy_bitmap(BITMAP *bitmap); - Uvoľní pamäť alokovanú volaním
create_bitmap
. - void clear_bitmap(BITMAP *bitmap); - Nastaví všetky pixely obrázka na hodnotu 0.
- void blit(BITMAP *source, BITMAP *dest, int source_x, int source_y, int dest_x, int dest_y, int width, int height); - Skopíruje oblasť z obrázka
source
do obrázkadest
. Ľavý horný roh kopírovanej oblasti je určený súradnicami [source_x,source_y] a jeho umiestnenie do cieľového obrázka je určené súradnicami [dest_x,dest_y].
Použijeme ich v nasledujúcom príklade (celý zdrojový kód).
BITMAP *actual=create_bitmap(screen->w,screen->h); if(actual==NULL) main_koniec(3,"Nepodarilo sa naalokovat pamat pre bitmap!"); clear_bitmap(actual); int x=screen->w/2; int y=screen->h/2; XY xy[DLZKA],a; float uhol=0; for(int i=0;i<DLZKA;i++){ xy[i].x=x; xy[i].y=y; } int pos=0; while(!close_button_pressed){ for(int i=0;i<DLZKA;i++){ a=xy[(pos+i)%DLZKA]; if(i+pos>=DLZKA){ circlefill(actual,a.x,a.y,10,makecol((i+1)*255/DLZKA,0,0)); } } a.x+=(int)(cos(uhol)*8); a.y+=(int)(sin(uhol)*8); uhol+=(float)(rand()%101-50)/100; if(a.x<0||a.y<0||a.x>=screen->w||a.y>=screen->h) uhol+=1; xy[(pos++)%DLZKA]=a; blit(actual,screen,0,0,0,0,actual->w,actual->h); rest(20); } destroy_bitmap(actual);
Príklad opäť vykresľuje hada Nehada, tentokrát však nie sú kruhy posielané priamo na obrazovku, ale kreslené do pamäťového bufferu, ktorý je následne celý poslaný na obrazovku.
Načítanie zo súboru, sprity
Po tom, ako sme sa naučili používať pamäťové bitmapy, sme len krôčik od načítavania obrázkov zo súboru. Allegro podporuje niekoľko grafických formátov. Dnes, už ide žiaľ, o tie menej rozšírené. Sú to BMP, PCX, TGA a LBM. Prvé tri z nich vie aj ukladať. Slúžia na to funkcie:
- BITMAP *load_bitmap(const char *filename, RGB *pal); - Zo súboru načíta jeden z podporovaných obrázkov.
- BITMAP *load_bmp(const char *filename, RGB *pal); - Zo súboru načíta obrázok vo formáte Windows alebo OS/2 BMP.
- BITMAP *load_pcx(const char *filename, RGB *pal); - Zo súboru načíta obrázok vo formáte PCX.
- BITMAP *load_tga(const char *filename, RGB *pal); - Zo súboru načíta obrázok vo formáte TGA.
- BITMAP *load_lbm(const char *filename, RGB *pal); - Zo súboru načíta obrázok vo formáte IFF ILBM/PBM.
- int save_bitmap(const char *filename, BITMAP *bmp, const RGB *pal); - Uloží obrázok do súboru.
Zoznam všetkých funkcií nájdete v manuálových stránkach. Dôležité je taktiež poznamenať, že v prípade, ak má uložený obrázok inú farebnú hĺbku, ako je aktuálne nastavená, obrázok je pri načítaní automaticky skonvertovaný.
S obrázkami (a celkovo s pamäťovými buffermi) sa dajú robiť zaujímavé veci. Okrem kopírovania oblastí (blit
) je možné obrázok vykresliť ako takzvaný sprite
. Predstavte si, že máte hada, ktorý by ale mal mať nejakú hlavu s očami a ústami. No kresliť oči a ústa pomocou primitív je pomerne náročné. Preto by ste chceli použiť hotový obrázok hlavy. Problém ale je, že hlava by mala byť okrúhla a blit
ovanie podporuje len celé obdĺžniky. Riešenie je jednoduché. Jedna farba v Allegre (a v mnohých iných knižniciach) je v istých prípadoch použitá ako priehľadná. V našom prípade to je farba fialová, červená a modrá zložka na maximum, zelená na nulu, teda (255,0,255). Stačí použiť túto farbu ako pozadie okolo hlavy a nevykresliť ju pomocou funkcie blit
, ale pomocou:
- #define MASK_COLOR_32 (8.8.8 pink) - Konštanta reprezentujúca farbu, ktorá sa považuje za priehľadnú pri 32-bitovom režime. Podobné konštanty existujú aj pre ostatné režimy.
- void masked_blit(BITMAP *source, BITMAP *dest, int source_x, int source_y, int dest_x, int dest_y, int width, int height); - Funguje rovnako, ako funkcia
blit
, akurát pri kreslení vynecháva priehľadnú farbu. - void draw_sprite(BITMAP *bmp, BITMAP *sprite, int x, int y); - Dáva rovnaký výsledok ako
masked_blit(sprite,bmp,0,0,0,0,sprite->w,sprite->h)
.
Použijeme ich v nasledujúcom príklade (celý zdrojový kód).
BITMAP *actual=create_bitmap(screen->w,screen->h); if(actual==NULL) main_koniec(3,"Nepodarilo sa naalokovat pamat pre bitmap!"); clear_to_color(actual,makecol(255,255,255)); BITMAP *penguin=load_bitmap("penguin.bmp",NULL); if(penguin==NULL) main_koniec(3,"Nepodarilo sa nacitat penguin.bmp!"); for(int y=0;y<actual->h;y+=penguin->h){ for(int x=0;x<actual->w;x+=penguin->w*2){ blit(penguin,actual,0,0,x+((y/penguin->h)%2)*penguin->w,y,penguin->w,penguin->h); } } destroy_bitmap(penguin); BITMAP *hlava=load_bitmap("hlava.bmp",NULL); if(hlava==NULL) main_koniec(3,"Nepodarilo sa nacitat hlava.bmp!"); int x=screen->w/2; int y=screen->h/2; XY xy[DLZKA],a; float uhol=0; for(int i=0;i<DLZKA;i++){ xy[i].x=x; xy[i].y=y; } int pos=0; a.x=-100; a.y=-100; while(!close_button_pressed){ circlefill(actual,a.x,a.y,16,0); for(int i=0;i<DLZKA;i++){ a=xy[(pos+i)%DLZKA]; if(i+pos>=DLZKA){ circlefill(actual,a.x,a.y,10,makecol((i+1)*255/DLZKA,0,0)); } } a.x+=(int)(cos(uhol)*8); a.y+=(int)(sin(uhol)*8); uhol+=(float)(rand()%101-50)/100; if(a.x<0||a.y<0||a.x>=screen->w||a.y>=screen->h) uhol+=1; xy[(pos++)%DLZKA]=a; draw_sprite(actual,hlava,a.x-hlava->w/2,a.y-hlava->h/2); blit(actual,screen,0,0,0,0,actual->w,actual->h); rest(20); } destroy_bitmap(actual); destroy_bitmap(hlava);
Na začiatku načítame obrázok milého tučniačika a šachovnicovo ním vytapetujeme pozadie. Následne samotného tučniačika zničíme, keďže ho už viac nepotrebujeme. Potom načítame hlavu hada Nehada. Samotného hada vykresľujeme rovnako, ako v predchádzajúcom príklade, akurát tesne pred skopírovaním actual
na obrazovku, dokreslíme aj hlavu. Ale čo sa to deje? Had nám požiera pekné pozadie s tučniačikmi a zanecháva za sebou skazu. A ani hlava sa správne nenatáča. Takto to určite nemôže ostať!
Priehľadnosť, rotácia
Allegro podporuje rotáciu pri vykresľovaní. Ako ste si určite všimli, na pohyb nášho hada používame uhol v radiánoch, ktorý náhodne meníme. Na slovenských školách sa pri výpočtoch bežne používajú stupne. Allegro nevyužíva ani jeden z týchto systémov určovania veľkosti uhlov a definuje vlastný. Ba čo viac, definuje aj vlastný číselný typ. Ide o typ typedef long fixed (viac tu). Uhly sa teda v Allegre zadávajú ako fixed
čísla od 0 po 256. V našom prípade teda musíme spraviť konverziu z radiánov.
Nasleduje popis niektorých funkcií:
- fixed itofix(int x); - Konvertuje
integer
na vstupe nafixed
číslo. - fixed ftofix(double x); - Konvertuje
double
na vstupe nafixed
číslo. - fixed fixmul(fixed x, fixed y); - Vynásobí dve
fixed
čísla. - extern const fixed radtofix_r; - Konštanta, ktorou je potrebné vynásobiť uhol zadaný v radiánoch, aby sme získali uhol vhodný pre Allegro.
- void rotate_sprite(BITMAP *bmp, BITMAP *sprite, int x, int y, fixed angle); - Funguje podobne ako
draw_sprite
, ale pred vykreslením je obrázok pootočený o zadaný uhol. - void pivot_sprite(BITMAP *bmp, BITMAP *sprite, int x, int y, int cx, int cy, fixed angle); - Funguje podobne ako
rotate_sprite
, ale obrázok je pootočený okolo zadaných súradníc vzhľadom na sprite.
Otáčaciu hlavu máme teda vyriešenú, ale stále sme nevyriešili problém s hadom požierajúcim pozadie. Doteraz sme hada kreslili tak, že sme vykreslili niekoľko kruhov, ktoré sa postupne zosvetľovali. Aby had chodil po pozadí, potrebujeme miesto obyčajného zosvetľovania (rastu červenej zložky) spraviť kruhy bližšie ku koncu hada stále viac priesvitné. Priesvitnosť (a niektoré ďalšie efekty) sú v Allegre riešené veľmi elegantne. V našom prípade potrebujeme priehľadne vykresľovať primitívy. To dosiahneme pomocou nasledujúcich funkcií:
- void drawing_mode(int mode, BITMAP *pattern, int x_anchor, int y_anchor); - Nastaví spôsob vykresľovania primitív.
- void set_trans_blender(int r, int g, int b, int a); - Nastaví interpolačný režim pre vykresľovanie priehľadných pixelov.
Všetko si predvedieme v nasledujúcom príklade (celý zdrojový kód).
BITMAP *actual=create_bitmap(screen->w,screen->h); if(actual==NULL) main_koniec(3,"Nepodarilo sa naalokovat pamat pre bitmap!"); BITMAP *back=create_bitmap(screen->w,screen->h); if(back==NULL) main_koniec(3,"Nepodarilo sa naalokovat pamat pre bitmap!"); clear_to_color(back,makecol(255,255,255)); BITMAP *penguin=load_bitmap("penguin.bmp",NULL); if(penguin==NULL) main_koniec(3,"Nepodarilo sa nacitat penguin.bmp!"); for(int y=0;y<back->h;y+=penguin->h){ for(int x=0;x<back->w;x+=penguin->w*2){ int r=rand()&3; if(r==0) draw_sprite(back,penguin,x+((y/penguin->h)%2)*penguin->w,y); if(r==1) draw_sprite_v_flip(back,penguin,x+((y/penguin->h)%2)*penguin->w,y); if(r==2) draw_sprite_h_flip(back,penguin,x+((y/penguin->h)%2)*penguin->w,y); if(r==3) draw_sprite_vh_flip(back,penguin,x+((y/penguin->h)%2)*penguin->w,y); } } destroy_bitmap(penguin); BITMAP *hlava=load_bitmap("hlava.bmp",NULL); if(hlava==NULL) main_koniec(3,"Nepodarilo sa nacitat hlava.bmp!"); int x=screen->w/2; int y=screen->h/2; XY xy[DLZKA],a; float uhol=0; for(int i=0;i<DLZKA;i++){ xy[i].x=x; xy[i].y=y; } int pos=0; a.x=-100; a.y=-100; drawing_mode(DRAW_MODE_TRANS,actual,0,0); while(!close_button_pressed){ blit(back,actual,0,0,0,0,back->w,back->h); for(int i=0;i<DLZKA;i++){ a=xy[(pos+i)%DLZKA]; if(i+pos>=DLZKA){ set_trans_blender(255,0,0,(i+1)*255/DLZKA); circlefill(actual,a.x,a.y,10,makecol(255,0,0)); } } a.x+=(int)(cos(uhol)*8); a.y+=(int)(sin(uhol)*8); pivot_sprite(actual,hlava,a.x,a.y,hlava->w/2,hlava->h/2,fmul(ftofix(uhol),radtofix_r)); uhol+=(float)(rand()%101-50)/100; if(a.x<0||a.y<0||a.x>=screen->w||a.y>=screen->h) uhol+=1; xy[(pos++)%DLZKA]=a; blit(actual,screen,0,0,0,0,actual->w,actual->h); rest(20); } destroy_bitmap(actual); destroy_bitmap(hlava);
Had Nehad už nemaže pozadie, ale ako na potvoru, vzniká opäť škaredý efekt na jeho chvoste. Kým doteraz sme kruhy celkom prekresľovali, pri priesvitnosti vidno všetky kruhy pod sebou. Aj toto sa dá pomerne jednoducho vyriešiť, ale to ponechám ako cvičenie pre čitateľov.
Určite ste si všimli, že v príklade sú použité niektoré funkcie, ktoré neboli spomenuté v texte. Z názvov je ale pomerne jednoduché zistiť, čo je ich úlohou.
Záver
Allegro poskytuje obrovské možnosti, čo sa týka vykresľovania a manipulácie s grafikou. V tomto článku je spomenutá len veľmi malá časť z nich. Preto povzbudzujem čitateľov k tomu, aby si otvorili manuál a prezreli si zoznam funkcií (odkazy na jednotlivé stránky sú na konci článku). Aj keď mnoho z nich možno nikdy nevyužijete, je dobré o ich existencii vedieť.
V následujúcej časti sa budeme venovať práci s klávesnicou a myšou. Po nej bude nasledovať časť venovaná práci so súbormi a kompresii. Zároveň v nej začneme konečne pracovať na hre Packoban, keďže už budeme mať na to dostatok vedomostí.
Pre pridávanie komentárov sa musíte prihlásiť.