7. diel - Aréna s bojovníkmi v C ++
V minulej lekcii, Bojovník do arény - Zapuzdrenie , sme si vytvorili triedu bojovníka. Hracie kocku máme hotovú z prvých lekcií objektovo orientovaného programovania. Dnes dáme všetko dokopy a vytvoríme funkčné arénu. C ++ tutoriál bude skôr oddychový a pomôže nám zopakovať si prácu s objektmi a algoritmické myslenie.
Potrebujeme napísať nejaký kód pre obsluhu bojovníkov a výpis správ užívateľmi. Najskôr si vyriešime, kde vlastne budú bojovníci. V našom prípade by bolo vhodné dať ich do triedy Hrac. Pre začiatok predpokladajme, že každý hráč má iba jedného bojovníka.
Hrac.h
#include <string> #include "Kostka.h" #include "Bojovnik.h" using namespace std; class Hrac { private: Bojovnik bojovnik; string jmeno; public: Hrac(string jmeno, Kostka &kostka); string getJmeno(); Bojovnik& getBojovnik(); };
Hrac.cpp
#include "Hrac.h" Hrac::Hrac(string jmeno, Kostka &kostka): bojovnik(100,8,5,kostka) { this->jmeno = jmeno; } string Hrac::getJmeno() { return this->jmeno; } Bojovnik Hrac::getBojovnik() { return this->bojovnik; }
Teraz sa pozrieme na arénu. Určite budeme chcieť, aby simulovala súboj
bojovníkov. Pre zjednodušenie budeme pracovať s tým, že útočník
zaútočí na náhodného iného bojovníka. Na začiatku sme si tiež povedali,
že sa bude jednať o ťahovú hru - potrebujeme si pamätať počet vykonaných
ťahov. Ďalej budeme chcieť vypísať informácie o bojovníkoch a útoku.
Všetky tieto metódy bude potrebovať trieda Arena. Najskôr si v
triede Arena vytvoríme atribút typu int
, pomenujeme ho
tah
a v konstruktoru mu nastavíme hodnotu 1. Potom si napíšeme
metódou pre výpis informácií o aréne - vypis()
.
void Arena::vypis() { cout << "-------------- Arena --------------" << endl; cout << "Tah: " << this->tah << endl; cout << "Zdravi bojovniku:" << endl; for (int i = 0; i < this->pocet_hracu; i++) { cout << "\t" << this->hraci[i]->getJmeno() << ": "; // vypíšeme jméno bojovníka if (!this->hraci[i]->getBojovnik().nazivu()) // zjistíme, že bojovník ještě není mrtev { cout << "mrtev" << endl; // pokud je bojovník mrtev tak o tom informujeme continue; // a pokračujeme na dalšího bojovníka } cout << "["; // začátek baru se životy // vypočítáme kolik procent života bojovník má float pocet_zivotu_procent = this->hraci[i]->getBojovnik().getZivot() / this->hraci[i]->getBojovnik().getMaxZivot(); for (double z = 0; z < 1.0; z += 0.1) cout << (z < pocet_zivotu_procent ? '#' : ' '); // vypisujeme procenta života // ukončíme bar se životy a vypíšeme info o útoku a obraně cout << "] (utok: " << this->hraci[i]->getBojovnik().getUtok() << ", obrana: " << this->hraci[i]->getBojovnik().getObrana() << ")" << endl; } }
Životy budeme zobrazovať iba graficky a to percentuálne. Preto si najskôr
vypočítame, koľko percent života bojovník má (premenná
pocet_zivotu_procent
) a potom iterujeme po 10-tich percentách a
vypisujeme #
(ak bojovník životy má) alebo medzeru (ak životy
stratil) - to zabezpečuje ternárne operátor vo výpise.
Konzolová aplikácia
-------------- Arena --------------
Tah: 1
Zdravi bojovniku:
Karel: [##########] (utok: 8, obrana: 5)
Pavel: [##########] (utok: 8, obrana: 5)
Honza: [##########] (utok: 8, obrana: 5)
Teraz sa presunieme na metódu zapas()
- hlavná slučku našej
hry. Metóda zapas()
nebude mať žiadne parametre a nebude ani
nič vracať. Vnútri bude cyklus, ktorý bude na striedačku volať útoky
bojovníkov a vypisovať informačnú obrazovku a správy. Najskôr si urobíme
pomocné metódy (budú privátne), ktoré neskôr použijeme:
Arena.cpp
bool Arena::existujeVitez() { return this->pocetZivych() == 1; } int Arena::pocetZivych() { int zivych = 0; // pro každého živého hráče for (int i = 0; i < this->pocet_hracu; i++) if (this->hraci[i]->getBojovnik().nazivu()) zivych++; // zvyš počet živých o jeden return zivych; }
Metóda nám zistí, koľko bojovníkov prežilo. Pokiaľ bude živý iba
jeden z nich, potom je víťaz a hra môže skončiť. A teraz ku sľubované
metóde zapas()
.
Arena.cpp
void Arena::zapas() { // dokud nezůstane pouze jeden hráč while (!this->existujeVitez()) { this->vypis(); // vypíšeme informace o hráčích // zkontroluj všechny hráče for (int i = 0; i < this->pocet_hracu; i++) { // pokud není bojovník naživu, potom ho přeskoč if (!this->hraci[i]->getBojovnik().nazivu()) continue; // mohlo se stát, že v předchozím kole někoho zabili a tak zůstal poslední bojovník // pokud se to stalo, potom hra končí if (this->existujeVitez()) break; // spočtení index nejbližšího živého hráče, na kterého budeme útočit int utok_na = (i + 1) % this->pocet_hracu; while (!this->hraci[utok_na]->getBojovnik().nazivu()) utok_na = (utok_na + 1) % this->pocet_hracu; // útok float zraneni = this->hraci[i]->getBojovnik().utoc(this->hraci[utok_na]->getBojovnik()); // vypsání výsledku souboje cout << this->hraci[i]->getJmeno() << " utoci na " << this->hraci[utok_na]->getJmeno() << " za " << zraneni << " zraneni" << endl; } // přesuneme se do dalšího tahu this->tah++; } }
Kroky som sa snažil popísať v komentároch, preto ich nemá zmysel popisovať znovu v texte. Najproblematickejšie je zrejme spočítané indexu hráča, na ktorého budeme útočiť. Začneme na bojovníkovi na vyššom indexe než je aktuálna bojovník (aby hráč neútočil sám na seba). Potom zisťujeme, či je bojovník živý. Ak nie je, potom sa posunieme na ďalšieho bojovníka v poradí. Čo ale robiť, keď dôjdeme na koniec poľa? Chceli by sme sa vrátiť späť na začiatok (index 0) - to nám zaistí modulo. Ak sa dostaneme na hodnotu 3 (a 3 je počet hráčov), potom modulo automaticky index zníži na 0.
Teraz už to stačí len celé spustiť:
Main.cpp
#include <iostream> #include "Kostka.h" #include "Arena.h" #include "Bojovnik.h" using namespace std; int main() { Kostka kostka; Arena arena(3, kostka); arena.zapas(); arena.vypis(); cin.get(); cin.get(); return 0; }
Ak ste program skúsili spustiť, bude bežať v nekonečnom cykle, ale nič
sa nebude meniť. Ako už bolo povedané, pri volaní metódy sa parametrami a
návratová hodnota prekopírujú. To je veľmi dôležité. V
metóde hráčov getBojovnik()
vraciame typ Bojovnik. To
ale znamená, že volajúci program nedostane nášho skutočného bojovníka,
ale iba jeho kópiu. Opravíme to tak, že zmeníme návratovú hodnotu na
referenciu alebo ukazovateľ - v našom prípade poslúži lepšie
referencie.
Hrac.h
class Hrac { private: Bojovnik bojovnik; string jmeno; public: Hrac(string jmeno, Kostka &kostka); string getJmeno(); Bojovnik& getBojovnik(); };
Hrac.cpp
Bojovnik& Hrac::getBojovnik() { return this->bojovnik; }
Teraz by mal program fungovať podľa našich predstáv.
Konzolová aplikácia
Zadejte jmeno hrace: Karel
Zadejte jmeno hrace: Pavel
Zadejte jmeno hrace: Honza
-------------- Arena --------------
Tah: 1
Zdravi bojovniku:
Karel: [###########] (utok: 8, obrana: 5)
Pavel: [###########] (utok: 8, obrana: 5)
Honza: [###########] (utok: 8, obrana: 5)
Karel utoci na Pavel
Pavel utoci na Honza
Honza utoci na Karel
-------------- Arena --------------
Tah: 2
Zdravi bojovniku:
Karel: [########## ] (utok: 8, obrana: 5)
Pavel: [########## ] (utok: 8, obrana: 5)
Honza: [########## ] (utok: 8, obrana: 5)
Karel utoci na Pavel
Pavel utoci na Honza
Honza utoci na Karel
Možno by sme chceli pri útoku zobraziť, koľko životov bola protihráči
ubrané. To vykonáme jednoducho, z metódy Bojovnik.utoc()
si
môžeme vrátiť zranenia, ktoré bolo spôsobené. Ďalej by sme na konci
programu chceli zobraziť informácie o víťazovi. Pre vykonáme opäť
jednoducho, pridáme si do triedy Arena
ďalšiu metódu, ktorá
vypíše informácie o víťazovi. Ako je vidieť, OOP prístup je veľmi
praktický a ľahko sa rozširuje.
#include <iostream> #include "Kostka.h" #include "Arena.h" #include "Bojovnik.h" using namespace std; int main() { Kostka kostka; Arena arena(6, kostka); arena.zapas(); arena.vypis(); arena.vypisViteze(); return 0; }
#ifndef __KOSTKA_H__ #define __KOSTKA_H__ using namespace std; class Kostka { private: int pocet_sten; public: Kostka(); Kostka(int pocet_sten); int hod(); int getPocetSten(); }; #endif
#include <iostream> #include <cstdlib> #include <ctime> #include "Kostka.h" using namespace std; Kostka::Kostka() : Kostka(6) { } Kostka::Kostka(int pocet_sten) { this->pocet_sten = pocet_sten; srand((unsigned int)time(NULL)); } int Kostka::hod() { return rand() % this->pocet_sten + 1; } int Kostka::getPocetSten() { return this->pocet_sten; }
#ifndef __BOJOVNIK_H_ #define __BOJOVNIK_H_ #include <string> #include "Kostka.h" using namespace std; class Bojovnik { private: float zivot; float max_zivot; float utok; float obrana; Kostka &kostka; public: Bojovnik(float zivot, float utok, float obrana, Kostka &kostka); bool nazivu(); float utoc(Bojovnik &druhy); float getZivot(); float getMaxZivot(); float getUtok(); float getObrana(); }; #endif
#include "Bojovnik.h" Bojovnik::Bojovnik(float zivot, float utok, float obrana, Kostka &kostka) : kostka(kostka), zivot(zivot), max_zivot(zivot), utok(utok), obrana(obrana) {} bool Bojovnik::nazivu() { return this->zivot > 0; } float Bojovnik::utoc(Bojovnik & druhy) { float obrana_druhy = druhy.obrana + druhy.kostka.hod(); float utok_prvni = this->utok + this->kostka.hod(); float zraneni = utok_prvni - obrana_druhy; if (zraneni < 0) zraneni = 0; druhy.zivot -= zraneni; return zraneni; } float Bojovnik::getZivot() { return this->zivot; } float Bojovnik::getMaxZivot() { return this->max_zivot; } float Bojovnik::getUtok() { return this->utok; } float Bojovnik::getObrana() { return this->obrana; }
#ifndef __HRAC__H_ #define __HRAC__H_ #include <string> #include "Kostka.h" #include "Bojovnik.h" using namespace std; class Hrac { private: Bojovnik bojovnik; string jmeno; public: Hrac(string jmeno, Kostka &kostka); string getJmeno(); Bojovnik& getBojovnik(); }; #endif
#include "Hrac.h" Hrac::Hrac(string jmeno, Kostka &kostka): bojovnik(100,8,5,kostka) { this->jmeno = jmeno; } string Hrac::getJmeno() { return this->jmeno; } Bojovnik& Hrac::getBojovnik() { return this->bojovnik; }
#ifndef __ARENA_H_ #define __ARENA_H_ #include "Hrac.h" #include "Kostka.h" class Arena { private: Hrac** hraci; int pocet_hracu; int tah; bool existujeVitez(); int pocetZivych(); public: Arena(int pocet_hracu, Kostka &kostka); ~Arena(); void vypis(); void vypisViteze(); void zapas(); }; #endif
#include <iostream> #include <cstdlib> #include "Arena.h" using namespace std; bool Arena::existujeVitez() { return this->pocetZivych() == 1; } int Arena::pocetZivych() { int zivych = 0; // pro každého živého hráče for (int i = 0; i < this->pocet_hracu; i++) if (this->hraci[i]->getBojovnik().nazivu()) zivych++; // zvyš počet živých o jeden return zivych; } Arena::Arena(int pocet_hracu, Kostka &kostka) : tah(1) { this->pocet_hracu = pocet_hracu; this->hraci = new Hrac*[pocet_hracu]; string jmena[] = {"Karel", "Pavel", "Honza", "Petr", "Josef", "Tomas"}; for (int i = 0; i < pocet_hracu; i++) { cout << "Zadejte jmeno hrace: " << jmena[i] << endl; this->hraci[i] = new Hrac(jmena[i], kostka); } } Arena::~Arena() { for (int i = 0; i < this->pocet_hracu; i++) delete this->hraci[i]; delete[] this->hraci; this->hraci = NULL; } void Arena::vypis() { cout << "-------------- Arena --------------" << endl; cout << "Tah: " << this->tah << endl; cout << "Zdravi bojovniku:" << endl; for (int i = 0; i < this->pocet_hracu; i++) { cout << "\t" << this->hraci[i]->getJmeno() << ": "; // vypíšeme jméno bojovníka if (!this->hraci[i]->getBojovnik().nazivu()) // zjistíme, že bojovník ještě není mrtev { cout << "mrtev" << endl; // pokud je bojovník mrtev tak o tom informujeme continue; // a pokračujeme na dalšího bojovníka } cout << "["; // začátek baru se životy // vypočítáme kolik procent života bojovník má float pocet_zivotu_procent = this->hraci[i]->getBojovnik().getZivot() / this->hraci[i]->getBojovnik().getMaxZivot(); for (double z = 0; z < 1.0; z += 0.1) cout << (z < pocet_zivotu_procent ? '#' : ' '); // vypisujeme procenta života // ukončíme bar se životy a vypíšeme info o útoku a obraně cout << "] (utok: " << this->hraci[i]->getBojovnik().getUtok() << ", obrana: " << this->hraci[i]->getBojovnik().getObrana() << ")" << endl; } } void Arena::vypisViteze() { if (!this->existujeVitez()) return; for (int i = 0; i < this->pocet_hracu; i++) if (this->hraci[i]->getBojovnik().nazivu()) { cout << endl << "-------------- VITEZ --------------" << endl; cout << "Vitezem se stal: " << this->hraci[i]->getJmeno() << " s " << this->hraci[i]->getBojovnik().getZivot() << " zivoty" << endl; return; } } void Arena::zapas() { // dokud nezůstane pouze jeden hráč while (!this->existujeVitez()) { this->vypis(); // vypíšeme informace o hráčích // zkontroluj všechny hráče for (int i = 0; i < this->pocet_hracu; i++) { // pokud není bojovník naživu, potom ho přeskoč if (!this->hraci[i]->getBojovnik().nazivu()) continue; // mohlo se stát, že v předchozím kole někoho zabili a tak zůstal poslední bojovník // pokud se to stalo, potom hra končí if (this->existujeVitez()) break; // spočtení index nejbližšího živého hráče, na kterého budeme útočit int utok_na = (i + 1) % this->pocet_hracu; while (!this->hraci[utok_na]->getBojovnik().nazivu()) utok_na = (utok_na + 1) % this->pocet_hracu; // útok float zraneni = this->hraci[i]->getBojovnik().utoc(this->hraci[utok_na]->getBojovnik()); // vypsání výsledku souboje cout << this->hraci[i]->getJmeno() << " utoci na " << this->hraci[utok_na]->getJmeno() << " za " << zraneni << " zraneni" << endl; } this->tah++; } }
Gratulujem vám, ak ste sa dostali až sem a tutoriály naozaj čítali a pochopili, máte základy objektového programovania a dokážete tvoriť rozumné aplikácie
Tým máme (aspoň zo základu) hotovú našu arénu a nabudúce, v lekcii Konštantný metódy v C ++ , sa pozrieme na konštantnej metódy v C ++.
Mal si s čímkoľvek problém? Stiahni si vzorovú aplikáciu nižšie a porovnaj ju so svojím projektom, chybu tak ľahko nájdeš.
Stiahnuť
Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami
Stiahnuté 137x (9.41 kB)
Aplikácia je vrátane zdrojových kódov v jazyku C++