TlačiťTlačiť Slovenčina English Hľadať RSS

© 2005 – 2024 Roman Horváth, všetky práva vyhradené. Dnes je 20. 4. 2024.

Stránka sa načítava, prosím čakajte…

Dátum: 26. 10. 2013, pred viacerými rokmi, aktualizované: 1. 8. 2020, pred štyrmi rokmi

Na tejto stránke je publikovaná implementácia jednoduchej hry Asteroidy. Je implementovaná s využitím programovacieho rámca grafického robota. otvárané v novom okne
(obvykle ide o externý odkaz)

  
obrázok 
Ukážka vzhľadu hry po spustení. 
 

 

~

import knižnica.GRobot;
import knižnica.Svet;
import knižnica.Zoznam;
import knižnica.Zvuk;

/**
 * Hlavná trieda, ktorá slúži na inicializáciu aplikácie a koordináciu
 * činnosti inštancií ostatných tried.
 */
public class Asteroidy extends GRobot
{
	// Faktor zmenšenia polomeru pri rozpade asteroidov, ktorý je vypočítaný
	// tak, aby obsah kružnice vypočítanej z veľkosti (polomeru) asteroidu
	// po rozpade bol polovičný ako pred rozpadom: S₂ = S₁ / 2, z čoho sa dá
	// odvodiť, že r₂² = r₁² / 2, z čoho vyplýva, že faktor zmenšenia
	// polomeru je rovný √¹⁄₂:
	private final static double faktor = 0.70710678118654752440084436210485;

	// Inštancia rakety:
	private static Raketa raketa;

	// Inštancie asteroidov:
	private final static Zoznam<Asteroid> asteroidy = new Zoznam<Asteroid>();

	// Inštancie striel:
	private final static Zoznam<Strela> strely = new Zoznam<Strela>();


	/**
	 * Konštruktor hlavnej triedy. Inicializuje aplikáciu, vytvára nevyhnutné
	 * inštancie, kreslí pozadie, konfiguruje rôzne vlastnosti a aktivuje hru.
	 */
	private Asteroidy()
	{
		// Nastavenia súvisiace s oknom aplikácie:
		Svet.zbaľ();
		Svet.farbaPozadia(čierna);

		// Vypnutie automatického prekresľovania (prekreslenie je zabezpečené
		// v reakcii tik):
		Svet.nekresli();

		// Nastavenie cesty k priečinku, ktorý obsahuje zvuky:
		Zvuk.priečinokZvukov("zvuky");

		// Skoré čítanie zvukov do vnútornej pamäte sveta, aby nevznikla
		// pauza pred prvým pokusom o prehratie zvuku (anglicky sa tento
		// proces nazýva preloading):
		Svet.čítajZvuky("boom.wav", "explode.wav", "shot.wav");

		// Skrytie hlavného robota:
		skry();

		// Vytvorenie inštancie rakety:
		raketa = new Raketa();


		// Nakreslenie niekoľkých dekoračných hviezd na pozadí (s použitím
		// hlavného robota):
		farba(svetložltá);
		veľkosť(5);
		for (int i = 0; i < 50; ++i)
		{
			náhodnáPoloha();
			náhodnýSmer();
			vyplňHviezdu();
		}
		podlaha.rozmaž(10);


		// Vytvorenie úvodných inštancií asteroidov (mieriacich na raketu):
		for (int i = 0; i < 5; ++i)
			asteroidy.pridaj(new Asteroid(30, raketa));

		// Aktivácia rakety (aj s časovačom):
		raketa.aktivuj();
	}


	// Metóda slúžiaca na rozbíjanie asteroidov na menšie časti. Metóda
	// prijíma v parametri inštanciu rozbíjaného asteroidu. Najprv zistí,
	// či má tento dostatočnú veľkosť na to, aby ešte mohol byť rozdelený
	// na menšie časti. Ak áno, zmenší jeho veľkosť o faktor vypočítaný tak,
	// aby plocha kruhu veľkosti nového asteroidu bola polovičná oproti
	// starej (metódou upravVeľkosť, pretože zmena veľkosti znamená zmenu
	// grafiky asteroidu a úpravu niektorých ďalších vlastností), nájde
	// (alebo vytvorí) najbližší neaktívny asteroid a vyrobí z neho dvojičku
	// tohto asteroidu, čím je efekt rozbitia úplný. Ak asteroid nemá
	// dostatočnú veľkosť na rozbitie, tak je deaktivovaný.
	private void rozbi(Asteroid asteroid)
	{
		if (asteroid.veľkosť() >= 5.5)
		{
			asteroid.upravVeľkosť(asteroid.veľkosť() * faktor);

			// Hľadanie jestvujúceho neaktívneho asteroidu:
			for (Asteroid dvojička : asteroidy)
				if (dvojička.neaktívny())
				{
					dvojička.aktivujDvojičku(asteroid);
					return;
				}

			// Vytvorenie nového asteroidu:
			Asteroid dvojička = new Asteroid();
			asteroidy.pridaj(dvojička);
			dvojička.aktivujDvojičku(asteroid);
		}
		else asteroid.deaktivuj();
	}


	/**
	 * Reakcia na tik časovača. Tu sa kontrolujú kolízie asteroidov, striel
	 * a rakety. Tiež sa tu podľa potreby vykonáva prekresľovanie grafiky
	 * sveta.
	 */
	@Override public void tik()
	{
		// Najskôr sa skontrolujú kolízie všetkých aktívnych striel
		// s aktívnymi asteroidmi. Keď je zistená kolízia, tak je
		// deaktivovaná strela a zavolaná metóda rozbi (definovaná
		// vyššie) s dotknutým asteroidom v argumente. Potom je prehraný
		// zvuk signalizujúci rozbitie asteroidu.
		for (Strela strela : strely) if (strela.aktívny())
			for (Asteroid asteroid : asteroidy)
				if (asteroid.aktívny() && strela.koliduje(asteroid))
				{
					strela.deaktivuj();
					rozbi(asteroid);
					Svet.zvuk("boom.wav");
					break;
				}

		// V prípade, že raketa nemá dištanc, sú skontrolované jej prípadné
		// kolízie s asteroidmi. Keď detegovaná kolízia, raketa havaruje
		// (čo je realizované volaním metódy rakety havaruj, ktorej
		// „rozsudok“ je v tomto projekte mierny – iba raketu presunie
		// do stredu plátna a nastaví jej nový dištanc). Zároveň sa asteroid
		// dôsledkom nárazu rozbije (rovnako ako keby ho trafila strela).
		if (raketa.nemáDištanc())
			for (Asteroid asteroid : asteroidy)
			{
				if (asteroid.aktívny() &&
					raketa.koliduje(asteroid))
				{
					raketa.havaruj();
					rozbi(asteroid);
					Svet.zvuk("explode.wav");
					break;
				}
			}

		// Prekreslenie sveta v prípade, že boli detegované nejaké zmeny:
		if (Svet.neboloPrekreslené()) Svet.prekresli();
	}


	/**
	 * Statická metóda, ktorá slúži na poskytovanie inštancií neaktívnych
	 * striel na prácu s nimi. Potrebuje ju najmä raketa, ktorá si vyžiada
	 * inštanciu strely pri každom výstrele. Metóda najskôr hľadá neaktívnu
	 * strelu v zozname striel a ak takú nenájde, vyrobí novú.
	 */
	public static Strela dajStrelu()
	{
		// Hľadanie neaktívnych striel v zozname striel:
		for (Strela strela : strely)
			if (strela.neaktívny())
				return strela;

		// Výroba novej strely (ak nebola nájdená vyhovujúca strela):
		Strela strela = new Strela();
		strely.pridaj(strela);
		return strela;
	}


	/**
	 * Hlavná metóda slúžiaca na spúšťanie aplikácie.
	 *
	 * @param args parametre prijaté z konzoly, ktorá aplikáciu spúšťala
	 *     (v tomto projekte nie sú využité)
	 */
	public static void main(String[] args)
	{
		Svet.použiKonfiguráciu("Asteroidy.cfg");
		new Asteroidy();
	}
}

~

import knižnica.Bod;
import knižnica.GRobot;
import knižnica.Oblasť;
import knižnica.Svet;

/**
 * Trieda asteroidov. Paradoxne, to jest napriek tomu, že sa zdá, že
 * asteroid „je len kamienok pasívne plávajúci priestorom,“ je táto trieda
 * najkomplikovanejšou v tomto projekte.
 *
 * Hlavným dôvodom je, že správanie sa asteroidu sa najviac odchyľuje od
 * predprogramovaného správania sa robotov: asteroidy náhodne menia rýchlosť,
 * musia byť generované na náhodnej pozícii pri okraji obrazovky, majú
 * nepravidelný tvar (pripomínajúci neobrúsený kameň), ktorý si samy generujú
 * (prípadne ďalšie odchýlky).
 */
public class Asteroid extends GRobot
{
	// Tento atribút (konštanta) určuje počet bodov, z ktorých bude pozostávať
	// nepravidelný tvar asteroidu:
	private final static int POČET_BODOV = 20;

	// V tomto atribúte (konštante) sa uchová uhol medzi dvomi bodmi tvaru
	// asteroidu. Musí byť rovný podielu 360 / počet bodov tvaru asteroidu:
	private final static double UHOL = 360.0 / (double)POČET_BODOV;

	// Oblasť uchovávajúca vlastný tvar tohto asteroidu:
	private Oblasť tvar;


	/*#
	 * Nasledujú definície dvoch druhov konštruktorov. Každý je určený na
	 * použitie v inej situácii. Prvý konštruktor (bez parametrov) je tzv.
	 * predvolený konštruktor. Prvým príkazom druhého konštruktora je volanie
	 * predvoleného konštruktora: this(), čiže všetko, čo zabezpečuje
	 * predvolený konštruktor sa automaticky prejaví aj v druhom konštruktore.
	 *
	 * (Poznámka: Aby asteroidy nekreslili pri presune čiaru, je do
	 * predvoleného konštruktora zaradený príkaz na zdvihnutie pera.
	 * To isté platí pre strely aj raketu.)
	 */


	/**
	 * Tento (predvolený) konštruktor vytvorí pasívny asteroid. Týmto
	 * spôsobom sú asteroidy vytvárané iba pri procese ich rozbíjania na
	 * menšie kúsky. To je dôvod, prečo tento konštruktor automaticky
	 * negeneruje tvar asteroidu. Jeho tvar je vygenerovaný až po aktivácii
	 * (kvázi „spárovaní sa“) ako dvojičky iného asteroidu. (Pri tom sa
	 * určuje mení jeho veľkosť a tvar by musel byť generovaný znova, takže
	 * sa tým šetrí výpočtový výkon.)
	 */
	public Asteroid()
	{
		farba(hnedá);
		hrúbkaČiary(1.75);
		zdvihniPero();
		náhodnáRýchlosť();
		skry();
	}

	/**
	 * Tento konštruktor vytvorí asteroid so zadanou veľkosťou nasmerovaný na
	 * (zadanú) raketu.
	 */
	public Asteroid(double veľkosť, Raketa raketa)
	{
		this();
		upravVeľkosť(veľkosť);
		náhodnáPolohaNaOkraji();
		otočNa(raketa);
	}


	/**
	 * Táto metóda slúži na aktivovanie dvojičky k zadanému asteroidu (kvázi
	 * „spárovanie sa“). Dvojička vzniká vždy pri rozbíjaní asteroidu. Pri
	 * procese aktivácie dvojičky (úmyselne sa vyhýbame slovu „spárovanie,“
	 * aby nevznikal dojem, že dvojičky zostávajú nejakým spôsobom naďalej
	 * prepojené) sa vlastnosti tejto inštancie asteroidu prispôsobia
	 * vlastnostiam zadanej inštancie asteroidu.
	 *
	 * Najskôr sa upraví veľkosť tohto asteroidu podľa zadaného asteroidu
	 * (volaním metódy upravVeľkosť, ktorá zároveň generuje náhodný tvar
	 * asteroidu), potom je tento asteroid presunutý na pozíciu zadaného
	 * asteroidu, nasmeruje sa náhodným smerom a nakoniec je aktivovaný.
	 *
	 * (Táto metóda by mala byť volaná len pre neaktívne asteroidy. V tomto
	 * projekte je toto pravidlo dodržané, preto metóda neobsahuje žiadnu
	 * kontrolu splnenia tejto podmienky.)
	 */
	public void aktivujDvojičku(Asteroid asteroid)
	{
		upravVeľkosť(asteroid.veľkosť());
		poloha(asteroid);
		náhodnýSmer();
		náhodnáRýchlosť();
		aktivuj();
	}

	/**
	 * Generuje náhodnú polohu asteroidu na okraji plátna. Vychádzame
	 * z predpokladu, že na to, aby bola hra dobre hrateľná, by sa prvé
	 * asteroidy mali vygenerovať na okraji plátna, to jest v dostatočnej
	 * vzdialenosti od rakety, ktorá sa predvolene nachádza v jeho strede.
	 *
	 * Toto sa dá dosiahnuť relatívne jednoducho:
	 *
	 *    • asteroid presunieme do stredu plátna,
	 *    • nasmerujeme ho náhodným smerom
	 *    • a posunieme ho dopredu o náhodnú avšak pomerne veľkú vzdialenosť
	 *      (v tomto prípade v rozmedzí od 300 do 310 bodov).
	 */
	public void náhodnáPolohaNaOkraji()
	{
		poloha(stred);
		náhodnýSmer();
		dopredu(Svet.náhodnéCeléČíslo(300, 310));
		aktivuj();
	}

	/**
	 * Nastaví asteroidu náhodnú (konštantnú) rýchlosť.
	 */
	public void náhodnáRýchlosť()
	{
		rýchlosť(Svet.náhodnéReálneČíslo(2.0, 3.5), false);
	}


	/**
	 * Spolu s úpravou veľkosti asteroidu sa náhodne mení aj generovaný
	 * nepravidelný tvar asteroidu.
	
	 * Na vygenerovanie nového tvaru sú tvorivým spôsobom využité metódy
	 * robota:
	 *
	 *    • Najskôr sa uložia súradnice série bodov umiestnených relatívne
	 *      pravidelne (v súvislosti s uhlovou odchýlkou) po obvode kružnice
	 *      s polomerom veľkosti asteroidu.
	 *    • Pritom každý bod je náhodne posunutý od polomeru, takže výsledný
	 *      tvar vytvorený pospájaním bodov bude pripomínať neobrúsenú skalu.
	 *    • Nakoniec je séria bodov prevedená prostredníctvom cesty na oblasť.
	 *
	 * Oblasť je potom použitá v reakcii na kreslenie (nižšie).
	 *
	 * 	(Predchádzajúce vysvetlenie generovania nového tvaru by bolo
	 * 	názornejšie viditeľné na obrázku. Predstavte si niekoľko bodov
	 * 	(napríklad päť) tvoriacich pravidelný n-uholník (napríklad
	 * 	päťuholník). Teraz si predstavte, že sa každý z bodov náhodne
	 * 	posunie smerom k stredu alebo od stredu n-uholníka. Výsledný tvar
	 * 	bude mierne deformovaný. Ak je počet uhlov n-uholníka vyšší,
	 * 	napríklad 10 alebo 20, tak bude výsledný útvar podobný balvanu.)
	 */
	public void upravVeľkosť(double nováVeľkosť)
	{
		// Úprava veľkosti a rýchlosti otáčania tvaru asteroidu:
		veľkosť(nováVeľkosť); mierka(1);
		otáčajTvar(Svet.náhodnéReálneČíslo(-1.0, 1.0));

		// Záloha pôvodnej polohy asteroidu (na obnovenie po vygenerovaní
		// nového tvaru):
		Bod poloha = poloha();

		// Definícia nového ohraničenia asteroidu (podľa jeho rozmerov):
		poloha(stred);
		ohranič(Svet.najväčšieX() + nováVeľkosť,
			Svet.najväčšieY() + nováVeľkosť);

		// Pole bodov, z ktorých bude vytvorený tvar asteroidu:
		Bod body[] = new Bod[POČET_BODOV];

		// Generovanie série bodov
		for (int i = 0; i < POČET_BODOV; ++i)
		{
			// Krok 1: Presunutie robota (resp. asteroidu; tento robot =
			// asteroid) do stredu plátna a nastavenie prislúchajúceho uhla
			// (násobku konštanty UHOL, riadiacej premennej i a náhodného
			// koeficientu):
			skočNa(stred);
			uhol(i * UHOL * Svet.náhodnéReálneČíslo(0.95, 1.05));

			// Krok 2: Posun dopredu o veľkosť asteroidu jemne upravenú
			// náhodným koeficientom v rozmedzí ⟨0,75; 1,25⟩:
			dopredu(nováVeľkosť * Svet.náhodnéReálneČíslo(0.75, 1.25));

			// Uloženie polohy získanej prostredníctvom predchádzajúcich
			// dvoch krokov:
			body[i] = poloha();
		}

		// Vytvorenie cesty:
		poloha(body[0]); začniCestu();
		for (int i = 1; i < POČET_BODOV; ++i) poloha(body[i]);

		// Prevedenie cesty na oblasť:
		tvar = new Oblasť(cesta());

		// Skok (návrat) na pôvodné súradnice robota:
		poloha(poloha);
	}


	/**
	 * Reakcia na kreslenie obkreslí oblasť, v ktorej je uložený tvar tohto
	 * asteroidu (ak tvar jestvuje).
	 */
	@Override public void kresliTvar()
	{
		if (null != tvar)
			obkresliOblasť(tvar);
	}

	/**
	 * Pri aktivácii sa asteroid automaticky zobrazí.
	 */
	@Override public void aktivácia()
	{
		zobraz();
	}

	/**
	 * Pri deaktivácii sa asteroid automaticky skryje.
	 */
	@Override public void deaktivácia()
	{
		skry();
	}
}

~

import knižnica.GRobot;
import knižnica.Oblasť;
import knižnica.Svet;
import knižnica.Zoznam;

import static knižnica.ÚdajeUdalostí.*;
import static knižnica.Kláves.*;

/**
 * Trieda pre raketu. Definuje správanie rakety, ktorá je ústredným prvkom
 * hry.
 *
 * Väčšinu jej funkčnosti zabezpečuje trieda GRobot, vďaka čomu je jej
 * implementácia relatívne jednoduchá.
 *
 * Relatívne dôležitou vlastnosťou, ktorá ju odlišuje od robota je tzv.
 * „dištanc“ (pozri definíciu atribútu nižšie).
 */
public class Raketa extends GRobot
{
	// Súkromný atribút určujúci dištanc rakety. Je to v podstate časový
	// interval, počas ktorého raketa nedokáže strieľať a nekoliduje
	// s asteroidmi.
	//
	// (Technické zabezpečenie, čiže použitie tohto atribútu, je realizované
	// v metódach Raketa.vystreľ a Asteroidy.tik.)
	private int dištanc = 100;

	/**
	 * Predvolený konštruktor rakety. Nastaví potrebné vlastnosti rakety
	 * (ohraničenie, farbu, veľkosť a pod.).
	 */
	public Raketa()
	{
		ohranič();
		farba(svetlotyrkysová);
		veľkosť(20.0);
		zdvihniPero();
		hrúbkaČiary(1.75);
	}


	/**
	 * Havária rakety je v tomto projekte šetrná. Raketa je premiestnená do
	 * stredu plátna a dostane stobodový dištanc.
	 */
	public void havaruj()
	{
		skočNa(stred);
		dištanc = 100;
	}


	/**
	 * Táto metóda overí, či nemá raketa dištanc.
	 */
	public boolean nemáDištanc()
	{
		return dištanc <= 0;
	}

	/**
	 * Reakcia na aktivitu, v ktorej je zabezpečené blikanie rakety počas
	 * trvania dištancu.
	 */
	@Override public void aktivita()
	{
		if (dištanc > 0)
		{
			// Počas dištancu raketa pravidelne bliká:
			if (--dištanc % 10 < 5)
				zobraz(); else skry();
		}
	}


	// Metóda vystreľ najprv skontroluje, či nemá raketa dištanc. Ak nemá, tak
	// si vyžiada neaktívnu inštanciu strely a za sprievodu zvuku s nastavením
	// rovnakej polohy a uhla ako má raketa ju aktivuje. (Skrátene povedané:
	// raketa vystrelí.)
	private void vystreľ()
	{
		if (nemáDištanc())
		{
			Strela strela = Asteroidy.dajStrelu();
			strela.poloha(this);
			strela.uhol(this);
			strela.aktivuj();
			Svet.zvuk("shot.wav");
		}
	}


	/**
	 * Reakciou na stlačenie kurzorových klávesov je otáčanie a posun rakety.
	 */
	@Override public void stlačenieKlávesu()
	{
		switch (kláves())
		{
			case HORE:   rýchlosť(10.0); break;
			case DOLE:   rýchlosť(-10.0); break;
			case VPRAVO: uhlováRýchlosť(-15.0); break;
			case VĽAVO:  uhlováRýchlosť(15.0); break;
		}
	}

	/**
	 * Reakciou na uvoľnenie kurzorových klávesov je zastavenie otáčania
	 * alebo posunu rakety. Uvoľnenie medzerníka znamená vystrelenie.
	 */
	@Override public void uvoľnenieKlávesu()
	{
		switch (kláves())
		{
			case HORE:   case DOLE:  rýchlosť(0.0); break;
			case VPRAVO: case VĽAVO: uhlováRýchlosť(0.0); break;
			case MEDZERA: vystreľ(); break;
		}
	}
}

~

import knižnica.GRobot;
import knižnica.KreslenieTvaru;

/**
 * Trieda pre strely. Definuje správanie striel, ktoré strieľa raketa.
 * Základná funkčnosť strely je definovaná pomerne jednoducho:
 *
 *    • strela je predvolene deaktivovaná,
 *    • po aktivácii sa pohybuje rovnomerne priamočiaro, kým neopustí
 *      obrazovku, kedy sa automaticky deaktivuje,
 *    • aktivácia strely (pri výstrele) je realizovaná v metóde Raketa.vystreľ
 *    • a detekcia kolízií s asteroidmi v hlavnej triede.
 */
public class Strela extends GRobot
{
	// Vlastný tvar strely je jednoduchý: rovná krátka čiarka. Objekt
	// kreslenia (inštancia triedy KreslenieTvaru) vytvorený prostredníctvom
	// lambda výrazu je uložený do statického atribútu, aby bol použiteľný
	// pre všetky strely súčasne.
	private static KreslenieTvaru tvar = r -> r.dopredu(10);

	/**
	 * Predvolený konštruktor strely nastavujúci základné vlastnosti strely
	 * ako je tvar (vzhľad), rýchlosť atď.
	 */
	public Strela()
	{
		ohranič();
		farba(tmavotyrkysová);
		zdvihniPero();
		hrúbkaČiary(2.5);
		vlastnýTvar(tvar);
		rýchlosť(20.0, false);
		skry();
	}

	/**
	 * Pri aktivácii sa strela automaticky zobrazí.
	 */
	@Override public void aktivácia()
	{
		zobraz();
	}

	/**
	 * Pri deaktivácii sa strela automaticky skryje.
	 */
	@Override public void deaktivácia()
	{
		skry();
	}

	/**
	 * Strela sa po opustení plátna automaticky deaktivuje.
	 */
	@Override public void mimoHraníc()
	{
		deaktivuj();
	}
}

Poznámka: Tento projekt je starší. Bude potrebné ho aktualizovať tak, aby bol kompatibilný s najnovšou verziou (už sa stalo) programovacieho rámca GRobot.

Na tejto stránke je publikovaná zjednodušená verzia hry Asteroidy naprogramovaná s využitím skupiny tried grafického robota. otvárané v novom okne
(obvykle ide o externý odkaz) Na vygenerovanie prázdneho projektu odporúčam použiť Generátor projektov BlueJ (dostupný v sekcii Softvér na prevzatie).

  
obrázok 
Triedy projektu. 
 

 

~

/**
 * Trieda pre strely. Definuje správanie striel, ktoré strieľa raketa. Celá
 * základná funkcionalita strely je definovaná pomerne jednoducho: strela je
 * predvolene deaktivovaná; po aktivácii sa pohybuje rovnomerne priamočiaro,
 * kým neopustí obrazovku, kedy sa automaticky deaktivuje; ostatné
 * záležitosti, ktoré súvisia s činnosťou strely (t. j. aktivácia = výstrel
 * a detekcia kolízií s asteroidmi) sú definované v hlavnej triede.
 */
public class Strela extends GRobot
{
	// Vlastný tvar strely je jednoduchý: rovná krátka čiarka. Objekt
	// vzniknutý inštancionalizáciou anonymnej implementácie rozhrania
	// „VlastnýTvar“ je uložený do statickej premennej, aby bol použiteľný
	// pre všetky strely súčasne.
	private static VlastnýTvar tvar = new VlastnýTvar()
	{
		@Override public void kresli(GRobot r)
		{
			r.dopredu(10);
		}
	};

	/**
	 * Predvolený konštruktor strely nastaví základné vlastnosti strely ako
	 * tvar (vzhľad), rýchlosť, atď.
	 */
	public Strela()
	{
		farba(tmavotyrkysová);
		zdvihniPero();
		hrúbkaČiary(2.5);
		vlastnýTvar(tvar);
		rýchlosť(20.0, false);
		skry();
	}

	/**
	 * Pri aktivácii sa strela automaticky zobrazí.
	 */
	@Override public void aktivácia()
	{
		zobraz();
	}

	/**
	 * Pri deaktivácii sa strela automaticky skryje.
	 */
	@Override public void deaktivácia()
	{
		skry();
	}

	/**
	 * Strela sa automaticky deaktivuje po opustení plátna.
	 */
	@Override public void aktivita()
	{
		if (polohaX() > svet.najväčšieX() ||
			polohaX() < svet.najmenšieX() ||
			polohaY() > svet.najväčšieY() ||
			polohaY() < svet.najmenšieY())
		{
			deaktivuj();
		}
	}
}

~

/**
 * Trieda pre raketu. Definuje správanie rakety, ktorá je ústredným prvkom
 * hry. Jej základná fukncionalita je definovaná pomerne jednoducho. Väčšinu
 * funkčnosti zabezpečuje trieda GRobot, vďaka čomu môže byť implementácia
 * zjednodušená. Dôležité je vedieť najmä to, či má raketa tzv. „dištanc“
 * (vysvetlené nižšie) a zabezpečiť to, aby nemohla opustiť obrazovku (vždy
 * po prekročení okraja sa objaví na protiľahlom kraji). Zvyšná funkcionalita
 * rakety (ovládanie, kolízie, streľba) je definovaná v hlavnej triede.
 */
public class Raketa extends GRobot
{
	// Súkromná premenná určujúca dištanc rakety. Počas dištancu raketa
	// nedokáže strieľať a nekoliduje s asteroidmi. To je zabezpečené na
	// iných miestach projektu, konkrétne v metódach hlavnej triedy „vystreľ“
	// a „aktivita“.
	private int dištanc = 0;

	/**
	 * Predvolený konštruktor rakety. Nastaví základné vlastnosti rakety
	 * (vzhľad a pod.)
	 */
	public Raketa()
	{
		farba(svetlotyrkysová);
		veľkosť(20);
		zdvihniPero();
		hrúbkaČiary(2.5);

		// Nastaví „dutý“ tvar rakety (bude
		// kreslená predvoleným tvarom robota):
		predvolenýTvar(false);
	}

	/**
	 * Havária rakety je v tomto projekte šetrná. Raketa je premiestnená do
	 * stredu plátna a dostane stobodový dištanc.
	 */
	public void havaruj()
	{
		skočNa(0, 0);
		dištanc = 100;
	}

	/**
	 * Táto metóda slúži na zistenie toho, či má raketa práve dištanc.
	 * (Je použitá v hlavnej triede.)
	 */
	public boolean máDištanc()
	{
		return dištanc > 0;
	}

	/**
	 * Prekrytá metóda aktivita kontroluje niektoré parametre rakety…
	 */
	@Override public void aktivita()
	{
		if (dištanc > 0)
		{
			// Počas dištancu raketa pravidelne bliká
			if (--dištanc % 10 < 5)
				zobraz(); else skry();
		}

		// Nasledujúce správanie platí bez ohľadu na dištanc – raketa
		// sa po prekročení okraja plátna objaví na protiľahlej strane:

		if (polohaX() > svet.najväčšieX())
			polohaX(svet.najmenšieX());

		if (polohaX() < svet.najmenšieX())
			polohaX(svet.najväčšieX());

		if (polohaY() > svet.najväčšieY())
			polohaY(svet.najmenšieY());

		if (polohaY() < svet.najmenšieY())
			polohaY(svet.najväčšieY());
	}
}

~

/**
 * Trieda pre asteroidy. Paradoxne je táto trieda najkomplikovanejšou triedou
 * tohto projektu, pretože správanie sa a iné vlastnosti asteroidov sú
 * relatívne komplikované. Musia mať nepravidelný tvar pripomínajúci
 * neobrúsený kameň. Musia rovnomerne rotovať a pritom sa pohybovať
 * priamočiarou konštantnou rýchlosťou. Definície v triede robot, ktoré
 * slúžia na zabezpečenie automatickej zmeny orientácie robota podľa uhlovej
 * rýchlosti a polohy robota podľa priamočiarej rýchlosti by sa dostali do
 * konfliktu, keby sme ich chceli využiť obidve naraz. Preto si treba vybrať,
 * ktorý vstavaný mechanizmus bude použitý a ktorý bude zabezpečený ináč.
 * Predovšetkým tieto dve požiadavky (tvar a spôsob pohybu) mali za následok
 * vznik o niečo vyššieho počtu čiastkových problémov, v porovnaní
 * s ostatnými triedami projektu. Riešenia sú poskytnuté nižšie…
 */
public class Asteroid extends GRobot
{
	// Súkromené premenné:
	// – oblasť uchovávajúca vlastný tvar tohto asteroidu,
	private Oblasť tvar;

	// – hodnoty smeru a rýchlosti asteroidu, ktoré musíme definovať týmto
	//   spôsobom, pretože sme sa rozhodli používať automatickú rotáciu
	//   asteroidu.
	private double smer = Smer.SEVER;
	private double rýchlosť = 0.0;

	/*#
	 * Nasledujú definície dvoch druhov konštruktorov. Každý je určený na
	 * použitie v inej situácii. Pred definíciami konštruktorov je vsunutá
	 * definícia súkromnej metódy „reset“, ktorá zoskupuje nastavovanie
	 * spoločných vlastností asteroidov…
	 */

	private void reset()
	{
		farba(hnedá);
		hrúbkaČiary(2);
		zdvihniPero();
		skry();
	}


	/**
	 * Tento konštruktor vytvorí asteroid so zadanou veľkosťou nasmerovaný na
	 * raketu.
	 */
	public Asteroid(double veľkosť, Raketa r)
	{
		reset();
		upravVeľkosť(veľkosť);
		náhodnáPolohaNaOkraji();
		nasmerujNa(r);
	}

	/**
	 * Tento (predvolený) konštruktor vytvorí pasívny asteroid určený „do
	 * rezervy“. Asteroidy vytvorené týmto spôsobom budú využité pri procese
	 * rozbíjania aktívnych asteroidov na menšie kúsky.
	 */
	public Asteroid()
	{
		reset();
	}


	/**
	 * Táto metóda slúži na aktivovanie dvojičky k zadanému asteroidu, ktorá
	 * vzniká pri rozbíjaní. Dvojičkou je táto inštancia, ktorá sa prispôsobí
	 * vlastnostiam zadanej inštancie „a“. Najskôr sa upravuje veľkosť tohto
	 * asteroidu podľa zadaného asteroidu „a“ (volaním metódy „upravVeľkosť“,
	 * ktorá zároveň generuje náhodný tvar asteroidu), potom je tento
	 * asteroid presunutý na rovnakú polohu ako má zadaný asteroid „a“,
	 * nasmeruje sa náhodným smerom a nakoniec je aktivovaný. Táto metóda by
	 * mala byť volaná len neaktívne asteroidy. V tomto projekte je pravidlo
	 * dodržané, preto metóda neobsahuje žiadnu kontrolu splnenia tejto
	 * podmienky.
	 */
	public void aktivujDvojičku(Asteroid a)
	{
		upravVeľkosť(a.veľkosť());
		skočNa(a);
		nasmerujNáhodne();
		aktivuj();
	}

	/**
	 * Spolu s úpravou veľkosti asteroidu sa náhodne mení aj vygenerovaný
	 * nepravidelný tvar asteroidu. Na vygenerovanie tvaru sú tvorivým
	 * spôsobom využité metódy robota. Najskôr sa uložia súradnice série
	 * desiatich bodov umiestnených pravidelne (aspoň v súvislosti s uhlovou
	 * odchýlkou) po obvode kružnice, pričom každý bod je náhodne odchýlený
	 * od originálneho polomeru, ktorý je rovný veľkosti robota.
	 * Predchádzajúce vysvetlenie by bolo názornejšie viditeľné na obrázku.
	 * Predstavte si sériu bodov, ktorých spojením by sme získali pravidelný
	 * desaťuholník. Potom každý z bodov náhodne posuňte smerom k stredu
	 * alebo od stredu desaťuholníka. Spojením posunutých bodov nevznikne
	 * pravidelný desaťuholník, ale útvar podobný balvanu. Nakoniec je séria
	 * bodov prevedená prostredníctvom cesty na oblasť. Oblasť je potom
	 * použitá v prekrytej metóde „kresli“ (nižšie) na nakreslenie balvanu.
	 */
	public void upravVeľkosť(double veľkosť)
	{
		double x[] = new double[10];
		double y[] = new double[10];

		// Záloha pôvodnej polohy robota
		java.awt.geom.Point2D poloha = poloha();

		// Generovanie série bodov
		for (int i = 0; i < 10; ++i)
		{
			domov();
			doprava(i * 36);
			dopredu(veľkosť *
				svet.náhodnéReálneČíslo(1, 2));

			x[i] = polohaX();
			y[i] = polohaY();
		}

		// Vytvorenie cesty
		skočNa(x[0], y[0]);
		začniCestu();
		for (int i = 1; i < 10; ++i)
			skočNa(x[i], y[i]);

		// Prevedenie cesty na oblasť
		tvar = new Oblasť(cesta());

		// Skok na pôvodné súradnice robota
		poloha(poloha);

		// Úprava veľkosti a uhlovej rýchlosti robota
		veľkosť(veľkosť);
		uhlováRýchlosť(svet.
			náhodnéCeléČíslo
			(-8, 8), false);
	}

	/**
	 * Náhodná poloha asteroidu by sa mala nachádzať v dostatočnej
	 * vzdialenosti od rakety, čiže by mala byť na okraji ďalej od stredu
	 * plátna. Toto sa dá dosiahnuť jednoducho: asteroid sa presunie do
	 * stredu plátna, nasmeruje sa náhodným smerom a posunie sa dopredu
	 * o náhodnú vzdialenosť v rozmedzí 200 až 210 bodov. Aby asteroidy
	 * nekreslili pri presune čiaru, je do konštruktora zaradený príkaz na
	 * zdvihnutie pera.
	 */
	public void náhodnáPolohaNaOkraji()
	{
		skočNa(0, 0); náhodnýSmer();
		dopredu(svet.náhodnéCeléČíslo(200, 210));
	}

	/**
	 * Táto metóda nasmeruje asteroid smerom na zadaného robota (ktorým
	 * je v tomto projekte raketa).
	 */
	public void nasmerujNa(GRobot r)
	{
		smer = smerNa(r) + svet.náhodnéReálneČíslo(-15, 15);
		rýchlosť = svet.náhodnéReálneČíslo(2, 3.5);
	}

	/**
	 * Táto metóda nasmeruje asteroid náhodným smerom (to je užitočné pri
	 * rozbíjaní asteroidov).
	 */
	public void nasmerujNáhodne()
	{
		smer = svet.náhodnéReálneČíslo(0.0, 360.0);
		rýchlosť = svet.náhodnéReálneČíslo(2, 3.5);
	}

	/**
	 * S cieľom zjednodušenia tejto triedy bol zvolený menej efektívny, ale
	 * jednoduchší spôsob kreslenia vlastného tvaru.
	 */
	@Override public void kresliTvar()
	{
		if (null != tvar)
			obkresliOblasť(tvar);
	}

	/**
	 * Metóda aktivita implementuje rovnomerný priamočiary pohyb posunom
	 * o hodnoty vlastného smeru a rýchlosti. Asteroid sa po prekročení
	 * hranice plátna objaví na protiľahlej strane plátna.
	 */
	@Override public void aktivita()
	{
		posuňVSmere(smer, rýchlosť);

		if (polohaX() > svet.najväčšieX() + veľkosť())
			polohaX(svet.najmenšieX() - veľkosť());

		if (polohaX() < svet.najmenšieX() - veľkosť())
			polohaX(svet.najväčšieX() + veľkosť());

		if (polohaY() > svet.najväčšieY() + veľkosť())
			polohaY(svet.najmenšieY() - veľkosť());

		if (polohaY() < svet.najmenšieY() - veľkosť())
			polohaY(svet.najväčšieY() + veľkosť());
	}

	/**
	 * Pri aktivácii sa asteroid automaticky zobrazí.
	 */
	@Override public void aktivácia()
	{
		zobraz();
	}

	/**
	 * Pri deaktivácii sa asteroid automaticky skryje.
	 */
	@Override public void deaktivácia()
	{
		skry();
	}
}

~

/**
 * Trieda {@code HlavnáTrieda} slúži na spúšťanie aplikácie. Koordinuje
 * činnosť ostatných inštancií tried.
 *
 * @author     Roman Horváth
 * @version    1.0
 */
public class HlavnáTrieda extends GRobot
{
	// Inštancie hry:
	private Raketa raketa = new Raketa();
	private Zoznam<Strela> strely = new Zoznam<Strela>();
	private Zoznam<Asteroid> asteroidy = new Zoznam<Asteroid>();

	/**
	 * Predvolený konštruktor – v tomto projekte štartuje aplikáciu.
	 * V konštruktore definujeme obsluhu udalostí na ovládanie robota s pomocou
	 * klávesnice a vytvárame všetky inštancie potrebné na plynulé fungovanie
	 * hry.
	 */
	private HlavnáTrieda()
	{
		// Nastavenia súvisiace s hlavným oknom:
		super(400, 300);
		svet.zbaľ();
		svet.farbaPozadia(čierna);

		// Vypnutie automatického prekresľovania:
		svet.nekresli();

		// Skrytie hlavného robota:
		skry();

		// Definícia obsluhy udalostí:
		new ObsluhaUdalostí()
		{
			@Override public void stlačenieKlávesu()
			{
				switch (údajeUdalostí.kláves())
				{
					case Kláves.HORE: raketa.rýchlosť(10.0); break;
					case Kláves.DOLE: raketa.rýchlosť(-10.0); break;
					case Kláves.VPRAVO: raketa.uhlováRýchlosť(-15.0); break;
					case Kláves.VĽAVO: raketa.uhlováRýchlosť(15.0); break;
				}
			}

			@Override public void uvoľnenieKlávesu()
			{
				switch (údajeUdalostí.kláves())
				{
					case Kláves.HORE:
					case Kláves.DOLE:
						raketa.rýchlosť(0.0);
					break;

					case Kláves.VPRAVO:
					case Kláves.VĽAVO:
						raketa.uhlováRýchlosť(0.0);
					break;

					case Kláves.MEDZERA:
						vystreľ();
					break;
				}
			}
		};

		// Vytvorenie 10 inštancií striel:
		for (int i = 0; i < 10; ++i)
			strely.pridaj(new Strela());

		// Vytvorenie 3 inštancií asteroidov mieriacich na raketu:
		for (int i = 0; i < 3; ++i)
			asteroidy.pridaj(new Asteroid(30, raketa));

		// Vytvorenie 30 inštancií asteroidov „do rezervy“:
		for (int i = 0; i < 30; ++i)
			asteroidy.pridaj(new Asteroid());

		// Aktivácia prvých troch asteroidov:
		for (int i = 0; i < 3; ++i)
			asteroidy.daj(i).aktivuj();

		// Aktivácia rakety:
		raketa.aktivuj();

		// Aktivácia hlavného robota a zapnutie automatického prekresľovania:
		aktivuj();
		svet.kresli();
	}

	// Metóda vystreľ najprv skontroluje, či má raketa dištanc, ak nie,
	// nájde neaktívnu inštanciu strely a aktivuje ju (za sprievodu zvuku
	// a s nastavením rovnakej polohy a uhla ako má raketa) – skrátene
	// povedané: raketa vystrelí…
	private void vystreľ()
	{
		if (raketa.máDištanc()) return;

		for (Strela strela : strely)
		{
			if (strela.neaktívny())
			{
				svet.zvuk("shot.wav");
				strela.skočNa(raketa);
				strela.otoč(raketa);
				strela.aktivuj();
				break;
			}
		}
	}

	// Metóda prijíma do parametra „a“ inštanciu asteroidu. Zistí, či má
	// asteroid dostatočnú veľkosť. Ak áno, upraví jeho veľkosť na polovicu
	// (spolu s tým sú upravené aj niektoré ďalšie vlastnosti), nájde
	// najbližší neaktívny asteroid a vyrobí z neho dvojičku. Ak asteroid
	// nemá dostatočnú veľkosť, je deaktivovaný.
	private void rozbi(Asteroid a)
	{
		if (a.veľkosť() >= 5)
		{
			a.upravVeľkosť(a.veľkosť() / 2);

			for (Asteroid asteroid : asteroidy)
			{
				if (asteroid.neaktívny())
				{
					asteroid.aktivujDvojičku(a);
					break;
				}
			}
		}
		else a.deaktivuj();
	}

	/**
	 * Prekrytá metóda aktivita kontroluje kolízie asteroidov, striel
	 * a rakety.
	 */
	@Override public void aktivita()
	{
		// Najskôr sa skontrolujú kolízie všetkých aktívnych striel
		// s aktívnymi asteroidmi. Keď je zistená kolízia je prehraný zvuk,
		// deaktivovaná strela a zavolaná metóda „rozbi“ (definovaná vyššie)
		// s daným asteroidom v argumente.
		for (Strela strela : strely)
		{
			if (strela.aktívny())
			{
				for (Asteroid asteroid : asteroidy)
				{
					if (asteroid.aktívny())
					{
						if (strela.koliduje(asteroid))
						{
							svet.zvuk("boom.wav");
							strela.deaktivuj();
							rozbi(asteroid);
							break;
						}
					}
				}
			}
		}

		// V prípade, že raketa nemá dištanc, sú skontrolované aj jej
		// prípadné kolízie s asteroidmi. Keď nastane kolízia, raketa
		// „havaruje“ (je zavolaná metóda rakety „havaruj“, ktorá je v tomto
		// projekte šetrná – iba raketu presunie do stredu a nastaví jej
		// určitý dištanc). Zároveň sa asteroid rozbije.
		if (!raketa.máDištanc())
		{
			for (Asteroid asteroid : asteroidy)
			{
				if (asteroid.aktívny())
				{
					if (raketa.koliduje(asteroid))
					{
						svet.zvuk("explode.wav");
						raketa.havaruj();
						rozbi(asteroid);
						break;
					}
				}
			}
		}
	}

	/**
	 * Metóda {@code main} slúži na spúšťanie aplikácie.
	 * @param  args  parametre prečítané z príkazového riadka
	 */
	public static void main(String[] args)
	{
		new HlavnáTrieda();
	}
}