Logika mapek w grze cRPG Django/Python

Prace nad "Wyspą Mrozu" posuwają się do przodu. Obecnie implementuję poszczególne elementy mapy, po której porusza się postać. System zakłada wykorzystanie mini-map podzielonych na kwadratowe klocki (tile) we współrzędnych x,y...

Oto przykład mapy z naniesioną siatką:
icetiled
Mapa opisana jest za pomocą kilku prostych JSONów zawierających współrzędne obszarów po których można się poruszać (z podziałem na strefy), obszary akcji (wywoływana jest akcja o podanym ID), pola generowania mięsa armatniego, czy też pola z lokacją już istniejących postaci (gracza, jak i wrogów, NPC):
class BigTile(models.Model):
	"""
	Model minimap złożonych z tilów
	"""
	name = models.CharField(max_length=255, verbose_name='Nazwa')
	img = models.ImageField(upload_to='ice/bigtiles/', verbose_name='Grafika')
	size = models.IntegerField(default=100, verbose_name='Rozmiar Klocka')
	movable_tiles = models.TextField(verbose_name=u'Obszary Ruchu')
	entry_tiles = models.TextField(verbose_name=u'Obszary Wejścia/Wyjścia')
	spawn_tiles = models.TextField(blank=True, verbose_name=u'Obszary Spawnowania')
	action_tiles = models.TextField(blank=True, verbose_name=u'Obszary Zdarzeń')
	char_locations =  models.TextField(blank=True, verbose_name=u'Lokacje NPC')
	class Meta:
		verbose_name = u'BigTile'
		verbose_name_plural = u'8. BigTile'
		db_table = 'ice_bigtile'
	def __str__(self):
		return self.name
	def __unicode__(self):
		return self.name

class BigTileCopy(models.Model):
	"""
	Model kopii minimap dla danego usera/questa
	"""
	mapa = models.ForeignKey(BigTile, verbose_name=u'Mapa rodzic')
	movable_tiles = models.TextField(verbose_name=u'Obszary Ruchu')
	entry_tiles = models.TextField(verbose_name=u'Obszary Wejścia/Wyjścia')
	spawn_tiles = models.TextField(blank=True, verbose_name=u'Obszary Spawnowania')
	action_tiles = models.TextField(blank=True, verbose_name=u'Obszary Zdarzeń')
	char_locations =  models.TextField(blank=True, verbose_name=u'Lokacje postaci i przeciwników')
	class Meta:
		verbose_name = u'BigTile Kopia'
		verbose_name_plural = u'9a. BigTile Kopie'
		db_table = 'ice_bigtilecopy'
	def __str__(self):
		return str(self.mapa)
	def __unicode__(self):
		return unicode(self.mapa)


class QuestCopy(models.Model):
	"""
	Model kopii questu dla danego usera
	"""
	quest = models.ForeignKey(Quest, verbose_name=u'Quest')
	char = models.ForeignKey(Character, verbose_name=u'Postać')
	maps = models.ManyToManyField(BigTileCopy, verbose_name='Mapy/Stan dla usera', related_name='maps')
	current_map = models.ForeignKey(BigTileCopy, verbose_name=u'Obecna mapa', related_name='current_map')
	class Meta:
		verbose_name = u'Quest'
		verbose_name_plural = u'9b. Questy Kopie'
		db_table = 'ice_questcopy'
	def __str__(self):
		return str(self.quest)
	def __unicode__(self):
		return unicode(self.quest)
Wcześniej zaprezentowana mapa ma tylko jeden obszar ruchu:
{"1": ["3,0", "4,0", "2,1", "3,1", "4,1", "2,2", "3,2", "4,2", "5,2", "2,3", "3,3", "4,3", "5,3", "6,3", "2,4", "3,4", "4,4", "5,4", "2,5", "3,5", "4,5", "5,5"]}

Przykładowo mając pośrodku mapy zamknięte drzwi - po jednej stronie byłby jeden obszar ruchu, a po drugiej stronie - drugi. Postać może poruszać się tylko w obrębie własnego obszaru ruchu. Po otworzeniu drzwi pola z drugiego obszaru zostałyby dołączone do pierwszego i postać mogłaby przejść dalej (Modele-kopie umożliwiają tu bezproblemową edycję JSONów dla danego questu/gracza).

Podobnie to wygląda dla innych pól, ale o tym później. Poruszanie zrobione jest w dość prosty sposób. Mapa wyświetlana jest jako tło diva. Natomiast postacie i inne elementy orientowane są w przestrzeni DIVa za pomocą absolutnej pozycji. Lokacja 3,0 odpowiada left/top 3*rozmiar tila,0*rozmiar tila. Wokół postaci (sąsiadujące tile) rysowane są przeźroczyste grafiki z zielonym obramowaniem oznaczające pola na jakie postać może przejść (uwzględniając czy współrzędne są w obszarze ruchu). Kliknięcie w takie pole wysyła żądanie AJAXem do Django w celu zwalidowania ruchu i zmiany pozycji postaci. Zwracane są nowe grafiki i koordynaty dla awatara postaci i umieszczane w tym DIVie:

<div style="background-image:url(/site_media/ice/bigtiles/room_.png); background-repeat:no-repeat;width:576px;height:576px;position:relative;" id="map">

<img src="/site_media/ice/good.png" class="good" alt="2,3" style="position:absolute;left:144px;top:216px;" />
<img src="/site_media/ice/good.png" class="good" alt="3,3" style="position:absolute;left:216px;top:216px;" />
<img src="/site_media/ice/good.png" class="good" alt="3,4" style="position:absolute;left:216px;top:288px;" />
<img src="/site_media/ice/good.png" class="good" alt="3,5" style="position:absolute;left:216px;top:360px;" />
<img src="/site_media/ice/good.png" class="good" alt="2,5" style="position:absolute;left:144px;top:360px;" />
<img src="/site_media/ice/action.png" title="Skrzynia" alt="1" style="position:absolute;left:72px;top:288px;" class="action" />
<img src="/site_media/ice/token4.png" title="Czesiek" alt="Czesiek" style="position:absolute;left:149px;top:296px;" />
<img src="/site_media/ice/token3.png" title="Ubogi rozbójnik" alt="Ubogi rozbójnik" style="position:absolute;left:365px;top:368px;" />
</div>
W przeglądarce wygląda to tak:
icemap
Obsługa JavaScriptowa jest dość prosta i realizowana za pomocą jQuery:
$(document).ready(function(){
	// Update character position
	$(".good").live("click", function(){
		$.ajax({
			url: "/ice/qmap_update/{{ qc.id }}/?move_on="+this.alt,
			cache: false,
			success: function(html){
				$("#map").html(html);
			}
		});
	});
	// Show action tile content
	$(".action").live("click", function(){
		$.ajax({
			url: "/ice/qmap_update/{{ qc.id }}/?action_on="+this.alt,
			cache: false,
			success: function(html){
				// tutaj ladnie to trzeba będzie wyświetlić...
// 				$("#map").html(html);
				alert(html);
			}
		});
	});

});
Demo pojawi się pewnie w weekend/po weekendzie na crpg.rk.edu.pl/ice/, a kod pewnie też ujży światło dzienne w tych okolicach jak go trochę ogarnę coby używał i18n.
RkBlog

Tworzenie gier w Pythonie, 15 May 2009, Piotr Maliński

Comment article
RkBlog main page Search RSS Contact