18:00Temat wykladu:
GameDev: Poziomy trudności w grach (by Minias, 30.08.06) @ wyklady.net
Komentarze: http://forum.wyklady.net/index.php?topic=82.0
18:02Miniasno dobra moze zaczniemy
Miniaszanim zaczne chce Wam przypomniec ze wyklad bedziecie mogli ocenic po jego zakonczeniu
Miniashttp://forum.wyklady.net/index.php?topic=82.0
Miniaso tutaj
18:03Miniasno dobra jestem Minias, mam 15 lat interesuje sie ogolnie programowaniem, takze gier :)
Miniasdzisiaj poprowadze wyklad o tym, jak od strony programisty wyglada tworzenie poziomow trudnosci w grach
18:04Miniaspo kazdym 'fragmencie' bedzie czas na pytania
Miniasa te najpilniejsze mozecie zadawac na priv ;)
Miniaswyklad dotyczy jezyka C++
Miniaschce jednak przypomniec ze nie sa to ani typowe podstawy, ani kurs jezyka
18:05Miniaszakladam ze czytajacy posiada pewna wiedze z zakresu programowania w C++
Miniasnie znam sie takze na Linuksie, wiec moga byc drobne bledy spowodowane roznicami w sciezkach do bibliotek
Miniasitd
18:06Miniasno coz, mam nadzieje ze bede w stanie odpowiedziec na wszystkie wasze pytania :)
Miniaswyklad jest wlasciwie podzielony na dwie czesci
Miniastej dotyczacej gier RPG
Miniasi tej dotyczacej tradycyjnych shooterow
Miniasno i moze cos jeszcze jak mi cos wpadnie do lba w czasie wykladu
Minias:P
Miniasno dobra, let's do that :)
Minias(najpierw teoria)
18:07Miniaswiadomo nie od dzis, ze przemierzajac swiat gier RPG natkniemy sie w koncu na potworki
Miniasi nic w tym dziwnego bo w koncu cos w tej grze robic trzeba :>
Miniaspotworki oczywiscie nie daja sobie w kasze dmuchac
Miniasi najczesciej stawiaja opor
Miniaspierwszy z naszych przykladow dotyczyc bedzie tego
Miniasjak wysoki jest to opor tzn
18:08Miniaschcemy zrobic tak, by na odpowiednim poziomie trudnosci zadawaly one mnie lub wiecej obrazen
Miniasmniej*
Miniasilustruje to obrazek nr1
Minias
18:09Miniasnasz cel mozemy zrealizowac za pomoca zmiennej statycznej w klasie
Minias(tu male wyjasnienie bo zmienna statyczna w klasie, a zmienna statyczna w funkcji to zupelnie co innego ;>)
Miniaswiec zmienna statyczna w FUNKCJI to taka, ktora zostaje w pamieci nawet po jej wykonaniu
Miniasi w kolejnym jej wykonaniu ma ona wartosc, jaka miala ostatnio
18:10Minias(zwykle zmienne w funkcji sa usuwane z pamieci po jej wykonaniu i pozostaja po nich jedynie smieci)
Miniasco do zmiennej statycznej w klasie: zmienna ta jest taka sama w kazdym obiekcie danej klasy
Miniasoto przyklad:
Minias#include <iostream>
Minias#include <string>
Minias
Miniasusing namespace std;
Minias
18:11Miniasclass Wyklad
Minias{
Miniaspublic:
Miniasstatic bool moderate;
Miniaschar autor[80],
Miniastemat[80];
Miniasvoid wyk();
MiniasWyklad(char *aut, char *tem)
Minias{
Miniasstrcpy(autor,aut);
Miniasstrcpy(temat,tem);
Minias{
Miniasstrcpy(autor,aut);
Miniasstrcpy(temat,tem);
Minias}
Minias};
Minias
Miniasbool Wyklad::moderate = true;
Minias
Miniasvoid main()
Minias{
MiniasWyklad Pierwszy("Minias", "blabla");
MiniasWyklad Drugi("sainiM", "bleble");
Miniascout << Pierwszy.moderate
Minias<< endl
18:12Minias<< Drugi.moderate;
Miniassystem("pause");
Minias}
Miniaswidzimy tu zmienna statyczna "klasowa" moderate typu bool (zwraca true lub false [prawda lub falsz])
Miniasdefiniujemy dwa obiekty typu Wyklad a nastepnie funkcja cout wypisujemy na ekran zawartosc zmiennej moderate
Miniaskazdego obiektu z osobna
Miniasjak widac w obydwu obiektach zmienna ma ta sama wartosc = 1 (czyli true) co potwierdza "moja" teorie
Miniasze zmienna statyczna w klasie jest zawsze taka sama
18:13Miniasprzy okazji chcialbym podkreslic, ze po zakonczeinu modulu klasy stawiamy srednik
Miniasno to teraz z tej klasy sprobujmy zbudowac klase potworkow
Miniasa wlasciwie klase zmienimy na strukture (wywolujemy slowem 'struct')
18:14Miniasrozni sie od klasy tym ze poprzez domniemanie kazdy jej skladnik jest publiczny (w klasie jest odwrotnie)
Miniaspodczas wykladu nie bede oddzielal danych prywatnych i publicznych
Miniaswiec w gruncie rzeczy struktura jest dla nas korzystniejsza
Miniasprzerobmy wiec klase na korzystna dla nas strukture:
18:15Miniasstruct Monster
Minias{ static int trudnosc;
Miniasint tMin, tMax;
Miniaschar tName[20];
Miniasvoid atak(int tObr);
MiniasMonster(char *name, int min, int max)
Minias{
Miniasstrcpy(tName, name);
MiniastMin = min;
MiniastMax = max;
Minias}
Minias};
18:16Miniasw taki sposob mamy gotowa strukturke potworkow (ktora w gruncie rzeczy przyda nam sie w duzej czesci wykladu)
Miniasmamy tu takze zdefiniowany konstruktor (typu inline)
Miniasdopasowuje on gorny i dolny zakres obrazen a takze nazwe potwora
Miniasnalezy jednak pamietac by nie przesadzac z konstruktorami / funkcjami typu inline
18:17Miniassa one szybsze w wykonaniu tylko w przypadku malych funkcji (1, 2 w ostatecznosci 3 linijkowych)
MiniasOczywiscie, zamiast zmiennej statycznej moglibysmy uzyc zmiennej globalnej
Miniasja uzylem zmiennej statycznej poniewaz byl to pierwszy pomysl na jaki wpadlem :)
Miniaspo prostu tak obmyslilem i tak ma byc ;>
18:18Miniasjak ktos chce moze uzywac zmiennej globalnej (w dalszej czesci wykladu w jednym przypadku i tak jej uzyjemy)
Miniaszalozmy ze mamy narazie taki fragment kodu:
Minias#include <iostream>
Minias#include <string>
Miniasusing namespace std;
Miniasinline int random(int nMin, int nMax)
Minias{ return rand() % (nMax - nMin + 1) + nMin;
Minias}
Minias
18:19Miniasstruct Monster
Minias{
Miniasstatic int trudnosc;
Miniasint tMin, tMax;
Miniaschar tName[20];
Miniasvoid atak(int tObr);
MiniasMonster(char *name, int min, int max)
Minias{
Miniasstrcpy(tName, name);
MiniastMin = min;
MiniastMax = max;
Minias}
Minias};
Miniaschcemy zrobic tak, by potwor zadawal nam losowe obrazenia
Miniasdlatego potrzebna jest nam funkcja "random"
Miniasfunkcja ta losuje liczbe z przedzialu nMin i nMax
18:20Miniasteraz dobudujmy funkcje 'atak' ze struktury Monster
Miniasvoid Monster::atak(int tObr);
Minias{ cout << tName << " zadal Ci " << tObr << " obrazen\n";
Minias}
Miniasna dobra sprawe takze moglaby byc to funkcja inline
Miniasprzestrzegam jednak, ze funkcje skladniowe danej klasy / struktury sa inline wtedy
18:21Miniasgdy ich definicja znajduje sie wewnatrz modulu klasy / struktury
Miniasnie nalezy dodawac 'inline' tak jak w przypadku funkcji random:)
Miniaswrocmy do naszego kodu
Miniastu pewne wyjasnienie co do funkcji Monster::atak i tName, ktore sie tam znalazlo
18:22Miniasno bo niby skad ono tam jest?
Miniasa wiec jest to zmienna aktualnego obiektu danej klasy / struktury
Miniasmoglibysmy ja takze oznaczyc wskaznikiem this, tzn
Miniascout << this->tName;
Miniasnie jest to jednak wymagane
Miniaskompilator sam sobie wstawi ten wskaznik
Miniasjak to mozliwe ze sie nie myli?
18:23Miniasotoz, wywolywanie funkcji skladniowej danej klasy jest mozliwe tylko przez obiekt
Miniasnie mozemy jej wywolac np. Monster::atak(100);
Miniastylko najpierw musimy zdefiniowac obiekt tej klasy, a dopiero poznije (z tego obiektu!) wywolac funkcje
Miniasnp. tak:
MiniasMonster Ork("Ork", 50, 100);
18:24MiniasOrk.atak(100);
Miniasno dobrze
Miniasskonczylismy na zbudowaniu funkcji Monster::atak
Miniasmamy tam tylko informacje o tym ze potwor zadal nam x obrazen
Miniasale czy napewno?
Miniastak na prawde wcale tego nie zrobil
Miniasjak zrobic, aby potwor zadawal REALNE obrazenia, dowiecie sie pozniej :>
Miniasw dalszej czesci wykladu
18:25Miniasno dobrze, do naszego kodu wystarczy dobudowac teraz procedure 'main'
Miniasint main()
Minias{ srand((unsigned int)time((time_t*)NULL)); bool dalej = false; while(!(dalej)) {
18:26Miniascout << "Podaj poziom trudnosci (1 lub 2 lub 3): ";
Miniascin >> Monster::trudnosc;
Miniasif((Monster::trudnosc == 1) || (Monster::trudnosc == 2) || (Monster::trudnosc == 3))
Minias{
Miniasdalej = true;
Minias}
Minias}
Miniasint a, b;
Miniasa = 5 * Monster::trudnosc;
Miniasb = 10 * Monster::trudnosc;
MiniasMonster Ork("Ork", a, b);
Miniasint obr;
Miniasobr = random(Ork.tMin, Ork.tMax);
MiniasOrk.atak(obr);
Miniassystem("pause");
Miniasreturn 0;
Minias
Minias}
Miniasteraz tak:
Miniasfunkcja srand to generator liczb pseudolosowych
18:27Miniasposlugujemy sie tutaj parametrem 'time', gdyz czas ciagle sie zmienia wiec liczby beda zawsze inaczej przelosowane
Miniasjej opis znajdziecie tutaj:
Miniashttp://www.linuxmanpages.com/man3/srand.3.php
Miniasw dalszej czesci kodu (w petli while [wiem ze powyzej nieladnie sie wkleilo, beda pozniej dostepne czytelne kody zrodlowe])
18:28Miniaswiec w petli while ustalamy poziom trudnosci (a dokladniej okreslamy wartosc zmiennej statycznej trudnosc klasy Monster)
Miniaszanim definiujemy obiekt typu Monster, okreslamy dolna i gorna granice obrazen
Miniasliczby 5 i 10, to pozycje wyjsciowe, ale ich wlasciwa wartosc obrazen zalezy od wyznaczonego poziomu trudnosci
18:29Miniasdalej znajduje sie definicja obiektu Ork za pomoca naszego konstruktora
Miniasnastepnie losujemy liczbe z przedzialu Ork.tMin (minimalne obrazenia) i Ork.tMax (maksymalne obrazenia)
Miniaswylosowana liczba to obrazenia, jakie nam w danej chwili zadal ork
Miniasta liczba jest parametrem funkcji Ork.atak
Miniaskod, ktory skonstruowalismy znajduje sie tutaj:
18:30Miniashttp://wyklady.net/minias/dev01.cpp
Miniassa pytania?
Miniasno to lecimy dalej
18:31Miniaspowyzej udalo nam sie zrobic tak, by potwor zadawal - zaleznie od poziomu trudnosci - mniej lub wiecej obrazen
Miniasteraz zrobimy rzecz odwrotna - czyli jak zrobic, ze to GRACZ zadaje wieksze / mniejsze obrazenai
Miniasilustruje to obrazek nr 2:
Minias
18:32Miniasczesc kodu jest taka sama, takze pozwole sobie ja skopiowac i uzupelnic o braki
Minias#include <iostream>
Minias#include <string>
Minias#include <ctime>
Miniasusing namespace std;
Miniasstruct Monster;
MiniasMonster *potwor;
Miniasint random(int nMin, int nMax)
Minias{ return rand() % (nMax - nMin + 1) + nMin;
Minias}
Minias
Miniasstruct Monster
Minias{
Miniasstatic int trudnosc;
Miniaschar tName[20];
18:33Miniasint nowHP,
MiniasmaxHP;
MiniasMonster(char *name, int max)
Minias{
Miniasstrcpy(tName, name);
MiniasmaxHP = max;
MiniasnowHP = maxHP;
Minias}
Minias};
Minias
Miniasint Monster::trudnosc = 0;
Minias
Miniasstruct Gracz
Minias{
Miniasint minObr,
MiniasmaxObr;
Miniasvoid atak(int tObr);
MiniasGracz(int minO, int maxO)
Minias{
MiniasminObr = minO;
MiniasmaxObr = maxO;
Minias}
Minias};
18:34Miniaszanim przejdziemy do omowienia struktury gracza chce zwrocic Wasza uwage na jedna wazna rzecz
Miniasmianowicie, na deklaracje (nie mylic z definicja!) struktury ("struct Monster;") oraz jej wskaznik
Miniaswskaznik ten przyda sie nam za chwile - a zanim moglismy go utworzyc, musimy znac typ na jaki ma wskazywac
Miniasi stad ta deklaracja struktury. Jest to tzw deklaracja zwiastujaca
Miniasw powyzszym przykladzie mamy juz dwie struktury: Gracza i potworka
Miniasw drugim przypadku mamy takze zmienne okreslajace stan jego zdrowia, ktory po naszych atakach bedzie malal
18:35Miniaszanim opracujemy zawartosc funkcji Gracz::atak() chcialbym najpierw okreslic zawartosc main()
Miniaswydaje mi sie, ze w ten sposob latwiej bedzie zrozumiec, co i jak dzieje sie w naszym przykladzie :)
Miniaswiec oto zawartosc naszej glownej procedury:
Miniasint main()
Minias{ srand((unsigned int)time((time_t*)NULL)); bool dalej = false; while(!(dalej)) {
Miniascout << "Podaj poziom trudnosci (1 lub 2 lub 3): ";
Miniascin >> Monster::trudnosc;
Miniasif((Monster::trudnosc == 1) || (Monster::trudnosc == 2) || (Monster::trudnosc == 3))
Minias{
Miniasdalej = true;
18:36Minias}
Minias}
Minias
MiniasMonster Ork("Ork", 200);
Miniaspotwor = &Ork;
Miniasint a, b;
Miniascout << "Podaj gorna granice obrazen gracza: ";
Miniascin >> a;
Miniascout << endl;
Miniasb = a / Monster::trudnosc;
MiniasGracz Ja(0, b);
Miniaswhile(!(potwor -> nowHP <= 0))
Minias{
Miniasint obr;
Miniasobr = random(Ja.minObr,Ja.maxObr);
MiniasJa.atak(obr);
Minias}
Miniassystem("pause");
Miniasreturn 0;
Minias
Minias}
Miniaspierwsza czesc (do zakonczenia petli while) jest taka sama jak uprzednio
18:37Miniastworzymy obiekt typu Monster posiadajacy 200 pkt zdrowia (ang heal points - hp)
Miniasa nastepnie nakierowujemy na niego wskaznik *potwor
Miniaspozniej podajemy wzgledna gorna granice obrazen gracza
Miniaswzgledna poniewaz gorna granica to wynik dzielenia naszej liczby przez poziom trudnosci
18:38Miniasnastepnie, dopoki HP Orka nie zejda do 0, wywolywana jest funkcja Gracz::atak
Miniasktora wlasnie zbudujemy:
Miniasvoid Gracz::atak(int tObr)
Minias{
Miniaspotwor -> nowHP -= tObr;
Miniascout << "Zadales " << tObr << " obrazen\n"
Minias<< potwor -> tName << " ma jeszcze " << potwor -> nowHP << " pkt zycia\n";
Minias}
Miniaso to jest juz REALNE zadawanie obrazen o ktorych wspominalem wczesniej :)
Miniasdopiero tu widzimy takze role wskaznika *potwor
18:39Miniasotoz okresla on przeciwnika, ktorego aktualnie atakujemy
Miniasnie bede sie tu na ten temat za duzo rozpisywal bo nie do konca tego dotyczy wyklad
Miniasaczkolwiek troszke nadmienie [teoria] jak zrobic wybieranie "aktualnego" potworka, kiedy jest ich wiecej w grze
Miniasno wiec pierwszy ze sposobow to taki ze okreslamy w klasie potworkow ich polozenie (wspolrzedne)
18:40Miniasi jesli gracz bedzie na tych samych wspolrzednych to potwor staje sie aktualnie atakowanym monsterkiem
Miniasto taki przyklad dla gier "pikselowych" takich jak przyklady w tym wykladzie ;>
Miniasinny sposob to taki, ze jesli w naszym otoczeniu znajduje sie kilka potworow
18:41Miniasto my (jako gracz) wybieramy tego, ktory ma atakowac (np. gra sie zatrzymuje i nas o to pyta)
Miniasno ale to tylko teoria, moze kiedys wiecej o tym sie powie ;)
Miniaswrocmy do nasszego wykladu
Miniasopisalismy juz praktycznie wszystko co potrzebne jest nam do powyzszego przykladu
Miniaswiec polaczmy nasze zrodelka w jednosc:
Miniashttp://wyklady.net/minias/dev02.cpp
Miniaspytania?
18:42Miniasno ok, zrobilismy juz zroznicowanie zadawanych obrazen
Miniaszarowno gracza jak i potwora
Miniasjednak obrazenia to nie wszystko: nawet zadajac duzy bol przeciwnikowi moze on ginac bardzo dlugo (chodzi oczywiscie o gre :P)
Miniaszaleznie od stanu jego punktow zdrowia
18:43Miniastego tez dotyczy nasz kolejny przyklad. Mianowicie napiszemy kod gdzie zaleznie od wyboru trudnosci
Miniaspotwor bedzie mial wiecej / mniej punktow zdrowia
Miniasprzedstawia to rys 3
Minias
18:44Miniasjest to wlasciwie analogia poprzednich dzialan, wiec moze tylko udostepnie caly kod zrodlowy i go pokrotce skomentuje
Miniashttp://wyklady.net/minias/dev03.cpp
Miniasstruct Monster
Minias{
Miniasstatic int trudnosc;
Miniaschar tName[20];
Miniasint nowHP,
MiniasmaxHP;
MiniasMonster(char *name, int max)
Minias{
Miniasstrcpy(tName, name);
MiniasmaxHP = max;
MiniasnowHP = maxHP;
Minias}
18:45Minias};
Miniasw strukturze potworkow nie ma nic zaskakujacego, mamy tu tylko pkt zycia i nazwe :)
Minias(pomijam poziom trudnosci i konstruktor)
Miniascout << "Podaj ilosc punktow zycia Orka:>>? ";
Miniascin >> a;
MiniasMonster Ork("Ork", (a * Monster::trudnosc));
Miniascout << "\nOrk na tym poziomie trudnosci posiada " << Ork.maxHP << " punktow zycia" << endl;
Miniasa tutaj odbywa sie czynnosc, ktora chcielismy osiagnac
Miniasnajpierw podajemy wzgledna, maksymalna ilosc pkt zycia Orka
Miniasnastepnie za pomoca konstruktora tworzymy obiekt z tym ze nalezy pamietac ze ilosc pkt zdrowia
Miniasto nie ta liczba ktora podalismy
18:46Miniastylko ta liczba PRZEMNOZONA przez nasz poziom trudnosci
Miniasnastepnie wyswietlamy, ile wynosi HP Orka na wybranym przez nas poziomie trudnosci ;)
Miniasi to tyle jesli chodzi o ten przyklad
Miniaspytania?
18:47Miniasno dobra, przerobilismy juz obrazenia i pkt zdrowia oparte o nasz system poziomow trudnosci
Miniasteraz zabawimy sie z doswiadczeniem, ktore otrzymujemy od potworkow, ale najpierw troche teorii
Miniaszazwyczaj w kazdej grze role-playing system jest oparty na schemacie
Miniasgdzie za zabitego potworka zyskujemy doswiadczenie (ang. experience - exp)
18:48Miniasa kiedy otrzymamy jego dostateczna ilosc, przechodzimy na nastepny poziom (ang level - lvl)
Miniaswtedy najczesciej zyskujemy kilka punktow doswiadczenia
Miniasdo rozdzielenia na wybrane statystyki
Miniasilustruje to rys 4
18:49Minias
Miniaswidzimy ze praktycznie wszystko kreci sie wokol zabijania potworkow
Miniasmozna powiedziec, ze musimy sie zapetlic, aby cos osiagnac :)
Miniaspierwsza metoda zrobienia trypow gry roznej trudnosci zwiazana z doswiadczeniem
Miniasto taka, aby za potworka otrzymywac mniej / wiecej expa, zaleznie od wyboru poziomu trudnosci
18:50Miniasaby to zrobic musimy najpierw okreslic tzw. wyjsciowa ilosc doswiadczenia z danego potworka
Miniaswarto tu zastosowac jakis uniwersalny wzor
Miniasu nas bedzie to
Miniasexp = (a * 2) / Poziom_Trudnosci;
Miniasgdzie 'a' to nasza wyjsciowa ilosc doswiadczenia podana przez nas
18:51Miniasjest to fragment konstruktora potworkow
Miniaswiec zmienna typu integer 'exp' jest opatrzona wskaznikiem 'this'
Miniaswspominalem o tym wczesniej
Miniaswyjasnil to takze MeMeK, o tutaj
Miniashttp://wyklady.net/logi/cpp_oop_memek.html
Minias;>
Miniasw praktyce wyglada to np. tak:
Miniashttp://wyklady.net/minias/dev04.cpp
18:52Miniasw sumie wyglada to na dobry sposob dopasowania zdobywanej ilosci doswiadczenia
Miniasale czasem lepszym rozwiazaniem moze byc zrobienie roznych wzorow
Miniasdla roznych poziomow trudnosci czyli
Miniasnajpierw sprawdzamy jaki poziom trudnosci jest ustalony
Miniaskiedy to juz wiemy, uzywamy odpowiedni wzor
Miniasilustruje to listing 5
Miniashttp://wyklady.net/minias/dev05.cpp
18:53Miniasw sumie jedyna zmiana w porownaniu do listingu 4
Miniasto zawartosc konstruktora Monsterkow
MiniasMonster::Monster(char *name, int max, int e)
Minias{
Miniasstrcpy(tName, name);
MiniasmaxHP = max;
MiniasnowHP = max;
Miniasif(trudnosc == 1)
Minias{
Miniasexp = e * 4;
Minias}
Miniaselse if(trudnosc == 2)
Minias{
Miniasexp = e * 2;
Minias}
Miniaselse if(trudnosc == 3)
Minias{
Miniasexp = e / 2;
Minias}
Minias}
18:54Miniasjak widac sa tu rozne wzory zaleznie od trudnosci
Miniasna najlatwiejszym poziomie exp jest 4-krotnie wiekszy
Miniasna srednim dwukrotnie, a na trudnym jest dwa razy mniejszy niz podany przez nas
Miniasczyli ogolnie jest to, o co nam chodzilo - zroznicowanie poziomow
Miniasw sumie trudno powiedziec ktory sposob warto uzywac (uniwersalny lub osobny)
18:55Miniasja powiedzialbym, ze wzor uniwersalny warto wykorzystac gdy chcemy osiagnac mala roznice pomiedzy poziomami trudnosci
Miniasjesli zas chcemy by przepasc pomiedzy trudnosciami byla wieksza - powinnismy zainteresowac sie indywidualnymi wzorami
Miniasi w sumie to wszystko jesli chodzi o doswiadczenie z potworkow
18:56Miniasale nasza przygoda z experience jeszcze sie nie zakonczyla
Minias:)
Miniaspotrafimy zrobic juz zroznicowanie jesli chodzi o bezposrednio otrzymywanie doswiadczenie z potworkow
Miniasale to nie wszystko
MiniasPora opracowac metode, dzieki ktorej zaleznie od poziomu trudnosci wymagana bedzie wieksza liczba doswiadczenia
Miniasaby uzyskac nastepny poziom
18:57Miniasi tu w sumie takze mozna zrobic uniwersalny wzor lub osobne dla kazdego poziomu trudnosci
Miniasja przedstawie tu tylko rozwiazanie z tym pierwszym, osobne wzory sa analogiczne jak w poprzednim przykladzie
18:58Miniasno wiec chodzi nam o to by na niszym (latwiejszym) poziomie trudnosci wymagane bylo mniej expa
Miniasa na wyzszym (trudniejszym) wiecej aby osiagnac nastepny lvl
Miniaswlasciwie jest tu podobnie jak z doswiadczeniem z potworkow
Miniastyle tylko ze tu bedziemy dzielic
Miniashttp://wyklady.net/minias/dev06.cpp
Miniasmetoda dzialania jest prosta - w strukturze Gracza znajduja sie dwie zmienne zwiazane z expem:
18:59Minias- pierwsza (allExp) pokazuje ile zdobylismy doswiadczenia do tej pory
Minias- druga (expToLvl) pokazuje ile nam jeszcze brakuje doswiadczenia do nastepnego lvl
Miniaskiedy otrzymujemy jakies doswiadczenie, ilosc expToLvl sie zmniejsza podczas gdy allExp = zwieksza sie
Miniasa nasz uniwersalny wzor to:
19:00MiniasexpToLvl = (lvl + 20) * Monster::trudnosc;
Miniasniestety jak pewnie zauwazyliscie podpatrujac kod zrodlowy - jest pewien problem
Miniasa mianowicie, kiedy otrzymamy doswiadczenie i przekroczy ono iloc potrzebnego nam do next lvl
Miniasto nie przechodzi ono do nastepnego naliczania
19:01Miniasniby nic, ale kiedy przejdzie nam na nastepna kolejke 50000expa i przez to przepadnie - mozna sie wkurzyc;>
Miniasoczywiscie i na to jest sposob. Wystarczy zmienic ponizszy fragment:
Miniascout << target.tName << " zabity :D!\n" << "Otrzymales " << target.exp << " doswiadczenia\n";
MiniasallExp += target.exp;
MiniasexpToLvl -= target.exp;
Miniasif(expToLvl <= 0)
Minias{ awans();
Minias}
Miniaselse cout << "Do nastepnego lvl brakuje Ci jeszcze " << expToLvl << endl;
Miniasnizej przykladowe rozwiazanie problemu
Minias(nalezy tylko zrobic tak, by funkcja Gracz::awans() przyjmowala wartosc int)
19:02Miniasnp. Gracz::awans(int nadwyzka);
Miniascout << target.tName << " zabity :D!\n" << "Otrzymales " << target.exp << " doswiadczenia\n";
MiniasallExp += target.exp;
MiniasexpToLvl -= target.exp;
Miniasif(expToLvl < 0)
Minias{ int a; a = +expToLvl; awans(a);
Minias}
Miniasif(expToLvl = 0)
Minias{ awans(0);
Miniasi zawartosc funkcji Gracz::awans:
Minias}
Miniaselse cout << "Do nastepnego lvl brakuje Ci jeszcze " << expToLvl << endl;
Miniasvoid Gracz::awans(int nadwyzka)
Minias{ cout << "Osiagnales nastepny poziom doswiadczenia!\n"; lvl++; expToLvl = (lvl + 20) * Monster::trudnosc; expToLvl -= nadwyzka; allExp += nadwyzka;
Minias}
19:03Miniasi koniec problemu ze zmarnowanym doswiadczeniem :)
Miniaswyjasnie tylko co oznacza linijka 'a = +expToLvl'
Miniasotoz jest to zmiana znaku zmiennej expToLvl na dodatni
Miniasbo to oczywiste, ze skoro jest mniejsze od 0 to jest minusowe. A my chcemy ja na plusie ;>
19:04Miniaspytania?:P
Miniasno to jedziemy dalej
Miniasteraz zabawimy sie ze statystykami
Miniasjak wczesniej wspomnialem w grach rpg wystepuja statystyki
Miniastakie jak sila, zrecznosc, inteligencja, zywotnosc itd
19:05Miniasotoz kolejna zabawa z poziomem trudnosci polega na tym
Miniasze zmieniamy ilosc otrzymywania pkt do rozdzielenia na wybrane statystyki kiedy otrzymujemy next lvl
19:06Miniasotrzymujemy ich wiecej lub mniej, zaleznie od trudnosci
Miniasosobiscie nie polecam tego sposobu zroznicowania
Miniasstatystyki powinno sie zawsze otrzymywac w takiej samej ilosci ;>
Miniasale opisac nie zaszkodzi:)
19:07Miniasjesli ktos sie zdecyduje uzyc tego sposobu, to nie polecam takze uniwersalnych sposobow na ilosc pkt statystyk
Miniaslepiej dla kazdego z poziomow trudnosci ustalic osobny wzor
Miniasoto przyklad
Miniashttp://wyklady.net/minias/dev07.cpp
Miniasto poprzedni kod ze zmieniona funkcja Gracz::awans
Miniasvoid Gracz::awans(int nadwyzka)
Minias{ cout << "Osiagnales nastepny poziom doswiadczenia!\n"; lvl++; expToLvl = (lvl + 20) * Monster::trudnosc; expToLvl -= nadwyzka; allExp += nadwyzka; if(Monster::trudnosc == 1) {
19:08Miniasstats += 10;
Minias}
Miniaselse if(Monster::trudnosc == 2)
Minias{
Miniasstats += 8;
Minias}
Miniaselse if(Monster::trudnosc == 3)
Minias{
Miniasstats += 5;
Minias}
Minias}
Minias(ech rozlazlo sie)
19:09Miniasjak widac (albo i nie widac;/) zaleznie od poziomu trudnosci otrzymujemy konkretnie okreslona ilosc punktow
Miniasdo rozdzielenia na statystyki
Miniasnatknelismy sie tutaj na statystyki. W powyzszym przykladzie nie robilismy z nimi nic szczegolnego
Miniasnic od nich nie zalezalo
Miniasbaa, nawet nie istnialy ;>
19:10Miniaspora naprawic nasz (moj;p?) blad. Na poczatku zmodyfikujmy strukture Gracza:
Miniasstruct Gracz
Minias{
Miniasint tObr,
MiniasallExp,
MiniasexpToLvl,
Miniaslvl,
Miniasstats,
Miniassila,
Miniaszywotnosc,
MiniasnowHP,
MiniasmaxHP;
MiniasGracz(int obr);
Miniasbool atak(Monster &target);
Miniasvoid awans(int nadwyzka);
Minias};
Miniasdodalismy dwie statystyki na potrzeby wykladu
Miniastj sila i zrecznosc
19:11Miniaswystarcza nam, aby zaprezentowac jak nadac im jakas wazna role :)
Miniasspojrzmy na nasz konstruktor
MiniasGracz::Gracz(int obr)
Minias{ tObr = obr; allExp = 0; lvl = 0; expToLvl = ((lvl + 2) * 5) * Monster::trudnosc;
Minias}
Miniasnas juz to nie zadowala
Miniaspo pierwsze nie ma tu zadnej wzmianki o sile czy zrecznosci
Miniaszywotnosci*
19:12Miniasa po drugie nie odpowiada nam juz wzor
MiniastObr = obr;
Miniaszmienmy wiec troszke konstruktor:
MiniasGracz::Gracz(int obr)
Minias{
Miniasstats = 5;
Miniaswhile(stats > 0)
Minias{
Miniaschar znak;
Miniascin >> znak;
Miniasif(znak == 's')
Minias{
Miniassila++;
Miniasstats--;
Minias}
19:13Miniaselse if(znak == 'z')
Minias{
Miniaszywotnosc++;
Miniasstats--;
Minias}
Minias}
MiniastObr = obr + sila;
MiniasmaxHP = 30 + (zywotnosc * 2);
MiniasnowHP = maxHP;
MiniasallExp = 0;
Miniaslvl = 0;
MiniasexpToLvl = ((lvl + 2) * 5) * Monster::trudnosc;
Minias}
Miniasjak widzimy jest tu juz przedstawiona pewna rola sily i zywotnosci:)
Miniasnalezy takze zmienic Gracz::awans:
Miniasvoid Gracz::awans(int nadwyzka)
Minias{ cout << "Osiagnales nastepny poziom doswiadczenia!\n";
Miniaslvl++;
MiniasexpToLvl = (lvl + 20) * Monster::trudnosc;
MiniasexpToLvl -= nadwyzka;
MiniasallExp += nadwyzka;
Miniasif(Monster::trudnosc == 1)
Minias{
19:14Miniasstats += 10;
Minias}
Miniaselse if(Monster::trudnosc == 2)
Minias{
Miniasstats += 8;
Minias}
Miniaselse if(Monster::trudnosc == 3)
Minias{
Miniasstats += 5;
Minias}
Miniaswhile(stats > 0)
Minias{
Miniaschar znak;
Miniascin >> znak;
Miniasif(znak == 's')
Minias{
Miniassila++;
Miniasstats--;
Minias}
Miniaselse if(znak == 'z')
Minias{
Miniaszywotnosc++;
Miniasstats--;
Minias}
19:15Minias}
MiniastObr += sila + lvl;
MiniasmaxHP += 5 * zywotnosc;
MiniasnowHP = maxHP;
Minias}
Miniasnajpierw dodajemy punkty do rodzielenia kiedy otrzymamy next lvl
Minias(zaleznie od poziomu trudnosci jest ich wiecej lub mniej)
Miniasa pozniej je rozdajemy, dopoki je mamy :)
Miniasnastepnie ponownie okreslamy wartosc tObr
Miniasi maxHP
Miniasi to tyle jesli chodzi o zabawe ze statystykami
Miniasale powtarzam to, co nadmienilem na poczatku:
Miniasnie polecam tego sposobu zroznicowania poziomow
19:16Miniasdam jeszcze malutka podpowiedz odnosnie statystyk
Miniasbo w powyzszym przykladzie pominalem kwestie np. zrecznosci
Miniasotoz gdyby ktos chcial w swej grze dodac 'zrecznosc' i chcialby, aby odpowiadala ona za czestotliwosc (szybkosc) atakow
Miniasto zaprezentuje tutaj przykladowe rozwiazanie tego problemu
19:17Miniasmianowicie
Miniasmozna to zrobic za pomoca funkcji Sleep() - Windows
Miniaslub sleep() - Linuks
Miniasprzyklad uzycia:
Minias#include <iostream>
Minias
Minias#ifdef __unix__ #include <unistd.h>
Minias#else
Minias#include <windows.h>
Minias#endif
Minias
Miniasusing namespace std;
Minias
Miniasint main()
Minias{
Miniascout << "a";
Minias#ifndef __unix__
MiniasSleep(1000);
19:18Minias#else sleep(1000);
Minias#endif
Miniascout << "b";
Miniassystem("pause");
Miniasreturn 0;
Minias
Minias}
Miniaswartosc funkcji sleep (Sleep) to milisekundy
Miniasprzypominam ze 1000 msek = 1 sek
Miniasmozna wprowadzic do niej takze zrecznosc np. tak
Miniassleep(czaspoczatkowy - (zrecznosc * 10));
19:19Miniasgdzie czas poczatkowy to czestotliwosc ataku na poczatku rozgrywki ;>
Miniasno dobra, ale to tylko taki dodatek dla zainteresowanych :)
Miniaspytania jakies?
Minias:>
Miniasno dobrze
19:20Miniasto teraz zajmijmy sie czyms prostszym;>
Miniasotoz od zawsze wiadomo, ze to, co napedza gry z gatunku rpg to zadania poboczne
Minias(odtad bede je po prostu nazywal jako "questy")
Miniasno wiec ta swoboda w grach rpg jest esencja ich zycia:)
Miniasodpuscic sobie glowny watek i poniesc sie tej plochej przygodzie - questom :>
19:21Miniasraz jest tak, ze za questy dostajemy doswiadczenie - a raz, ze nie - ze dostajemy tylko jakas materialna nagrode
Miniasbron lub zloto, nevermind :)
Miniasi tu dochodzimy do kwestii ktora chcialem omowic a mianowicie
Miniaszaleznie od poziomu trudnosci po wykonaniu questu zyskujemy doswiadczenie lub nie
19:22Miniasilustruje to obrazek 5
Minias
Miniasw sumie to dosc proste:
Minias- na poziomie trudnym (3) nie otrzymujemy doswiadczenia po wykonaniu questu
Minias- na poziomie latwnym (1) otrzymujemy doswiadczenie po wykonaniu questu
Minias- a na poziomie normalnym (2).. no wlasnie, kto zgadnie co?;>
19:23Miniasotoz to zalezy od naszego ogolnego(!) zalozenia co do gry
Miniasjesli chcemy, by nieotrzymywanie doswiadczenia bylo utrudnieniem na poziomie trudnym
Miniasto na poziomie normalnym powinnismy je otrzymywac
Miniasnatomiast jesli doswiadczenie z questow ma byc milym dodatkiem na najlatwiejszym poziomie
Miniasto na poziomie normalnym otrzymywac go nie powinnismy
19:24Miniasmam nadzieje ze to jasne bo oto przyklad tego drugiego rozwiazania:
Miniashttp://wyklady.net/minias/dev08.cpp
Miniaswszystko zalezy od zmiennej statycznej struktury Quest - tzn "static bool exp;"
Miniasjezeli jej wartosc to true (prawda) to otrzymujemy doswiadczenie
Miniasa jezeli false (falsz), to go nie otrzymujemy
19:25Miniasw sumie otrzymywanie w tym przykladzie czegokolwiek to fikcja
Miniasbo doswiadczenie przekazywane jest nam tylko poprzez funkcje 'cout'
Miniasi nic innego nie mozemy z nim zrobic
Miniaszeby to dzialalo poprawnie musielibysmy dodac strukture gracza i rozwiazac to podobnie jak w przykladzie 7
Miniasale to juz zostawie Wam, bo to proste i na pewno Wam sie uda to polaczyc;>
19:26Minias"Item tNagroda" to nagroda materialna, ktora mozemy dostac za quest
Miniaswspomniana wczesniej bron czy zloto:)
Miniasmysle ze nie ma z questami zadnych niejasnosci
19:27Miniaswiec pominmy zdejmowanie opcji moderate ;>
Miniasjak ktos ma pytania to na priv prosze:)
Miniasno wiec
Miniaswrocmy ponownie do potworkow :)
Miniastym razem zainteresujmy sie potworkami i ich stuffem
Miniasa mianowicie, kiedy wreszcie uda nam sie takiego gnojka zabic
Miniasto zwykle zrzucaja z siebie jakies przedmioty
19:28MiniasPrzy odrobinie szczescia mozna sie niezle oblowic
MiniasNo wlasnie. Przy odrobinie szczescia
Miniasa te odrobine szczescia mozemy regulowac za pomoca poziomu trudnosci
Miniastzn im latwiejszym poziom, tym wieksza szansa ze z potworka wypadna itemki
Miniasi to jest naszym aktualnym tematem
Miniashttp://wyklady.net/minias/dev09.cpp
19:29Miniaswszystko znow jest zalezne od wzoru
Miniasu nas jest to
MiniasDM.iSzansa = ((5 + DM.iWartosc * Monster::trudnosc) / 2);
Miniasale takze od warunku
Miniasif((a >= 5) && (a <= 10))
19:30Miniasto kazdy moze dopasowac do swoich indywidualnych potrzeb
Miniasw kazdym razie w powyzszym przykladzie przy konstrukcji obiektu typu Monster
Miniasdobieramy mu jeden przedmiot typu Item.
Miniasoczywiscie moglibysmy mu dodac wiecej tych przedmiotow robiac np. w strukturze potworkow
Miniastablice obiektow Item - jeden na helm, inny na bron itd.
19:31Miniasten przedmiot to rzecz, ktora moze wypasc z potworka jesli dany warunek jest spelniony
Miniasszansa przedmiotu: "im mniejsza, tym wieksza!" :D
Miniasalbo inaczej: im mniejsza, tym latwiej go zdobyc ;>
Miniasi w ten sposob osiagnelismy co chcielismy :)
Miniaspytania?
19:32Miniasno dobra, wiec lecimy dalej
19:33Miniaspewnie kazdy z Was podczas milych podrozy po krainach swiata gier rpg
Miniasnatrafil na rozne, ciekawsze lub mniej, skrzynie beczki i rozne pojemniki
Miniasw ktorych znajdowaly sie przedmioty o duzej lub malej wartosci
Miniasw czesci grach te dane (tzn co w danej skrzyni sie znajduje) sa zawsze takie same
19:34Miniasale w innych zawartosc skrzyn jest losowa
Miniasi dochodzimy do sedna sprawy. Losowosc zawartosci. Co do dla nas znaczy?
Miniasa no tyle, ze zaleznie od poziomu trudnosci mozemy wylosowac lepsza lub troszke gorsza zawartosc skrzyni:)
Miniasokreslenie lepsza lub gorsza tyczy sie oczywiscie wartosci danego przedmiotu;)
19:35Miniasi w sumie tyle teoria
Miniasteraz gorsze pytanie: JAK to zrobic? :>
Miniasna poczatku musimy stworzyc klase (strukture;p) pojemnikow
Miniasoto ona
Miniasstruct Pojemnik
Minias{ static int Pojemnik::trudnosc; int wspX, wspY, wspZ; Item inside[2]; Pojemnik(int X, int Y, int Z); void zawartosc(Item mozliwosci[]);
Minias};
19:36Minias(wybaczcie ze sie rozlazlo, pozniej bedziecie mieli do wgladu czytelniejszy kod)
Miniasw strukturze pojemnikow nie ma zadnej nazwy
Miniasidentyfikowac skrzynie bedziemy tylko po wspolrzednych
19:37Minias"Item inside[2]" to tablica obiektow typu Item
Miniasjak widac sklada sie ona z 2 indexow
Miniasw naszym przykladzie w skrzyni znajdowac sie beda 2 rzeczy :)
19:38MiniasPrzenieslismy tutaj zmienna trudnosc, gdyz struktura monsterkow nie jest nam w tym przykladzie potrzebna - na smietnik z nia!:P
Miniasstworzmy teraz kilka itemkow (zalozmy, 4)
MiniasItem przedmioty[4];
Miniasstrcpy(przedmioty[0].name, "Morgenstern");
Miniasprzedmioty[0].iWartosc = 30;
Miniasprzedmioty[0].iSzansa = ((5 + przedmioty[0].iWartosc * Pojemnik::trudnosc) / 2);
19:39Minias
Miniasstrcpy(przedmioty[1].name, "Miecz");
Miniasprzedmioty[1].iWartosc = 40;
Miniasprzedmioty[1].iSzansa = ((5 + przedmioty[1].iWartosc * Pojemnik::trudnosc) / 2);
Minias
Miniasstrcpy(przedmioty[2].name, "Topor");
Miniasprzedmioty[2].iWartosc = 60;
Miniasprzedmioty[2].iSzansa = ((5 + przedmioty[2].iWartosc * Pojemnik::trudnosc) / 2);
Minias
Miniasstrcpy(przedmioty[3].name, "Rozdzka");
Miniasprzedmioty[3].iWartosc = 100;
Miniasprzedmioty[3].iSzansa = ((5 + przedmioty[3].iWartosc * Pojemnik::trudnosc) / 2);
Miniasnasze itemki :)
Miniasteraz musimy wylosowac dwie z nich - ale w taki sposob, by na mniejszych poziomach trudnosci losowac te lepsze
Miniasa na trudniejszych te gorsze
Miniasoto rozwiazanie:
19:40Miniashttp://wyklady.net/minias/dev10.cpp
Miniascala tajemnica twki w funkcji void Pojemnik::zawartosc(Item tItem[])
Miniaskod jest dlugi, ale nie skomplikowany
Miniasmysle ze sami zrozumiecie o co w nim chodzi ;)
19:41Miniasja chce zwrocic Wasza uwage na inna rzecz
Miniasze w strukturze Itemkow jest zmienna typu bool - 'cenny'
Miniasokresla ona, czy przedmiot jest cenny czy tez nie jest
Minias(w naszym przypadku jest cenny gdy jego wartosc wynosi ponad 50)
19:42Miniasa gdy jest cenny - to nie moze sie znalezc w skrzyni na najtrudniejszym poziomie
Miniasna poczatku dodajemy takze do inside[0] i inside[1] pojemnika wzmianke, ze te miejsca sa puste
19:43Miniasgdybysmy tego nie zrobili - i nie wylosowalibysmy na to miejsce zadnego przedmiotu
Miniaswyswietlilyby sie smieci
Miniaslepiej wyglada slowo 'puste' niz jakies znaczki :>
Miniasoczywiscie zdaje sobie sprawe, ze przeciez nasz "skrypt" moze uznac nawet slowo 'puste' za jakis item
19:44Miniasmozemy jednak to naprawic
Miniasif(!((inside[0].name == "puste") || (inside[1].name == "puste")
Minias{ Player::bierz();
Minias}
Miniasale funkcje bierz() bedziecie juz musieli opracowac sami ;>
Miniasa moze innym razem wspomne o tym przy okazji;)
Miniasno to moze tym razem sa pytania? :)
19:46Miniasno to lecimy dalej
Miniasw sumie teraz omowie cos co troche wykracza poza temat wykladu
Miniasale mysle ze warto to wyjasnic :)
19:47Miniasmam na mysli klonowanie potworkow
Minias(spotykania tych samych gatunkow wiele razy)
Miniaschodzi oczywiscie o tablice obiektow typu Monster: )
Miniasjesli to dla kogos nie jest jasne, to niech spojrzy na listing 11
Miniashttp://wyklady.net/minias/dev11.cpp
19:48Miniasmysle ze tu pytan nie ma to i oszczedzimy sobie zdejmowanie opcji moderate ;>
Miniaszreszta dzis nie jestescie zbyt pytajacy :)
Miniasale jak ktos ma pytanie to na priv prosze :>
Miniasno to przejdzmy do czegos blizszego tematu tego wykladu
Miniasomowilismy juz wiele zagadnien zwiazanych z grami RPG
Miniasale co z shooterami?
19:49Miniasno powiedzmy, ze wlasnie do nich przechodzimy :)
Miniasw sumie nie bedzie to tak rozbudowane jak poprzednie kwestie, ale zawsze cos:)
Miniaspierwsza kwestia to apteczki
Miniaszwykle po pewnym odcinku na kazdej planszy mozemy natknac sie na apteczki
19:50Miniasi tutaj rola poziomow trudnosci: w zaleznosci od poziomu trudnosci znajdujemy wiecej lub mniej apteczek na swej drodze
Miniasprzy okazji wykorzystamy tutaj nowo nabyta umiejetnosc klonowania - tym razem apteczek:)
Miniaszdefiniujmy strukture Apteczka
Miniasstruct Apteczka
Minias{
Miniasstatic int trudnosc;
Miniasint wsplX,
MiniaswsplY,
MiniaswsplZ,
MiniasileHp;
Miniasbool istnienie;
Miniasvoid regeneracja();
Minias};
19:51Miniaszmienna 'istnienie' ustala czy apteczka istnieje czy tez nie. Jest to wymagane przez metode ktora my wybralismy
Minias'ileHp' okresla, ile dana apteczka regeneruje nam pkt zycia:)
Miniasw sumie to takze moglby okreslac poziom trudnosci
Miniashttp://wyklady.net/minias/dev12.cpp
Miniasoto przykladowy kod zwiazany z apteczkami
19:52Miniaswidzimy, ze im latwiejszy poziom trudnosci tym wiecej apteczek ma ustawiona zmienna 'istnienie' na true
Miniasczyli wiecej apteczek znajdujemy na swej drodze:)
MiniasApteczka zadziala, gdy gracz bedzie stal na tych samych wspolrzednych (w powyzszym kodzie juz tego nie ma)
Miniasanalogicznie postepujemy z amunicja, ktora mozna znalezc po drodze:
Miniashttp://wyklady.net/minias/dev13.cpp
Miniascos jest niejasne?:)
19:53garusjak narazie wszystko ok :D
19:54Miniastak.. wiec omowmy kwestie wspolna gier rpg i strzelanek;>
Miniasno bo wiadomo, przemierzajac swiat i w rpg, i w shooterach, natykamy sie na hordy potworkow
Miniasi w ten niepozorny temat znow wciska sie nam poziom trudnosc ;)
Miniastrudnosci*
Miniasczyli
Miniasw zaleznosci od ustalonego poziomu trudnosci spotykamy wiecej / mniej przeciwnikow
19:55Miniasilustruje to rys.6
Minias
Miniaswlasciwie mozna to rozwiazac praktycznie tak samo jak z apteczkami / amunicja
Miniaswiec mysle ze rozpisywanie sie na ten temat jest zbedne ;)
19:56Miniasno i w sumie to tyle jesli chodzi o dzisiejszy wyklad, tzn tyle bylo w planach
Miniasmysle ze nie warto juz przedluzac, zreszta i tak skonczyly mi sie pomysly:P
Miniastak wiec nie zapomnijcie ocenic i skomentowac wykladu
Miniashttp://forum.wyklady.net/index.php?topic=82.0
Miniasjednoczesnie niesmialo zapowiadam, ze byc moze poprowadze drugi wyklad o game developingu;>
Miniasniesmialo;>
Miniasdobra, dzieki wszystkim:)
Miniaskoniec :)