Make a game in C/C++ 2.part (under construction)
Updated: 03.06.2008
Předmluva
Před čtením tohoto článku je nutné znát základy programování v jazyce C a okrajově v C++. Jinak nejdříve navštivte některou z následujících stránek:
Případně použijte některý z dostupných vyhledávačů (www.seznam.cz, www.google.com, www.altavista.com nebo www.yahoo.com).
Doporučuji si též pořídit knihu Učebnice jazyka C (280 stran, 179Kč). Jsou v ní názorně vysvětleny všechny příkazy programovacího jazyka C a jejich použití. Navíc si v odkazu na knihu můžete stáhnout zdrojové soubory příkladů. Autorem je Ing. Pavel Herout.
Tvorba hry
V předchozím článku Tvorba her 1.část jsme se zabývali pouze instalací a konfigurací jednotlivých programů. Nyní si při tvorbě jednoduché hry ukážeme práci s těmito programy.
Postup je rozdělen na několik kroků. Na konci každého kroku je k dispozici odladěný zdrojový kód ke stažení nebo shrnutí výše řečeného pro úplné pochopení každého kroku.
1. Definování hry
Ze všeho nejdříve si musíme nadefinovat parametry naší hry. Tomu se pak podřídí grafika a vlastní kód hry.
Není na škodu si během definování hry psát poznámky na papír. Uteče Vám tak méně informací, které zpravidla během designování hry zapomenete pro jiné stejně důležité věci.
Shrnutí parametrů námi vytvářené hry:
Pracovní název hry: stroje (podle něj se pojmenuje pracovní adresář)
Skutečný název hry: Nadvláda strojů
Příběh: Stroje kdesi na ostrově spustí energetický štít kolem Země. Tím ožijí stroje na celém světě a zabíjejí lidi. Chlapec se vydá na cestu plnou překážek (z města, přes moře, na ostrov, skrz vodní kanál k základně), aby vypnul generátor energetického štítu kolem Země. Stroje pak znehybní a lidstvo je zachráněno.
-
Úvodní obrazovka: ano
Start hry
Instrukce
Opustit hru
Intro: ano (Stroje kdesi na ostrově spustí energetický štít kolem Země. Tím ožijí stroje na celém světě.)
Outro: ano (Chlapec vypne generátor energetického štítu kolem Země. Stroje znehybní a lidstvo je zachráněno.)
Rozlišení: 640 x 480 pixelů (full screen)
Hloubka barev: 16bit (65535 barev)
Typ: plošinovka (všesměrová)
Grafika: cartoon
-
Počet najednou se vykreslujících rovin: 3
mapa hry
1. pozadí
2. pozadí (nepohyblivé)
-
Počet úrovní: 1
1.úroveň: město
-
Počet hlavních postav: 1
Počet životů: 3 (sníží se po každém kontaktu s překážkou nebo tvorem)
Počet snímků pro animaci: v každém směru 3, celkem tedy 4*3=12 snímků
-
Počet tvorů: 2
-
Motor
Počet snímků pro animaci: 5
-
Zbíječka
Počet snímků pro animaci: 3
Počet snímků pro animaci: 3
-
Počet statických překážek: 3
-
Počet animovaných překážek: 1
Počet snímků pro animaci: 3
atd.
Tzn. že představa je následující:
2. Kreslení grafiky
K vytvoření potřebných obrázků do naší hry můžeme použít některý z následujících programů:
Kreslení.exe - tento program je součástí instalace Windows
Adobe Illustrator (30 denní zkušební verze)
Paint Shop Pro (30 denní zkušební verze)
FLASH MX Professional 2004 (30 denní zkušební verze)
Výsledkem bude jeden veliký obrázek ve formátu bmp (samozřejmě lze i jiný formát např.: jpg) obsahující všechny objekty:
Je to z důvodu lepší práce s grafikou ve hře. Celý obrázek, tvořen jednou bitmapou, pak nahrajeme do videopaměti. V procesu vykreslování jednotlivých pozadí a objektů na obrazovku je to pak rychlejší.
3. Vytvoření projektu
Nejdříve vytvoříme pracovní adresář C:\tvorba\c\stroje. Jelikož je zapotřebý výsledný zdrojový kód přeložit do spustitelného souboru s příponou exe, budeme pracovat v projektu, ke kterému přiřadíme kompilátor. Postup je následující:
a) Spustíme textový editor PSPad.
b) V menu
zvolíme položku . Tím založíme nový projekt.-
c) Rovněž v menu Výchozí adresář: cestu k našemu pracovnímu adresáři. Viz následující obrázek:
zvolíme volbu a v záložce zadáme do položky -
d) Pak klikneme na záložku
kde vyplníme následující položky:Kompilátor: C:\MinGW\bin\g++.exe
Parametry: main.c -o stroje.exe -lalleg (tuto položku budeme postupně doplňovat o další naše knihovny)
Výchozí adresář: C:\Tvorba\C\stroje
Jak je též patrné z obrázku níže:
Pak nastavení potvrdíme kliknutím na tlačítko
(nebo klávesou ). -
e) Založíme hlavní soubor. V menu C/C++. Potvrdíme kliknutím na tlačítko . V editoru se otevře prázdný soubor Novy1.c.
zvolíme položku a v následujícím dialogovém okně vybereme jako typ souboruNyní jej ještě musíme přidat do našeho projektu. V menu stroje a dále zadáme název projektu stroje do položky a potvrdíme tlačítkem . Viz též následující obrázek:
zvolíme volbu . Tím se soubor přiřadí k našemu projektu. Zkontrolovat si to můžeme v okně , který vyvoláme/skryjeme klávesovou zkratkou . Dále je ještě třeba celý náš projekt pojmenovat a uložit do našeho pracovního adresáře. Použijeme klávesovou zkratkou . Zobrazí se dialogové okno , kde nastavíme pro položku náš pracovní adresářPak ještě musíme uložit hlavní soubor. Stiskneme klávesovou zkratku main a potvrdíme tlačítkem .
. Opět se vyvolá dialogové okno , kde zadáme do položky jméno hlavního souboruPak je ještě nutné vložit do projektu hlavní hlavičkový soubor main.h (stejným způsobem jako main.c).
Postup pro přidání dalších souborů v budoucnu je stejný jako v tomto bodě.
Tím jsme založili náš projekt a vytvořili jsme hlavní soubory main.c a main.h v našem projektu, kam budeme psát kód hry. V budoucnu přibude ještě několik souborů. Ale teď nebudu předbíhat.
4. Programování
Zde se již dostáváme k samotnému programování.
Lekce 1: Základní použití Allegra
Nejdříve si ukážeme, jak se používá knihovna Allegro. Zdrojový kód je velmi jednoduchý a lze z něho lehce pochopit základní použití knihovny. Jednotlivé klíčové bloky jsou okomentovány.
Na závěr této lekce si ukážeme, jak zkompilovat náš zdrojový kód do spustitelného exe souboru. Po spuštění program přepne zobrazování do grafického módu a vypíše bílý text doprostřed černé obrazovky. Ukončí programu se provede klávesou :
// Lekce 1: Zakladni pouziti Allegra
/* Tento kod osvetluje zakladni pouziti Allegra. Po spusteni se zobrazi cerna obrazovka s bilym textem uprostred. Program se ukonci stiskem klavesy F10. */
// Vlozeni knihoven
#include <stdio.h> #include "allegro.h"
// Definice globalnich konstant
// Rozliseni hry a hloubka barev
#define SCREEN_WIDTH 640 #define SCREEN_HEIGHT 480 #define SCREEN_BPP 16
// Potrebujeme inicializovat allegro a nahrat nase data
int InitAll() { if ( allegro_init() != 0 ) { fprintf(stderr, "Allegro se nepodarilo inicializovat.\n"); exit(1); } install_keyboard(); /* Nastavime hloubku barev a graficky rezim. V pripade neuspechu se vypise na obrazovku varovna hlaska a program se ukonci. */ set_color_depth( SCREEN_BPP ); if ( set_gfx_mode( GFX_AUTODETECT_WINDOWED, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0 ) != 0 ) { set_gfx_mode( GFX_TEXT, 0, 0, 0, 0 ); allegro_message( "Pozadovany graficky mod neni dostupny!\n %s\n" , allegro_error ); return(-1); } clear_keybuf (); return 0; }
// Pred ukoncenim hry musime uvolnit nase data z pameti
void DeleteAll() { allegro_exit(); }
// main funkce
int main() { if ( InitAll() < 0 ) return -1; clear_keybuf ();
// Vypis bily text na cernem pozadi doprostred obrazovky
textout_centre_ex( screen, font, "Pro ukonceni programu stiskni F10!", SCREEN_W/2, SCREEN_H/2, makecol( 255 , 255 , 255 ), makecol( 0, 0, 0 ) );
// Stiskni F10 pro ukonceni hry
do { } while (!key[KEY_F10]); DeleteAll(); return 0; } END_OF_MAIN();
Po zkopírování výše vypsaného kódu do našeho main.c spustíme kompilátor klávesovou zkratkou . Proběhne kompilace našeho zdrojového kódu a její výsledek se zobrazí v okně ve spodní části PSPad editoru. V případě, že kompilace proběhla bezchybně, zobrazí se následující text:
V opačném případě, tj. že překlad neproběhl správně, se v KEY_F10:
okně vypíše příslušná chyba včetně čísla řádku a funkce, kde se chyba nalézá. Např. při překlepu při zadávání předdefinované hodnotySpustitelný soubor stroje.exe se nachází po zkompilování ve stejného adresáři jako náš zdrojový kód.
> zakladni_pouziti_allegra.zip <
Během programování lze použít též manuál k Allegru, který je distribuován spolu s knihovnou. Je k dispozici hned v několika formátech. Doporučuji však používat html verzi (je v adresáři C:\allegro\docs\html\). Díky barevnému provedení je přehlednější. Spouští se souborem allegro.html.
Lekce 2: Časovač, double buffering
Použít Allegro už tedy umíme. Nyní se zaměříme na plynulou animaci a potažmo i stejně rychlý běh naší hry na všech počítačích. K tomu slouží jednak časovač v našem PC, který bude spouštět náš algoritmus na výpočet nové polohy objektů.
Dále pro vykreslování grafiky použijeme techniku zvanou double buffering. První bitmapa se vykresluje na obrazovku a do druhé kreslíme naše objekty. Když jsme hotovi, vykreslíme druhou na obrazovku a kreslime objekty do prvni. Obraz je tak stálý a nepříjemně nebliká.
Doplněný kód je zvýrazněn červenou barvou:
// Lekce 2: Casovac, double buffering
/* Pouzijeme casovac pro dosazeni stejneho behu hry na vsech PC. Pro vykresleni objektu na obrazovce pouzijeme techniku 'double buffering'. */
// Vlozeni knihoven
#include <stdio.h> #include "allegro.h"
// Definice globalnich konstant
// rozliseni hry a hloubka barev #define SCREEN_WIDTH 640 #define SCREEN_HEIGHT 480 #define SCREEN_BPP 16
// Rychlost hry
#define GAME_SPEED 50
// Deklarace globalnich promennych
// deklarace 3 ukazatelu na BITMAPu, 1 promenna pro casovac BITMAP *back = NULL; BITMAP *page_1, *page_2; volatile int game_time;
// Citac - po kazdem vyvolani teto funkce se promenna 'game_time' zvysi o 1
void game_timer() { game_time++; } END_OF_FUNCTION(game_timer);
// propocitej nove pozice objektu ve hre
void MoveAll() { }
// Vykresli vse na obrazovku
/* Vse kreslime (pro jednoduchost je to opet jen bily text) do BITMAPy, na kterou je aktualne nastaven ukazatel 'back'. Pak se vysledek zobrazi na obrazovce a ukazatel se nastavi na druhou BITMAPu, ktera byla az do ted zobrazovana. Proces se stale opakuje. */ void DrawAll() { // Vypis bily text na cernem pozadi doprostred stranky, na kterou ukazuje 'back' textout_centre_ex( back, font, "Pro ukonceni programu stiskni F10!", SCREEN_W/2, SCREEN_H/2, makecol( 255 , 255 , 255 ), makecol( 0, 0, 0 ) ); // zobraz stranku na obrazovce show_video_bitmap(back); // nastav ukazatel na druhou stranku if (back == page_1) back = page_2; else back = page_1; }
// Potrebujeme inicializovat allegro a nahrat nase data
int InitAll() { if ( allegro_init() != 0 ) { fprintf(stderr, "Allegro se nepodarilo inicializovat.\n"); exit(1); }
// Nainstalujeme casovac
install_timer();
install_keyboard(); /* Nastavime hloubku barev a graficky rezim. V pripade neuspechu se vypise na obrazovku varovna hlaska a program se ukonci. */ set_color_depth( SCREEN_BPP ); if ( set_gfx_mode( GFX_AUTODETECT_WINDOWED, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0 ) != 0 ) { set_gfx_mode( GFX_TEXT, 0, 0, 0, 0 ); allegro_message( "Pozadovany graficky mod neni dostupny!\n %s\n" , allegro_error ); return(-1); }
// Uzamkneme citac a jeho promennou
LOCK_FUNCTION(game_timer); LOCK_VARIABLE(game_time);
// Nastavime citac na preruseni od casovace v nasem PC
install_int (game_timer, GAME_SPEED);
// Vytvorime 2 stranky ve video pameti - obe stejne velke jako rozliseni hry
// Vytvor 1. stranku (pri neuspechu program skonci) page_1 = create_video_bitmap(SCREEN_WIDTH, SCREEN_HEIGHT); if (page_1 == NULL) { allegro_message( "Nedostatek video pameti pro prvni stranku!\n"); destroy_bitmap(page_1); exit(1); } // Vytvor 2. stranku (pri neuspechu zrusime 1.stranku a program skonci) page_2 = create_video_bitmap(SCREEN_WIDTH, SCREEN_HEIGHT); if (page_2 == NULL) { allegro_message( "Nedostatek video pameti pro druhou stranku!\n"); exit(1); }
// Promenne nastavime na pocatecni hodnotu
back = page_1; game_time = 0;
clear_keybuf (); return 0; }
// Pred ukoncenim hry musime uvolnit nase data z pameti
void DeleteAll() {
destroy_bitmap(page_1); destroy_bitmap(page_2);
allegro_exit(); }
// main funkce
int main() { if ( InitAll() < 0 ) return -1;
// Hlavni smycka hry
/* V pravidelnych intervalech, ktere jsou dany periodou (rychlosti) generovani preruseni od casovace v PC, je volana funkce 'game_timer()' (nas citac), ktera pokazde zvysi promenou 'game_time' o jednicku. To se deje nezavisle na behu nasi hry. Pokud je tato promenna vetsi nez 0, objekty se posunou. V opacnem pripade se jen vykresli na obrazovku. Obecne plati, ze cim je 'game_time' vetsi, tim vice se objekty posunuji. Jinymi slovy: na pomalych PC se objekty posunuji stejne rychle jako na rychlych PC, ale na obrazovce se to projevy trhavymi pohyby. Pro ukonceni behu programu opet stiskni F10. */ do { while (game_time > 0) { MoveAll(); game_time--; } DrawAll(); } while (!key[KEY_F10]);
DeleteAll(); return 0; } END_OF_MAIN();
> casovac_doublebuffering.zip <
Lekce 3: Potřebujeme hlavní postavu
Dále do naší hry přidáme algoritmus na ovládání a vykreslování hlavní postavy. Bohužel neexistuje žádný univerzální pro všechny typy her. Rovněž to platí o algoritmu herní mapy a pozadí. Ty jsou zpravidla závislé na poloze hlavní postavy (to platí pro všesměrové plošinovky) a nebo se pohybují nezávisle na hlavní postavě (např. střílečky typu 1945, které se pohybují konstantní rychlostí jen v jednom směru). Pro ty druhé je algoritmus o něco jednodušší - ať hráč hraje nebo ne, herní mapa a pozadí se stále pohybují.
ASI SMAZAT: Současný kód už umožňuje přidat algoritmy na řízení postavy. Jednotlivé obrázky hlavní postavy, tzv. frames, už máme připraveny.
// Lekce 3: Potřebujeme hlavní postavu
> rozpohybovani_hlavni_postavy.zip <
Lekce 4: Vytvoříme herní mapu
Dále je třeba si vytvořit program k editaci jednotlivých úrovní naší hry. To lze řešit buď jako samostatný program nebo jako součást hry. Druhé řešení má výhodu v okamžité editaci a následném otestování. Avšak nese to i jednu podstatnou nevýhodu. Pokud se rozhodnete editor nedistribuovat společně s hrou, musíte jej z kódu vymazat. V případě velkého a většinou i dost nepřehledného (to je závislé na Vašich zkušenostech) programu pak hrozí smazání nechtěných částí programu. Což může v lepším případě vést k nefunkčnosti hry, v horším se to nemusí povést ani odladit. Řešením je zapodmínkovat algoritmy související s editorem a provést podmíněný překlad. Ten se sám postará o odstranění nepotřebných algoritmů z výsledného kódu.
V našem příkladu proto zvolim druhou možnost - podmíněný překlad dělá.
bude součástí hry. A názorně si ukážeme jak se takový