Návrh

Předně, tahle stránka je obyčejná webová stránka napsaná co nejstručněji v jazyce HTML. (Až tak stručně, že porušuje některé dobré zásady.) Její zdrojový kód si v prohlížeči můžete ukázat zkratkou Ctrl+U anebo ⌘+⌥+U na Macu. Úsek mezi <script> a </script> je samotný kód programu. Mimo něj je ještě podstatná značka <canvas>, která označuje ve stránce oblast ke kreslení.

Hru Gorily jsme navrhli nejdřív hodně stručně a postupně jsme tu představu zpřesňovali. První návrh zněl takhle:

  1. Ukáže se náhodný terén,
  2. z levé strany se namaluje dráha letícího banánu.
Gorilu, která ten banán hází, ani jejího protivníka zatím kreslit nebudeme. Zrovna tak zatím nejde hozenému banánu určit směr a ve hře chybí vítr.

Po několika úvahách jsme postup zpřesnili takhle:

  1. Terén:
    1. se nejdřív spočítá jako spousta sloupečků náhodné výšky a potom
    2. se vyhladí tak, že každý sloupeček zprůměrujeme s jeho sousedy.
  2. Hozený banán:
    1. za letu opíše tvar paraboly (x2) a tu můžeme vykreslovat kousek po kousku počítáním pro různá x,
    2. zastavíme ve chvíli, kdy výška paraboly v nějakém x je pod výškou terénu v tom místě.

Program

Terén

Zbývá tenhle plán vysvětlit počítači. Trochu hrozivé se může zdát, že šířka terénu je něco přes tisíc, a sloupečky musíme vyrobit jeden po druhém. Zařídí to cyklus na dva řádky, který máte v kódu hned na začátku:

for (var x=0; x<1024; x+=1) {
	teren[x] = Math.random() * 768;
}
Proměnná x postupně projde všechny hodnoty od 0 do 1023, což taky znamená, že po každém kroku se má zvýšit o jedna. Terénu na každé takové pozici x nastavíme náhodnou hodnotu od 0 do 768.

V bodu 1b. chceme každý sloupeček zprůměrovat s jeho sousedy. O to se postará podobný cyklus, ale pro jednoduchost vynecháme první a poslední sloupec, protože ty mají jenom jednoho souseda:

for (var x=1; x<1023; x+=1) {
	teren[x] = (teren[x+1] + teren[x-1] + teren[x]) / 3;
}
V kódu ale vidíte ještě jeden cyklus do 250. Proč to? Můžete si zkusit, že jedno takové průměrování nestačí. Když ho zopakujeme 250-krát, už jsou kopečky docela hezky hladké. Máme tedy jeden cyklus v druhém, a dáváme si pozor, aby každý z nich měl jinou proměnnou (kromě x také j):
for (var j=0; j<250; j+=1) {
	for (var x=1; x<1023; x+=1) {
		teren[x] = (teren[x+1] + teren[x-1] + teren[x]) / 3;
	}
}

Terén jsou zatím jenom čísla, která jsme si uložili někam do paměti počítače. Aby byl vidět, musíme prohlížeči vysvětlit, že ho chceme namalovat jako spoustu černých sloupečků různé výšky. O to se postará tenhle kód:

var ctx = obraz.getContext("2d");
for (var x=0; x<1024; x+=1) {
	ctx.moveTo(x+0.5, 768);
	ctx.lineTo(x+0.5, teren[x]);
}
ctx.strokeStyle = "#000";
ctx.stroke();
Hodnota ctx.strokeStyle určuje barvu čáry a namísto hodnot RGB, jak jsou tam napsané teď, bychom mohli psát taky prostě "black". Čáru chceme malovat veprostřed každého sloupečku, proto k hodnotě x přičteme jednu polovinu. Je to 0.5, protože se v angličtině píše desetinná tečka místo české čárky.

Hozený banán

Banán hodíme z levého okraje obrazovky, takže tam bude začínat nová čára, a tentokrát červená (mohli bychom napsat "red"):

ctx.beginPath();
ctx.strokeStyle = "#f00";
ctx.moveTo(0, teren[0]);

Nadmořská výška banánu v nějakém místě x je určená parabolou, a dohodli jsme se, že nechceme moc probírat její detaily. Při programování hry jsem si z prstu vycucal vzoreček: y = (x/400 - 1)2. Snad jen abych předvedl, že můžu, jsem začal funkcí nadruhou(x) = x2:

function nadruhou(x) {
	return x * x;
}
...a tu jsem hned použil pro funkci parabola(x), která nám udává nadmořskou výšku:
function parabola(x) {
	return 300 * nadruhou(x / 400 - 1);
}
Jak jste už asi věděli, násobeni se píše počítači hvězdičkou a dělení lomítkem.

K výsledku téhle funkce budeme ještě něco přičítat, protože ji bude potřeba posunout nahoru nebo dolů. Chceme totiž, aby se u levého okraje obrazovky dotýkala terénu, takže má platit parabola(0) + něco = teren[0]. Jednoduchou úpravou rovnice získáme, že něco = teren[0] - parabola(0), a necháme si teda spočítat to přesné číslo:

var konstanta = teren[0] - parabola(0);

Zbývá takhle spočítanou čáru nakreslit. Zároveň se ale budeme ujišťovat, že kreslíme nad terénem – kdyby ne, domalujeme čáru přesně do země a skončíme. Tady přichází malá past, protože souřadnice v obrázku jsou obrácené než v matematice a y = 0 je na horním okraji, takže pokud y > terén, máme skončit. Celé to vypadá takhle:

for (var x=1; x<1024; x+=1) {
	var y = parabola(x) + konstanta;
	if (y > teren[x]) {
		ctx.lineTo(x, teren[x]);
		alert("zásah: " + x);
		break;
	}
	ctx.lineTo(x, y);
}
Podmínka je napsaná v závorce za slovem if a stejně, jako to bylo u cyklů, je její kód uzavřený ve složených závorkách. Nakonec už zbývá jenom čáru namalovat:
ctx.stroke();

K zamyšlení a vyzkoušení

Stránku si stáhněte k sobě na disk (Soubor ⇒ Uložit jako...), abyste ji mohli upravovat. Z disku ji pak můžete normálně otevřít v prohlížeči, buďto obyčejně poklikáním, anebo ten soubor do prohlížeče přetáhněte. Když chcete upravit kód programu, otevřete ten soubor zároveň v nějakém textovém editoru – poznámkový blok prozatím stačí – a po každé úpravě soubor uložte a v prohlížeči nechte načíst znovu (klávesová zkratka je obvykle F5).
  1. Jak terén bud vypadat, když místo 250 opakování budete průměrovat třeba sto tisíckrát, anebo jednou nebo dokonce vůbec ne?
  2. Jak bude vypadat parabola, když místo násobení třemi sty budeme násobit třeba 600-krát? A co když jí změníme ty hodnoty 400 a 1?
  3. Jakou má terén barvu, když při kreslení sloupečků nepřičteme polovinu (0.5)? Tušíte, čím by to mohlo být? (Když jsem program ukazoval na hodině, tohle tam bylo špatně.)
  4. Často se stává, že banán narazí hned v bodě 1, protože na levém okraji je moc strmý svah. Co se s tím dá dělat?