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

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

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

Dátum: 5. 8. 2020, pred štyrmi rokmi

Zadanie

Naprogramujte jednoduchú hru Delostrelec, ktorá bude mať nasledujúce pravidlá:

  • V pravej časti hracej plochy bude stacionárne umiestnené delo, ktoré môže hráč otáčať (a tým ním mieriť na cieľ) a tiež z neho strieľať (bez obmedzení).
  • V ľavej časti hracej plochy sa bude náhodným spôsobom pohybovať cieľ reprezentovaný obručou, ktorú sa bude hráč usilovať zasiahnuť.
  • Zvyšné detaily hry domyslite podľa potreby (ad­‑hoc).

Riešenie

 

Toto je hlavná trieda. Vzhľadom na jednoduchosť hry je krátka a jednoduchá. Reaguje na pohyb myši a stláčanie medzerníka. (Rovnako dobre by fungovalo klikanie myšou, ale medzerník sa nám počas testovania osvedčil viac.)

Reakciou na stlačenie medzerníka je výstrel delovej gule a reakciou na pohyb kurzorom je myši nad plátnom je zmena orientácie dela. Počet výstrelov sa ráta. Keď guľa trafí obruč, zaráta sa hráčovi úspešný zásah. Pomer úspešných zásahov (voči všetkým výstrelom) je prepočítaný percentuálne a zobrazený hráčovi.

~

import knižnica.GRobot;
import knižnica.Kláves;
import knižnica.Svet;
import knižnica.ÚdajeUdalostí;
import knižnica.Zoznam;

public class Delostrelec extends GRobot
{
	private final Obruč obruč = new Obruč();
	private final Delo delo = new Delo();

	private final Zoznam<Guľa> gule = new Zoznam<Guľa>();

	private int výstrelov = 0;
	private int zásahov = 0;

	private Delostrelec()
	{
		delo.skoč(280, 0);
		delo.uhol(180);
		skoč(0, 280);
		vlastnýTvar(r ->
		{
			if (0 == výstrelov)
				text("-");
			else
				text((zásahov * 100 / výstrelov) + " %");
		});
	}

	private void vystreľ()
	{
		Guľa strela = null;

		for (Guľa guľa : gule)
		{
			if (guľa.neaktívny())
			{
				strela = guľa;
				break;
			}
		}

		if (null == strela)
		{
			strela = new Guľa();
			gule.pridaj(strela);
		}

		strela.poloha(delo);
		strela.smer(delo);
		strela.rýchlosť(25);
		++výstrelov;
	}

	@Override public void tik()
	{
		for (Guľa guľa : gule)
		{
			if (guľa.aktívny() &&
				guľa.bodVElipse(obruč, 35, 25))
			{
				Svet.pípni();
				guľa.deaktivuj();
				++zásahov;
			}
		}
	}

	@Override public void pohybMyši()
	{
		delo.otočNaMyš();
	}

	@Override public void stlačenieKlávesu()
	{
		switch (ÚdajeUdalostí.kláves())
		{
			case Kláves.MEDZERA:
				vystreľ();
				break;
		}
	}

	public static void main(String[] args)
	{
		Svet.použiKonfiguráciu("Delostrelec.cfg");
		Svet.nekresli();
		new Delostrelec();
		if (Svet.prvéSpustenie()) Svet.zbaľ();
		Svet.kresli();
	}
}
  Delo »

Jedinou úlohou tejto triedy je algoritmické vykreslenie tvaru dela, prevod kresby na oblasť a vykresľovanie tejto oblasti ako tvaru dela. Ostatné činnosti s delom vykonáva hlavná trieda (Delostrelec).

~

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

public class Delo extends GRobot
{
	private static Oblasť oblasťDela = null;
	private final static KreslenieTvaru tvar = r -> r.vyplňOblasť(oblasťDela);

	public Delo()
	{
		veľkosť(120);
		mierka(1);
		predvolenýTvar(false);

		if (null == oblasťDela)
		{
			nekresliTvary();
			zdvihniPero();

			oblasťDela = new Oblasť();

			vpravo(180);
			dozadu(20);
			oblasťDela.pridaj(elipsu(0.20));

			dozadu(105);
			oblasťDela.odober(obdĺžnik(
				(veľkosť() / 5),
				(veľkosť() / 8)));

			dopredu(25);
			oblasťDela.pridaj(elipsu(
				(veľkosť() / 5),
				(veľkosť() / 10)));

			dopredu(200);
			oblasťDela.pridaj(kružnicu(
				veľkosť() / 10));

			kresliTvary();
			položPero();
		}

		domov();
		vlastnýTvar(tvar);
		farba(tmavošedá);
		vrstva(1);
	}
}
« Delostrelec Guľa »

Táto trieda, okrem kreslenia tvaru gule, overuje kolízie gule s okrajmi obrazovky, od ktorých sa odráža podľa zákona odrazu (rozhodnutie o tomto správaní sme vykonali ad­‑hoc), skrýva guľu po deaktivácii a pri aktivácii zabezpečuje (okrem zobrazenia gule) nastavenie konkrétneho maximálneho času aktivity.

~

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

public class Guľa extends GRobot
{
	private final static KreslenieTvaru tvar = r -> r.kruh();

	public Guľa()
	{
		veľkosť(12);
		mierka(1);
		vlastnýTvar(tvar);
		zdvihniPero();
	}

	@Override public void aktivita()
	{
		if (polohaX() < Svet.najmenšieX() ||
			polohaX() > Svet.najväčšieX())
		{
			if (smer() <= 180)
				smer(180 - smer());
			else
				smer(360 - (smer() - 180));
			dopredu(rýchlosť());
		}

		if (polohaY() < Svet.najmenšieY() ||
			polohaY() > Svet.najväčšieY())
		{
			smer(360 - smer());
			dopredu(rýchlosť());
		}
	}

	@Override public void deaktivácia()
	{
		skry();
	}

	@Override public void aktivácia()
	{
		spusti(50);
		zobraz();
	}
}
« Delo Obruč »

Fungovanie náhodného pohybu obruče je zabezpečené tromi metódami, z ktorých dve sú reakciami na udalosti, konkrétne na udalosť dosiahnutia cieľa obručou (resp. robotom, od ktorého je táto trieda odvodená) a udalosť aktivity, ktorá je automaticky periodicky vykonávaná počas aktívneho stavu obruče.

V reakcii na dosiahnutie cieľa sa obruč automaticky aktivuje (resp. „reaktivuje,“ pretože táto reakcia je podstate odozvou na deaktiváciu robota po dosiahnutí cieľa). V reakcii aktivity sa kontroluje to, či obruč má, alebo nemá aktívny cieľ a v prípade, že nemá sa zvolí nový cieľ.

Voľba cieľa je treťou dotknutou metódou. Obruč si volí cieľ s náhodnou vertikálnou polohou. Presúvanie do cieľa je úplne automatické (robotické). Funguje vďaka nastaveniu nenulového zrýchlenia v konštruktore. Všetky ostatné prvky tejto triedy sa starajú o kreslenie obruče (a iné vizuálne vlastnosti).

~

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

public class Obruč extends GRobot
{
	private static Oblasť oblasťObruče = null;
	private final static KreslenieTvaru tvar = r -> r.vyplňOblasť(oblasťObruče);

	public Obruč()
	{
		veľkosť(40);
		mierka(1);
		farba(červená);

		if (null == oblasťObruče)
		{
			nekresliTvary();
			oblasťObruče = new Oblasť();
			oblasťObruče.pridaj(elipsa(0.5));
			oblasťObruče.odober(elipsa(
				(veľkosť() / 2) - 5,
				veľkosť() - 5));
			kresliTvary();
		}

		vlastnýTvar(tvar);
		skoč(-370, 0);
		zrýchlenie(1);
		zdvihniPero();
	}

	public void zvoľCieľ()
	{
		cieľ(polohaX(), Svet.náhodnéReálneČíslo(
			Svet.najmenšieY() + 50, Svet.najväčšieY() - 50));
	}
	
	@Override public void aktivita()
	{
		if (!cieľAktívny()) zvoľCieľ();
	}

	@Override public void dosiahnutieCieľa()
	{
		aktivuj();
	}
}
« Guľa  

Výsledok

obrázok 
Ukážka priebehu hry.

 

Návrhy na zlepšenia

  • Skúste prepracovať hru tak, aby sa medzi úspešné zásahy rátal len zásah dovnútra obruče, pričom zásah presne do okrajov obruče by spôsobil nejakú aktivitu, napríklad roztočenie obruče na určitý čas (počas ktorého by bola nezasiahnuteľná).