luq techblog

o tworzeniu słów kilka…

Canvas & Sprites 2 lipca 2010

Filed under: GameDev,GameMap,JS,Web — Łukasz @ 19:19
Tags: , ,

Mapa

Tak jakoś wyszło, że postanowiłem napisać własny system poruszania się po mapie w widoku od góry. Mam teraz trochę czasu więc co mi tam… a skrobnę se… a co?! Całość ma działać tak jak dużo gier na GameBoy`a tzn. mamy bohatera który pozostaje zawsze na środku ekranu, oraz ma ograniczony widok ma mapę (widzi tylko to co jest najbliżej niego). Idąc przesuwa się mapa tj. widzi to czego wcześniej nie widział…

 

Zresztą chyba każdy wie o co chodzi :)

 

Całość będzie oparte o canvas (HTML5), JS + jQuery + Ajax + JSON, MySQL i PHP.

 

Grafika została pobrane z jakiegoś ogromnego obrazka-mapy i nie należy do mnie, jest użyta jedynie w celu pokazowym. Jeśli jesteś autorem i nie podoba Ci się to, że prezentuję kod wraz z Twoją grafiką, proszę o kontakt via email

 

W tym wpisie chcę opisać jak generować mapę.

 

 
 
 

Mapa – przemyślenia

a)
Najprymitywniejszym sposobem generowania mapy jest stworzenie sobie kafelków w każdy z oddzielnym pliku i wczytywanie ustawianie ich jako tło w siatce div`ów.

1.png
1

2.png
2

<style type="text/css">
#map div.row { width: 40px; height: 40px; float: left; }
#map div.clear { clear: both; }
</style>

(...)

<div id="map">
    <div class="row">
        <div style="background: url(1.png) no-repeat;"></div>
        <div style="background: url(2.png) no-repeat;"></div>
        <div style="background: url(1.png) no-repeat;"></div>
        <div style="background: url(1.png) no-repeat;"></div>
        (...)
    </div>
    <div class="clear"></div>
    (...)
</div>

Rozwiązanie niedobre z kilku powodów. Po pierwsze, obrazki w osobnych plikach to więcej wysłanych zapytań do serwera (w celu ich uzyskania) co daje większy czas wczytywania strony i większe obciążenie serwera. Po drugie, siatka div`ów nie umożliwia nam operowaniu na poszczególnych pikselach (co nam oferuje canvas), jest to niefajne i trudno się w to integruje. Może to było dobre rozwiązanie jakieś 2 lata temu, kiedy nikt o canvas nie słyszał (a może i nawet dawniej?), ale dziś należy skorzystać z canvas`a, w końcu po to go wymyślono. W końcu ułatwi nam to pracę :)

b)

Już o wiele lepszym sposobem byłaby taka sama siatka div`ów ale z wykorzystaniem CSS Sprites.

map.png
CSS Sprites map

<style type="text/css">
#map div.row { width: 40px; height: 40px; float: left; }
#map div.clear { clear: both; }
</style>

(...)

<div id="map">
    <div class="row">
        <div style="background: url(map.png) 0px 0px no-repeat;"></div>
        <div style="background: url(map.png) -40px 0px no-repeat;"></div>
        <div style="background: url(map.png) 0px 0px no-repeat;"></div>
        <div style="background: url(map.png) 0px 0px no-repeat;"></div>
        (...)
    </div>
    <div class="clear"></div>
    (...)
</div>

W tym wypadku pobieramy tylko jeden obrazek i wyświetlamy go częściami. A więc jest to jedynie jedno zapytanie do serwera. Zresztą taki wielki szum wokół tego całego CSS Sprites się zrobił, wszyscy reklamują jaki to jest szczyt technologi, takie fajne, zajebiste, a rozwiązanie te w świecie programowania deskoptowego (głównie gier, no w sumie tylko tutaj się z tym spotkałem) jest znane od bardzo dawna. Oczywiście żeby mnie ktoś źle nie zrozumiał, fajnie, że takie coś przeniesiono do web`a, ale czy to jest aż takie wielkie cudo, żeby każdy webdeveloper musiał na swoim blogu opisywać i rozwodzić się nad tą, no… nie odkrywczą „techniką”?

c)

Stosujemy element canvas + kafelki w oddzielnych plikach.

d)

Stosujemy canvas + 1 plik z kafelkami pod mapę. To właśnie to rozwiązanie jest najlepsze (przynajmniej w tym przypadku) i zostanie tutaj zaprezentowane.

Do roboty

No to pierwszy na odstrzał idzie index.html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
  <head>
    <title>Game by `luq`</title>
    <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
    <link rel="stylesheet" type="text/css" href="./style/style.css" />
	
	<script type="text/javascript">
		var CORE_SIZE_FIELD = 40; // wielkosc kafleki
	</script>
	
	<script type="text/javascript" src="./js/jquery.js"></script>
	<script type="text/javascript" src="./js/Map.class.js"></script>
	<script type="text/javascript" src="./js/start.js"></script>
  </head>
<body>
	<div id="weather"></div>
	<canvas id="game_map" width="400" height="400">
		Upss.. canvas nie działa na tej przeglądarce.
	</canvas>
</body>
</html>

Tutaj za dużo nie ma do tłumaczenia. Chyba z oczywistych powodów została stworzona zmienna CORE_SIZE_FIELD, natomiast div[id=weather] na razie pominę w opisie, o nim będzie na końcu.

style/style.css

body { margin: 0; padding: 0; }

#weather { width: 400px; height: 400px; position: absolute; top: 2px; left: 2px; display: none; }
#game_map { border: 2px solid #777; }

No i teraz to o co głównie mi chodziło w tym wpisie, Map.class.js ale po kawałku:

function Map( mapId ){
	this.mapId = mapId;										// Map id
	this.canvasMap = document.getElementById( this.mapId ); // DOM canvas object
	this.ctx; 												// Canvas context
	this.img;												// Map image (all sprites of map)
	this.sizeFild = CORE_SIZE_FIELD;						// Size of field
	
	if( this.canvasMap.getContext ){
		this.ctx = this.canvasMap.getContext( '2d' );
		
		if( this.ctx ){
			this.img = new Image();
			this.img.src = './gfx/my_map.png';
		}
	}
}

Konstruktor nie robi nic ciekawego. Podajemy mu id elementu canvas, przypisuje do właściwości niezbędne nam dane, tworzy kontekst canvas`a i tworzy obiekt obrazka z wszystkimi naszymi spritami, zresztą oto on:

gfx/my_map.png (pozostawiłem „nieco” miejsca na kolejne sprity)
My sprites map

Teraz czas na metody:

Map.prototype._drawField = function( xCanvas, yCanvas, xImage, yImage ){
	this.ctx.drawImage( 
		this.img, 
		xImage * this.sizeFild, 
		yImage * this.sizeFild, 
		this.sizeFild, 
		this.sizeFild, 
		xCanvas * this.sizeFild, 
		yCanvas * this.sizeFild, 
		this.sizeFild, 
		this.sizeFild
	);
}

Za pomocą niej rysujemy poszczególne kafelki na canvas`ie. Nie posługujemy się w niej pikselami a liczbami określającymi wiersz oraz kolumnę, zarówno w canvas`ie jak i naszym zbiorze spritów. Jest to wygodniejsze i prostsze w obsłudze. Co do samej funkcji drawImage() a konkretniej jej argumentów, to całkiem fajnie prezentuje poszczególne z nich, obrazek pod prototypem tejże funkcji w MDC. Ogólnie rzecz biorąc jest to metoda prywatna, właściwie prywatna tylko z tego względu bo ja tam mówię ;) – JS niestety nie ma modyfikatorów dostępu (bo tak to się chyba zwie), mają być dodane w kolejnej wersji ECMAScript`u.

Kolejną metodą jest:

Map.prototype.drawMap = function( JSONmap ){
	for( var i = 0; i < 10; i++ ){
		for( var j = 0; j < 10; j++ ){
			this._drawField( j, i, JSONmap[i][j].x, JSONmap[i][j].y );
		}
	}
}

Po prostu wypełnia całą mapę zgodnie z danymi podanym w 2-wymiarowej tablicy obiektów – czyli jest to JSON. Ostatecznie oczywiście te dane będą odbierane z bazy danych, następnie budowany z nich będzie JSON po stronie PHP a my odbierzemy to przez asynchroniczne zapytanie AJAX`owe. JSON taki będzie musiał wyglądać w ten sposób:

var JSONmap = [
    // 1. wiersz canvasa
    [
        { 'x' : 0, 'y' : 1 }, // 1. kolumna canvasa
		{ 'x' : 1, 'y' : 1 }, // 2. kolumna canvasa
		{ 'x' : 3, 'y' : 0 }, (...)
		{ 'x' : 3, 'y' : 0 },
		{ 'x' : 2, 'y' : 0 },
		{ 'x' : 3, 'y' : 0 },
		{ 'x' : 0, 'y' : 1 },
		{ 'x' : 1, 'y' : 1 },
		{ 'x' : 0, 'y' : 1 },
		{ 'x' : 1, 'y' : 1 }
	],
	// 2. wiersz 
	(...)
]

We właściwościach x i y oczywiście są – odpowiednio – wiersz oraz kolumna spritów, liczone od zera:

Map sprite memo

No i to w sumie wszystko. js/start.js zawiera:

$(document).ready(function(){
    var oMap = new Map( 'game_map' );
    var JSONmap = (...) // nie bedę tego tu wklejał...
    oMap.drawMap( JSONmap );
});

Jeszcze dopisałem mały bajer. Stworzyłem sobie możliwość efektów pogodowych, świetlnych (dzień/noc) na mapie. Po to jest właśnie ten div[id=weather]. W js/Map.class.js mamy jeszcze:

/**
 * Set night mode
 */
Map.prototype.night = function(){
    $('#weather').css( 'background-color', '#000' );
    $('#weather').fadeTo( 2000, 0.3 );
}

/**
 * Set day mode
 */
Map.prototype.day = function(){
    $('#weather').fadeOut( 2000 );
}

natomiast w js/start.js

$('#game_map').click(function(){
    oMap.night();
});
	
$('#weather').click(function(){
    oMap.day();
});

Co umożliwia po kliknięciu zmienić porę dnia (oczywiście ostatecznie nie będzie to tak działać). Zresztą zobacz sam jak to działa, oraz podejrzyj kod jeśli chcesz.

LINK

Reklamy
 

5 Responses to “Canvas & Sprites”

  1. ptaku Says:

    Wszystko fajnie tylko niestety dałeś link do localhosta, bo z checia bym zobaczył live demo

    Pozdrawiam

  2. luq Says:

    O jaaa… Tak to jest jak się ma wersje localhost i net. Naprawione :)

  3. ptaku Says:

    Z tego co wiem canvas obłusguja tylko najnowsze przeglądarki : chrome,safari itp… to jezeli tworzymy gre na canvas to mamy ten problem ze tak naprawde ludzie ktorzy uzywaja IE8,FF 3.0… czyli ponad 50% odbiorców nie bedzie mogło zobaczyć naszego dzieła, czy jest na to jakies dobre rozwiazanie ostatnio spotkałem sie z : ExplorerCanvas (http://code.google.com/p/explorercanvas/) czy sie nie myle ze jezeli uzyjemy go przy budownie np : Gry,Strony przy uzyciu Canvas to wszystko powinno w miare chodzić ?

  4. luq Says:

    Canvas nie jest jedynie obsługiwany przez IE < 9. Fx (czy jak kto woli FF) miał już taką możliwość z rok czy nawet ponad, bo wtedy pierwszy raz bawiłem się tym obiektem. Zresztą zacytuje MDC:

    Mozilla applications gained support for starting with Gecko 1.8 (i.e. Firefox 1.5). The element was originally introduced by Apple for the OS X Dashboard and Safari. Internet Explorer does not natively support , but a page can effectively add support for it by including a script from Google’s Explorer Canvas project. Opera 9 also supports .

    Co do użytkowników IE w naszym kraju to jest ich podobno jakieś 26%, na świecie – tak, jakieś 50%. a nawet 60% . Zresztą to dla mnie bez znaczenia, gra to nie strona. Za dużo pracy i nerwów wymagałoby napisanie jej cross-browser. Zresztą, to normalne wymaganie sprzętowe, tak jak w grach deskoptowych karta graficzna czy RAM. To, że przeglądarka czegoś na zaimplementowała to mnie nie interesuje, nie mam zamiaru tego obchodzić. Mówię oczywiście o grach webowych a nie o stronach.

    Co do „haczenia” canvas`a pod IE, to od początku takie skrypty chodziły po necie, bo jeśli tylko jedna z wiodących przeglądarek czegoś nie obsługuje to albo trzeba to jakoś obejść albo nie można tego zazwyczaj stosować w budowie stron. W taki sposób wszystko będzie chodzić idealnie, zresztą zobacz sam ;)

  5. […] mój poprzedni wątek nt. mapy gry webowej, dziś stworzyłem GUI (Graphical User Interface) pod edytor map, który jest niezbędny do dalszej […]


Skomentuj

Wprowadź swoje dane lub kliknij jedną z tych ikon, aby się zalogować:

Logo WordPress.com

Komentujesz korzystając z konta WordPress.com. Wyloguj / Zmień )

Zdjęcie z Twittera

Komentujesz korzystając z konta Twitter. Wyloguj / Zmień )

Facebook photo

Komentujesz korzystając z konta Facebook. Wyloguj / Zmień )

Google+ photo

Komentujesz korzystając z konta Google+. Wyloguj / Zmień )

Connecting to %s