jQuery – to łatwe #6: Piszemy prostą grę!

Bardzo ciekawym eksperymentem, jeśli chodzi o JavaScript jest stworzenie jakiejkolwiek gry w tym języku. Pomaga to przede wszystkim nauczyć się podejścia koncepcyjnego do tworzenia tego typu kodu. Gdy wszystko oprzemy o jQuery, może być to niezwykle ekscytujące doświadczenie, jeśli chodzi o poznawanie tego frameworka. Do dzieła!

gra w js

Spróbujmy zrobić grę, polegającą na tym, że w danym obszarze poruszać będą się kolorowe boksy/skrzynki/cokolwiek, które należy usunąć, klikając w dany element. Za każdy udany klik i zniszczenie kolorowego prostokąta otrzymujemy jeden punkt. Zadanie jest proste – ustrzelić jak najwięcej boksów w danym czasie. Mimo że gra nie jest zbyt odkrywcza i na pewno znudzi się po kilku minutach, stanowi dobrą podstawę do dalszego tworzenia tego typu aplikacji.

Całość będzie toczyć się w obszarze #area, który będzie zwykłym divem. O takim:

gra_area.jpg

<div id="area"></div>

Po wystartowaniu gry będziemy dodawać do tego diva kolejne boksy, które trzeba będzie trafić kursorem myszki. Wtedy kod HTML będzie wyglądać mniej wiecej tak:

<div id="area">
	<div id="losowe_id"></div>
	<div id="losowe_id"></div>
	<div id="losowe_id"></div>
</div>

Obok obszaru, na którym toczyć będzie się ta pasjonująca rozgrywka, widniał będzie nasz aktualny wynik i licznik czasu, który pozostał do końca:

<div id="score">

	<h3>Wynik:</h3><h4 id="result">0</h4>

	<div><h3>Pozostało:</h3><h4><span id="time">30</span> sekund</h4></div>
</div>

Powstały szablon HTML gry prezentuje się następująco:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
 <head>
  <title>Gra</title>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  <script type="text/javascript" src="jquery.js"></script>

  <script type="text/javascript">

	//MIEJSCE NA NASZ KOD

  </script>
  <style type="text/css">
	* { margin: 0; padding: 0; }
	#wrapper
	{
		margin-left: 200px;
		width: 650px;
	}
	#area
	{
		width: 500px;
		height: 400px;
		background: #F2F2F2;
		border: 1px solid #6B8180;
		float: left;
	}
	#area div { position: absolute; }
	#score
	{
		width: 100px;
		font-size: 15px;
		font-family: Trebuchet MS;
		float: left;
		margin-left: 20px;
	}
	h3, h4 { display: block; }
  </style>
 </head>

 <body>
 <div id="wrapper">
	<div id="area"></div>
	<div id="score">

		<h3>Wynik:</h3><h4 id="result">0</h4>

		<h3>Pozostało:</h3><h4><span id="time">30</span> sekund</h4>
	</div>

 </div>
 </body>
</html>

Dobrze, mamy już HTML i jQuery na pokładzie. Przemyślmy teraz logikę działania gry.

Włączamy grę, wpisując adres do przeglądarki. Pora na działanie skryptu:

Do napisania gry zastosujemy styl Camel Case, który, stosowany przede wszystkim w języku JAVA, jest bardzo przejrzysty. Oto zmienne, które nam posłużą w budowie gry:

Poniżej z kolei funkcje, które musimy zaimplementować:

Jeśli mamy listę potrzebnych zmiennych i funkcji, wstawmy je w odpowiednie miejsce, by z czasem uzupełniać to i owo:

	var boxMaxWidth = boxMaxHeight = 40;
	var boxMinWidth = boxMinHeight = 30;

	var boxBackgrounds = ["green", "red", "blue", "black", "pink"];

	var boxContainer = [];

	var boxStartingCount = 5;
	var boxAdditionalMaxCount = 3;

	var boxMovement;
	var boxMovementHorizontal = boxMovementVertical = 10;

	var areaPosition = {};
	var areaSize = {};

	var gameTimer;
	var gameOver = false;

	function createBoxes(i)
	{
	}
	function setBoxRandomSize(obj)
	{
	}
	function setBoxRandomPosition(obj)
	{
	}
	function setBoxRandomBackground(obj)
	{
	}
	function executeTimer()
	{
	}
	function moveBoxes()
	{
	}
	$().ready(function()
	{
	});

Warto omówić sobie, kilka zastosowanych tutaj zabiegów. Po pierwsze:

var boxMaxWidth = boxMaxHeight = 40;

Jest to wielokrotne przypisanie wartości, używane zamiast czegoś takiego:

var boxMaxWidth = 40; var boxMaxHeight = 40;

Natomiast coś takiego:

var areaSize = {};

oznacza, że areaSize jest pustym obiektem. Z kolei

var boxContainer = [];

tworzy ze zmiennej boxContainer zwykłą tablicę.

Podsumowując, po włączeniu gry będziemy mieć do dyspozycji 5 boksów, których szerokość i wysokość mieścić będą się w przedziale od 30 do 40 pikseli. Dostępnych jest też 5 kolorów: „green”, „red”, „blue”, „black”, „pink”. Po strąceniu klocka skrypt będzie generował od 1 do 3 dodatkowych elementów. Co 200 milisekund każda „skrzynka” poruszać będzie się o 10 pikseli w poziomie i pionie. Pora teraz na to, co tygryski lubią najbardziej – kodowanie.

Ustaliliśmy, że po załadowaniu drzewa DOM, przeglądarka powinna pobrać dane na temat wymiarów i pozycji #area, uruchomić dodawanie boksów oraz wprawić nowe elementy w ruch, by na końcu długiej listy czynności uruchomić licznik. Zajmijmy się więc tym wszystkim w pierwszej kolejności.

$().ready(function()
{
	areaPosition = $("#area").position();
	areaSize.w = $("#area").width();
	areaSize.h = $("#area").height();

	createBoxes(boxStartingCount);

	boxMovement = window.setInterval("moveBoxes()", 200);
	gameTimer = window.setInterval("executeTimer()", 1000);

});

Pierwsze 3 linijki to czyste jQuery – funkcje frameworka – position, width i height pozwalają nam pobrać stosowne informacje o #area. Warto zwrócić uwagę na dwie rzeczy – position() zwraca obiekt o dwóch właściwościach: left i top. Dlatego też areaPosition będzie przechowywała te dwie wartości, a odwoływać się będziemy do nich tak:

areaPosition.left; areaPosition.top;

Podobnie jest z areaSize – jest to obiekt, któremu przypisujemy dwie, nowe właściwości – h i w, oznaczające wysokość i szerokość #area.

Potem odpalamy createBoxes z argumentem boxStartingCount, która doda 5 nowych elementów (bo właśnie tyle przypisaliśmy zmiennej boxStartingCount.

Na końcu uruchamiamy odpowiednie funkcje, o których już mówiliśmy, z pewnym, cyklicznym opóźnieniem. Wszystko dzięki setInterval, o którym pisałem choćby tutaj.

Zajmijmy się teraz pierwszą z brzegu – createBoxes.

Musimy stworzyć tyle boksów, ile podano w argumencie, tak więc użyjemy do tego pętli for, korzystającej z argumentu funkcji:

function createBoxes(i)
{

	for (var x = 0;x < parseInt(i); x++)
	{

Następnie tworzymy nowego diva:

function createBoxes(i)
{
	for (var x = 0;x < parseInt(i); x++)
	{
		var div = $("<div></div>");

Pora na małą przerwę w naszych, nie wątpie, zdecydowanych działaniach. Po stworzeniu diva przychodzi czas na wygenerowanie jego właściwości. Omówmy więc, na czym polega uzyskiwanie losowych wartości w JavaScript.

Za całe zamieszanie odpowiedzialna jest funkcja random(), należąca do obiektu MathMath.random(). Losuje ona liczbę od 0 do 1 (przedział lewostronnie domknięty <0;1)) w postaci ułamkowej, tak więc najczęstsze jej wyniki to 0,24325325325, 0,355554343 i tak dalej. Sama w sobie, funkcja random nie daje nam oczekiwanych rezultatów. Co zrobić, jeśli chcemy wylosować np. liczbę od 0 do 100? Odpowiedź jest prosta, musimy pomnożyć wynik funkcji random przez maksymalny wynik, jaki nas interesuje plus jeden.

var int = Math.random()*101;

W ten sposób osiągniemy liczby z zakresu od 0 do 100, w formie 22,232323 itd. Jeśli chcemy tylko i wyłącznie liczby całkowite, należy uzyć funkcji Math.floor. Zamienia ona dowolne liczby na całkowite właśnie, zaokrąglając do dołu.

var int = Math.floor(Math.random()*101);

Pytanie, co zrobić, jeśli chcemy liczbę z zakresu np. 25 do 50. Jest to dość skomplikowane, jednak po chwili zastanowienia stwierdzamy, że do całości należy dodać minimum, a wylosowaną liczbę pomnożyć przez powiększoną o jeden różnicę między maksimum a minimum.

var int = 25+Math.floor(Math.random()*26);

Dla fanów matematyki zrozumienie powyższego przykładu powinno być przyjemnością.

Bogatsi o podstawy teoretyczne, powróćmy do naszego przykładu. Musimy wylosować wysokość i szerokość boksu:

function createBoxes(i)
{
	for (var x = 0;x < parseInt(i); x++)
	{
		var div = $("<div></div>");

		var sizes = setBoxRandomSize(div);

Uzupełnijmy więc funkcję setBoxRandomSize:

function setBoxRandomSize(obj)
{
	var height = Math.floor((boxMaxHeight-boxMinHeight+1)*Math.random())+boxMinHeight;
	var width = Math.floor((boxMaxWidth-boxMinWidth+1)*Math.random())+boxMinWidth;

	obj.css({"height": height, "width": width});

	return {"height": height, "width": width};
}

Sprawa jest prosta – losujemy wartości z zakresu 30-40 i ustawiamy, dzięki jQuery, odpowiednie wartości. Tym razem robimy to kompleksowo, zamiast

obj.css("height", height).css("width", width);

piszemy

obj.css({"height": height, "width": width});

Jest to notacja obiektowa, o której szerzej w następnych artykułach na tym blogu. Sens działania tego zapisu można zrozumieć jednak bez większych problemów. Jako argument funkcji css podajemy wartości wg poniższego szablonu:

{nazwa_stylu: wartość, nazwa_stylu: wartosc, ..., nazwa_stylu: wartosc}

Pora na wylosowanie losowej pozycji elementu:

function createBoxes(i)
{
	for (var x = 0;x < parseInt(i); x++)
	{
		var div = $("<div></div>");

		var sizes = setBoxRandomSize(div);
		setBoxRandomPosition(div);

I uzupełnienie funkcji setBoxRandomPosition:

function setBoxRandomPosition(obj)
{
	var left = Math.floor((areaSize.w-parseInt(obj.css("width")))*Math.random())+areaPosition.left;
	var top = Math.floor((areaSize.h-parseInt(obj.css("height")))*Math.random())+areaPosition.top;

	obj.css({"left": left +"px", "top": top +"px"});
}

Następne losujemy tło:

function createBoxes(i)
{
	for (var x = 0;x < parseInt(i); x++)
	{
		var div = $("<div></div>");

		var sizes = setBoxRandomSize(div);
		setBoxRandomPosition(div);
		setBoxRandomBackground(div);

W ten sposób:

function setBoxRandomBackground(obj)
{
	var background = Math.floor(Math.random()*boxBackgrounds.length);
	obj.css("background-color", boxBackgrounds[background]);
}

Do zmiennej background losujemy liczbę z zakresu od 0 do *liczba elementów tablicy boxBackgrounds* (wyrażona poprzez boxBackgrounds.length). W ten sposób mamy losowy klucz tablicy boxBackgrounds, zawierający któryś, z kolorów (boxBackgrounds[background]).

I tak, mamy uzupełnione 3 funkcje, generujące losowe dane. Musimy teraz dokończyć dzieło tworzenia funkcji createBoxes. Zacznijmy od wygenerowania losowego id:

function createBoxes(i)
{
	for (var x = 0;x < parseInt(i); x++)
	{
		var div = $("<div></div>");

		var sizes = setBoxRandomSize(div);
		setBoxRandomPosition(div);
		setBoxRandomBackground(div);

		var date = new Date();
		var id = new String(""+sizes.height+""+date.getTime()+"");

		div.attr("id", id);

Do zmiennej date tworzymy obiekt typu Date, który jest w JS wbudowany, podobnie jak Image, Math i inne. Obiekt ten posiada funkcję getTime, która zwraca aktualny czas w formie UNIXowej. W ten sposób mamy unikalne id, które przyda nam się, by łatwo odwołać sie do konretnego boksu w tablicy zawierającej je wszystkie (boxContainer). O takiej:

function createBoxes(i)
{

	for (var x = 0;x < parseInt(i); x++)
	{
		var div = $("<div></div>");

		var sizes = setBoxRandomSize(div);
		setBoxRandomPosition(div);
		setBoxRandomBackground(div);

		var date = new Date();
		var id = new String(""+sizes.height+""+date.getTime()+"");

		div.attr("id", id);

		boxContainer[id] = [];

W ten sposób mamy nowy element tablicy boxContainer o losowym kluczu w postaci identyfikatora elementu np. boxContainer[1223424325432] itd. Z tego elementu tablicy robimy kolejną tablicę (= []), by umożliwić korzystanie z czegoś podobnego, na wzór tablicy wielowymiarowej:

boxContainer[id]['cos']

Paść może pytanie, po co nam taka tablica, skoro wszystkie divy będą należeć do #area, co za tym idzie można je łatwo pobrać podobnym wyrażeniem:

$("#area div");

Sprawa rozbija się o… szybkość działania skryptu. Iteracja po tablicach jest znacznie szybsza niż po DOM. Jeszcze większą szybkość moglibyśmy uzyskać, stosując klucze liczbowe – czyli zamiast „cos” np. 2. Dla przejrzystości zachowamy jednak klucze będące stringiem. Za zastosowaniem tablic przemawia również łatwe dodawanie (i szybsze) właściwości danego boksu, a więc w tym wypadku jego kierunku poruszania.

Jeśli mowa o kierunku poruszania się, wylosujmy kierunek w pionie i poziomie. Zrobimy to losując liczbę od 1 do 2. Defaultowo boks poruszać będzie się w prawo i w dół. Jeśli za pierwszym razem wypadnie jedynka, element będzie poruszał się w lewo, jeśli to samo stanie się za drugim razem, zmieni się również kierunek w pionie.

function createBoxes(i)
{

	for (var x = 0;x < parseInt(i); x++)
	{
		var div = $("<div></div>");

		var sizes = setBoxRandomSize(div);
		setBoxRandomPosition(div);
		setBoxRandomBackground(div);

		var date = new Date();
		var id = new String(""+sizes.height+""+date.getTime()+"");

		div.attr("id", id);

		boxContainer[id] = [];

		var arrow = Math.floor(2*Math.random())+1;
		boxContainer[id]['x'] = 'right';

		if (arrow == 1)
		{
			boxContainer[id]['x'] = 'left';
		}

		var arrow = Math.floor(2*Math.random())+1;
		boxContainer[id]['y'] = 'bottom';

		if (arrow == 1)
		{
			boxContainer[id]['y'] = 'top';
		}

W ten sposób, jeśli stworzymy element o id np. 222 (hipotetycznie), to tablica boxContainer wyglądać będzie tak:

boxContainer = [222:
	[
		y: top || bottom; (kierunek poruszania się w pionie)
		x: left || right; (kierunek poruszania się w poziomie)
	]
]

Wartości x i y zależą od wylosowanych liczb.

Zapiszmy jeszcze do tablicy z danym wygenerowanym elementem jego szerokość i wysokość. Po co pobierać ją za każdym razem, korzystając z DOM ($(element).css("height")), skoro można ten proces przyspieszyć:

function createBoxes(i)
{

	for (var x = 0;x < parseInt(i); x++)
	{
		var div = $("<div></div>");

		var sizes = setBoxRandomSize(div);
		setBoxRandomPosition(div);
		setBoxRandomBackground(div);

		var date = new Date();
		var id = new String(""+sizes.height+""+date.getTime()+"");

		div.attr("id", id);

		boxContainer[id] = [];

		var arrow = Math.floor(2*Math.random())+1;
		boxContainer[id]['x'] = 'right';

		if (arrow == 1)
		{
			boxContainer[id]['x'] = 'left';
		}

		var arrow = Math.floor(2*Math.random())+1;
		boxContainer[id]['y'] = 'bottom';

		if (arrow == 1)
		{
			boxContainer[id]['y'] = 'top';
		}

		boxContainer[id]['w'] = sizes.width;
		boxContainer[id]['h'] = sizes.height;

Wyróżnione części kodu powinny pokazać, skąd wzięły się wartości sizes.

Mamy teraz tablicę asocjacyjną, której głównym kluczem jest identyfikator elementu. Potem do wyboru mamy 4 wartości - 2 kierunki poruszania się oraz szerokość i wysokość boksu.

Pora dodać teraz zdarzenie onclick, oznaczające usunięcie elementu z #area oraz dodanie punktu.

function createBoxes(i)
{

	for (var x = 0;x < parseInt(i); x++)
	{
		var div = $("<div></div>");

		var sizes = setBoxRandomSize(div);
		setBoxRandomPosition(div);
		setBoxRandomBackground(div);

		var date = new Date();
		var id = new String(""+sizes.height+""+date.getTime()+"");

		div.attr("id", id);

		boxContainer[id] = [];

		var arrow = Math.floor(2*Math.random())+1;
		boxContainer[id]['x'] = 'right';

		if (arrow == 1)
		{
			boxContainer[id]['x'] = 'left';
		}

		var arrow = Math.floor(2*Math.random())+1;
		boxContainer[id]['y'] = 'bottom';

		if (arrow == 1)
		{
			boxContainer[id]['y'] = 'top';
		}

		boxContainer[id]['w'] = sizes.width;
		boxContainer[id]['h'] = sizes.height;

		div.click(function(e)
		{
			if (!gameOver)
			{
				$(this).hide("slow", function()
				{
					$(this).remove();
					$("#result").text(parseInt($("#result").text())+1);
				createBoxes(Math.floor(boxAdditionalMaxCount*Math.random())+1);
				});
			}
		});

Po dodaniu zdarzenia, sprawdzamy w warunku if, czy gra działa, czy nie. Jeśli zmienna gameOver będzie miała wartość false, będzie to oznaczało, że rozgrywka jest w toku i należy przeprowadzić usunięcie elementu. Ten z kolei usuwamy, kiedy ukryjemy dany boks (funkcja hide i drugi argument, w postaci funkcji wykonywanej po ukończeniu chowania elementu). Całość zostaje usunięta z drzewa DOM dzięki funkcji remove. Potem dodajemy do wyniku jeden, pobierając go wcześniej i zamieniając, dla upewnienia się, na liczbę (parseInt()). Ostatnia czynność to dodanie do #area od 1 do 3 nowych boksów (znowu urządzamy małe losowanie, korzystając z wiadomości poznanych wyżej).

Ostatnią rzeczą, którą musimy się zająć, jest oczywiście dodanie powstałego diva do kontenera, w którym będzie się poruszał:

function createBoxes(i)
{

	for (var x = 0;x < parseInt(i); x++)
	{
		var div = $("<div></div>");

		var sizes = setBoxRandomSize(div);
		setBoxRandomPosition(div);
		setBoxRandomBackground(div);

		var date = new Date();
		var id = new String(""+sizes.height+""+date.getTime()+"");

		div.attr("id", id);

		boxContainer[id] = [];

		var arrow = Math.floor(2*Math.random())+1;
		boxContainer[id]['x'] = 'right';

		if (arrow == 1)
		{
			boxContainer[id]['x'] = 'left';
		}

		var arrow = Math.floor(2*Math.random())+1;
		boxContainer[id]['y'] = 'bottom';

		if (arrow == 1)
		{
			boxContainer[id]['y'] = 'top';
		}

		boxContainer[id]['w'] = sizes.width;
		boxContainer[id]['h'] = sizes.height;

		div.click(function(e)
		{
			if (!gameOver)
			{
				$(this).hide("slow", function()
				{
					$(this).remove();
					$("#result").text(parseInt($("#result").text())+1);
				createBoxes(Math.floor(boxAdditionalMaxCount*Math.random())+1);
				});
			}
		});

		$("#area").append(div);

Voila.

Pora wprawić w ruch powstałe boksy i uruchomić licznik. Zacznijmy od tego drugiego:

function executeTimer()
{
	var time = parseInt($("#time").text());

	if (time-1 > -1)
	{
		$("#time").text(time-1);
		return false;
	}

	window.clearInterval(gameTimer);
	window.clearInterval(boxMovement);
	gameOver = true;

}

W odpowiedzialnej za to funkcji executeTimer, którą, przypomnijmy, odpalamy cyklicznie, dzieje się wiele istotnych, ale równie łatwych rzeczy. Po pierwsze, pobieramy stąd:

<h3>Pozostało:</h3><h4><span id="time">30</span> sekund</h4>

ile zostało nam czasu na zabawę:

var time = parseInt($("#time").text());

Znów używamy parseInt, by uniknąć jakichkolwiek niespodzianek.

Potem sprawdzamy, czy odejmując jeden od pobranej liczby sekund nie uzyskamy czasem liczby ujemnej, co byłoby przecież nielogiczne (jak może nam zostać do końca rozgrywki -1 sekund?). Jeśli więc wszystko pójdzie w porządku, rzeczywiście odejmujemy jeden od ogólnej liczby sekund i aktualizujemy licznik, dzięki funkcji jQuery text. return false; w tym wypadku kończy działanie funkcji, która pominie pozostały kod.

Ten z kolei zostanie uruchomiony, kiedy przekroczymy czas gry, a więc do końca zostanie nam te -1 sekund. Wtedy to usuwamy możliwość cyklicznego wykonywania ustawionych wcześniej funkcji oraz zmieniamy wartość zmiennej gameOver na true. Oznacza to po prostu koniec gry (wówczas zdarzenie onclick, które dodaliśmy wcześniej dla elementów nie uruchomi się, ze względu na wartość zmiennej gameOver).

Pozostało nam już tylko uruchomić poruszanie się boksów. Musimy dotrzeć do każdego z nich w tablicy boxContainer i sprawdzić jego położenie. Na jego podstawie ustalimy kierunek ruchu.

Dostać się do każdego elementu tablicy pomoże nam znów pętla for:

function moveBoxes()
{
	for (k in boxContainer)
	{

Użyta w ten sposób działa podobnie do PHPowej funkcji foreach - po prostu iteruje po każdym elemencie tablicy boxContainer, której kluczem jest zmienna k. Dostajemy wtedy każdy element tablicy w postaci:

boxContainer[k];

O ile dobrze przypominasz sobie dodawanie elementów do tej tablicy, zapewne wiesz, że pod k znajdują się po prostu identyfikatory divów. Wykorzystajmy to, by pobrać ich aktualną pozycje z drzewa DOM:

function moveBoxes()
{
	for (k in boxContainer)
	{
		var top = parseInt($("#"+k).css("top"));
		var left = parseInt($("#"+k).css("left"));

Teraz, w zależności od kierunku poruszania się danego elementu, ustalimy zaktualizowane wartości stylów left i top dla diva:

function moveBoxes()
{
	for (k in boxContainer)
	{
		var top = parseInt($("#"+k).css("top"));
		var left = parseInt($("#"+k).css("left"));

		if (boxContainer[k]['y'] == 'top')
		{
			var top = top-boxMovementVertical;
		}
		else
		{
			var top = top+boxMovementVertical;
		}
		if (boxContainer[k]['x'] == 'left')
		{
			var left = left-boxMovementHorizontal;
		}
		else
		{
			var left = left+boxMovementHorizontal;
		}

Jak widać, w zależności od kierunku, dodajemy bądź odejmujemy od tych wartości 10 pikseli (wartości zmiennych boxMovementHorizontal i boxMovementVertical). Biorąc pod uwagę, że funkcja moveBoxes jest uruchamiana co 200milisekund, da to nam złudzenie ruchu wszystkich boksów.

Oczywiście musimy sprawdzić, czy wartości left i top nie są za małe bądź za duże. Wtedy to boks wyjdzie poza obszar #area. Sprawdzamy więc, kiedy dany klocek dotknie granic dozwolonego obszaru. Jeśli poruszający się w prawo natknie się na prawą krawędź, wtedy jego dalszy ruch w prawo nie ma sensu - zmieniamy mu kierunek na lewy. To samo ma się, jeśli chodzi o ruch pionowy.

function moveBoxes()
{
	for (k in boxContainer)
	{
		var top = parseInt($("#"+k).css("top"));
		var left = parseInt($("#"+k).css("left"));

		if (boxContainer[k]['y'] == 'top')
		{
			var top = top-boxMovementVertical;
		}
		else
		{
			var top = top+boxMovementVertical;
		}
		if (boxContainer[k]['x'] == 'left')
		{
			var left = left-boxMovementHorizontal;
		}
		else
		{
			var left = left+boxMovementHorizontal;
		}

		if (left <= areaPosition.left)
		{
			boxContainer[k]['x'] = 'right';
			var left = areaPosition.left;
		}
		if (left+boxContainer[k]['w'] >= areaPosition.left+areaSize.w)
		{
			boxContainer[k]['x'] = 'left';
			var left = areaPosition.left+areaSize.w-boxContainer[k]['w'];
		}

		if (top < areaPosition.top)
		{
			boxContainer[k]['y'] = 'bottom';
			var top = 0;
		}
		if (top+boxContainer[k]['h'] >= areaSize.h+areaPosition.top)
		{
			boxContainer[k]['y'] = 'top';
			var top = areaPosition.top+areaSize.h-boxContainer[k]['h'];
		}

Pozostaje nam teraz zaktualizować dane wartości stylów każdego boksu:

function moveBoxes()
{
	for (k in boxContainer)
	{
		var top = parseInt($("#"+k).css("top"));
		var left = parseInt($("#"+k).css("left"));

		if (boxContainer[k]['y'] == 'top')
		{
			var top = top-boxMovementVertical;
		}
		else
		{
			var top = top+boxMovementVertical;
		}
		if (boxContainer[k]['x'] == 'left')
		{
			var left = left-boxMovementHorizontal;
		}
		else
		{
			var left = left+boxMovementHorizontal;
		}

		if (left <= areaPosition.left)
		{
			boxContainer[k]['x'] = 'right';
			var left = areaPosition.left;
		}
		if (left+boxContainer[k]['w'] >= areaPosition.left+areaSize.w)
		{
			boxContainer[k]['x'] = 'left';
			var left = areaPosition.left+areaSize.w-boxContainer[k]['w'];
		}

		if (top < areaPosition.top)
		{
			boxContainer[k]['y'] = 'bottom';
			var top = 0;
		}
		if (top+boxContainer[k]['h'] >= areaSize.h+areaPosition.top)
		{
			boxContainer[k]['y'] = 'top';
			var top = areaPosition.top+areaSize.h-boxContainer[k]['h'];
		}

		$("#"+k).css({"left": left, "top": top});

W ten sposób otrzymaliśmy dość prostą grę. Można ją uzupełnić o inne efekty, o których tutaj nie wspomniałem. Można również pewne rzeczy bardziej zoptymalizować. Pierwsze należy z pewnością do Was. Z chęcią zobaczyłbym efekty przeróbek tego skryptu. Będą śmiałkowie?

Co do drugiego wątku, optymalizacja w grach, w JS przede wszystkim, to klucz do sukcesu. Wkrótce postaram się zaprezentować zgrabny artykuł o innych technikach optymalizacji JS, o których tutaj nie wspomniałem.

Komentarze

1

Wyższa szkoła jazdy. Dzięki ci O Wielki!

zx
2

Pierwsze zdanie artykułu o optymalizacji powinno brzmieć: „Nie używaj jQuery”.

blue
3

Dwie wtopy — raz, to podawanie pierwszego argumentu setInterval jako stringa (hallo, dlaczego eval jest zły?); dwa – nie wiem skąd żeś sobie tą „plus jedynkę” wymyślił przy Math.random(). Dalej czytać mi się nie chciało.

Maciej Łebkowski
4

blue: masz racje, pisalem o tym zreszta sam… W ramach kursu jednak postanowilem dolaczyc to jQuery, co mi szkodzi, przynajmniej z eventami mniej roboty.

M. Lebkowski: Jesli nie masz nic ciekawego do powiedzenia, to nie pisz tutaj, prosze. W obydwu przypadkach zachecam do zajrzenia w manual, albo chociaż pomyślenia przez chwile.

EDIT, chociaz z eval podejrzewam o co Ci chodzi. Bedzie o tym przy okazji optymalizacji.

5

Świetne ! Może gierka sama w sobie jest nudna ale rzeczywiście można się bardzo dużo nauczyć. Brawo !

szalony_ivan
6

@ferrante: Maciej Łebkowski ma racje…
window.setInterval(func, delay [, param1, param2, ...]);
window.setInterval(code, delay);

Druga opcja jest evil! Zalecam uważne czytanie manuala ;-) bo czy nie lepiej przekazać referencje ?

shfx
7

Spojrz na moj edit ;-)

8

Jestem w szoku, od kilku miesięcy czytam Twojego bloga i naprawdę, nie mogę wyjść z podziwu :)

Artykuł rewelacja, w weekand będę czytał z większą uwagą :) do całości brakuje jeszcze js-hotkeys :)

pzdr, patS

pats
9

oh ;P sorry

shfx
10

http://www.wykop.pl/link/67634/jak-napisac-prosta-gre-w-javascript

Jakby co to mozna wykopac artykul ;-) Pozdro.

11

Dlaczego piszecie, żeby nie używać JQuery, bo źle to wpływa na optymalizację? Macie na myśli SEO? Czy coś innego? Tak w dwóch zdaniach poproszę :-)

tomek
12

SEO a optymalizacja to dwa rozne swiaty. Poczytaj moj artykul o frameworkach, wyjasni Ci sie co nieco :-)

Pozdrawiam

13

wielkie dzięki za ten kurs – wiele można z niego wyciagnąć ale przede wszystkim można zacząć pracę z jquery bez zbędnych rozczarowań :)
pozdrawiam i czekam na kolejne części kursu!

hugy
14

Witam.

Mam takie drobne pytanie. Gra nie daje sobie rady, kiedy ustawię margines górny wrappera na np. 200px. Po pewnym czasie prostokąty zaczynają mi fruwać przy krawędzi górnej. Dlaczego tak się dzieję?

15

Jeśli się nie mylę to powinno załatwić problem:

if (top < areaPosition.top)
{
boxContainer[k]['y'] = ‘bottom’;
top = areaPosition.top;//było var top = 0;
}

dib
16

Fajny, ale nie wiem gdzie co wklejac :/ Mógłby ktos przesłac mi na np. maila gotowy skrypt?? witek122-1994@o2.pl

Witek
17

Świetny kurs :-) Przydało by się go jeszcze uzupełnić o nowe funkcje jQuery 1.3

maro
18

Co wy tutaj wogóle o optymalizacji prawicie jak pierwsze co mi sie rzuciło w oczy przy pobierznym przegladaniu strony to:


for (var x = 0;x < parseInt(i); x++)
{
var div = $("");
....

Pierdylion razy w pętli tworzony będzie obiekt ‘div’ ktory jest zawsze taki sam!… aż się prosi by go wyrzucić przed rozpoczęcie pętli… eh…
dalej nawet nie czytam… żegnam!:P

bender
19

A teraz Panie pieniaczu przeczytaj ostatni akapit.

Swoja droga, nie mam czasu by skonczyc ten artykul, a szkoda. Moze kiedys…

20

nie da sie wlaczyc wpisalem wszytko kro po kroku i nic…

Piter
21

O wielki dzięki Ci – dowiedziałem się że można użyć czegoś takiego:
$x = $y = 666;
zamiast
$x = 666; $y = 666;
damn żyłem w niewiedzy..

lol
22

jak dla mnie Cool ;-)

oooo
23

ja też nie wiem jak wykonać może ktoś podesłać mi : simman@wp.pl a i jeszcze jedno próbuje samemu zrobić Tmierka do mojego zadanka

$(document).ready(function(){

var gameTimer;

function executeTimer(){

$(".zdjecie1").delay(600).fadeIn(1);
}
gameTimer = window.setInterval("executeTimer()", 1000);

});

Jak coś korzystam z googlowego:

rafał
24

$(document).ready(function(){

var gameTimer;

function executeTimer(){

var time = parseInt(30);

if (time-1 > -1)
{
$(„#linki”).text(time-1);
return false;
}
$(„.zdjecie1″).delay(600).fadeIn(1);
}
gameTimer = window.setInterval(„executeTimer()”, 1000);
});

rafał
25

Dla tych co nie wiedzą co gdzie wklejać polecam wyświetlić źródło strony z działającą grą :)

Kubcon
26

if (top < areaPosition.top)
{
boxContainer[k]['y'] = 'bottom';
var top = 0;
}

proponuję zamienić na

if (top < areaPosition.top)
{
boxContainer[k]['y'] = 'bottom';
var top = areaPosition.top;
}

Kuba
27

Ogólnie czaje wszystko, gra mi sie bardzo podoba tylko mam jedno pytanie dla czego funkcja „setBoxRandomSize(obj)” musi miec na końcu

„return {„height”: height, „width”: width};”

John
28

Niezły programik i niezły opis. Prawie wszystko byłem w stanie prześledzić :)
Nie rozumiem tylko czemu nie zdecydowałeś się na „płynniejszą” wersję. Np. zmieniając argument setInterval dla boxMovement na 40, a boxMovementHorizontal = boxMovementVertical = 2 gra się o wiele łatwiej i przyjemniej, no i po prostu fajniej wygląda jako rezultat do którego śledzący ten kurs zmierza. Większa motywacja :)

29

gdzie mam to pisać????????????/

Dodaj komentarz

Dozwolone tagi: <blockquote>, <code>, <strong>