Advertisement
  1. Code
  2. Coding Fundamentals
  3. Game Development

Maken Isometrische Worlds: Een Primer voor Game Ontwikkelaars

Scroll to top
Read Time: 21 min

() translation by (you can also view the original English article)

In deze tutorial, geef ik je een overzicht van wat je moet weten om te isometrische ervaringswerelden creëren. Je leert wat de isometrische projectie is, en hoe om te isometrische niveaus als 2D arrays te vertegenwoordigen. We zullen formuleren relaties tussen de weergave en de logica, zodat wij kunnen gemakkelijk het manipuleren van objecten op het scherm en handvat dakpan botsingdetectie. We zullen ook kijken diepte sorteren en karakter animatie.

Verwante Posten

Wil je nog meer tips voor het maken van isometrische werelden? Check out de follow-up post, isometrische werelden maken: A Primer voor Gamedevs, vervolg en Juwal het boek, Starling spel ontwikkeling Essentials.


1. De Isometrische Wereld

Isometrische weergave is een display methode gebruikt voor het maken van een illusie van 3D voor een anders 2D game - soms aangeduid als pseudo 3D of 2.5 D. Deze beelden (overgenomen uit Diablo 2 en Age of Empires) illustreren wat ik bedoel:

diablodiablodiablo

Diablo 2
AOEAOEAOE

Age of Empires

Uitvoering van een isometrische weergave kan worden gedaan op vele manieren, maar omwille van de eenvoud zal ik me concentreren op een tegel-gebaseerde benadering, die de meest efficiënte en meest gebruikte methode is. Ik heb elke screenshot hierboven bedekt met een diamant raster tonen hoe het terrein is opgedeeld in tegels.


2. Tegel Gebaseerde Spellen

Elk visueel element is in de tegel-gebaseerde benadering opgesplitst in kleinere stukken, zogenaamde tegels, van een standaard formaat.  Deze tegels worden gerangschikt om te vormen van de game wereld volgens vooraf bepaalde niveau gegevens - meestal een 2D matrix.

Zo laten we eens kijken een topdown standaard 2D weergave met twee tegels - een gras tegel en de tegel van een muur - zoals hier wordt weergegeven:

base 2d tiles

Sommige eenvoudige tegels

Deze tegels zijn elk dezelfde grootte als elke andere, en vierkant, zodat de hoogte van de tegel en de tegel breedte hetzelfde zijn.

Voor een niveau met grasland aan alle kanten omsloten door muren, zal het niveau gegevens 2D matrix als volgt uitzien:

1
[[1,1,1,1,1,1],
2
 [1,0,0,0,0,1],
3
 [1,0,0,0,0,1],
4
 [1,0,0,0,0,1],
5
 [1,0,0,0,0,1],
6
 [1,1,1,1,1,1]]

Hier 0 duidt een gras tegel en 1 duidt op een tegel van de muur. Regelen van de tegels volgens de niveau gegevens zal produceren de onder niveau afbeelding:

2d level simple

Een eenvoudige niveau, weergegeven in een weergave-van bovenaf.

Wij kan dit verbeteren door de toevoegen hoekelementen en aparte verticale en horizontale wandtegels, waarvoor vijf extra tegels:

1
[[3,1,1,1,1,4],
2
 [2,0,0,0,0,2],
3
 [2,0,0,0,0,2],
4
 [2,0,0,0,0,2],
5
 [2,0,0,0,0,2],
6
 [6,1,1,1,1,5]]
2d level complex

Verbeterde niveau met getallen van de tegel

Ik hoop dat het concept van de tegel gebaseerde benadering is nu duidelijk. Dit is een eenvoudig 2D raster implementatie, die we kunnen code als volgt:

1
for (i, loop through rows)
2
 for (j, loop through columns)
3
  x = j * tile width
4
  y = i * tile height
5
  tileType = levelData[i][j]
6
  placetile(tileType, x, y)

Hier veronderstellen wij dat tegel breedte en hoogte van de tegel zijn gelijke (en hetzelfde geldt voor alle tegels), en komen overeen met de tegel beelden afmetingen. Dus, de tegel breedte en hoogte van de tegel voor dit voorbeeld zijn zowel 50px, waardoor de totale niveau grootte van 300x300px - dat wil zeggen zes rijen en zes kolommen van tegels meten van elke 50x50px.

In een normale tegel gebaseerde benadering, voeren we ofwel een top-down weergave of een zijaanzicht; we moeten de isometrische projectie implementeren voor een isometrische weergave.


3. Isometrische Projectie

De beste technische uitleg van wat "isometrische projectie" betekent, voor zover ik weet, is uit dit artikel door Clint Bellanger:

We hoek onze camera langs twee assen (swing de camera 45 graden aan de ene kant, vervolgens 30 graden omlaag). Hiermee maakt je een raster van de ruitvormige (ruit) waar de raster ruimten twee keer zo groot zijn als ze hoog zijn. Deze stijl werd gepopulariseerd door Strategiespellen en actie RPG's. Als we kijken naar een kubus in deze weergave, drie zijden zijn zichtbaar (boven en twee tegenoverliggende zijden).

Hoewel het klinkt een beetje ingewikkeld, is eigenlijk de uitvoering van deze weergave eenvoudig. Wat we nodig hebben om te begrijpen is de relatie tussen 2D en de isometrische ruimte - dat wil zeggen, de relatie tussen het niveau gegevens en de beeld; de transformatie van top-down "Cartesiaanse" coördinaten naar isometrische coördinaten.

the_isometric_gridthe_isometric_gridthe_isometric_grid

Cartesische grid vs. isometrische raster.

(We zijn niet een zeshoekige tegel gebaseerd techniek, dat een andere manier is van de isometrische werelden gezien.)

Isometrische Tegels Plaatsen

Verhuren mij uitproberen voor het vereenvoudigen van de relatie tussen niveau gegevens opgeslagen als een 2D matrix en de isometrische weergave - dat wil zeggen, hoe we transformeren Cartesiaanse coördinaten naar isometrische coördinaten.

We zullen proberen om de isometrische weergave maken voor onze muur-omsloten grasland niveau gegevens:

1
[[1,1,1,1,1,1],
2
 [1,0,0,0,0,1],
3
 [1,0,0,0,0,1],
4
 [1,0,0,0,0,1],
5
 [1,0,0,0,0,1],
6
 [1,1,1,1,1,1]]

In dit scenario kunnen wij een walkable gebied vaststellen door te controleren of het element van de matrix 0 op die coördinaat is, waardoor die aangeeft gras. De uitvoering van de 2D weergave van het bovenstaande niveau was een simpele iteratie met twee lussen, vierkante tegels compensatie elk met de vaste tegel hoogte en breedte van de tegel te plaatsen.

1
for (i, loop through rows)
2
 for (j, loop through columns)
3
  x = j * tile width
4
  y = i * tile height
5
  tileType = levelData[i][j]
6
  placetile(tileType, x, y)

Voor de isometrische weergave, blijft de code alleen hetzelfde, maar de wijzigingen van de functie placeTile().

Voor een isometrisch aanzicht moeten we de bijbehorende isometrische coördinaten binnen de lussen berekenen. 
De vergelijkingen om dit te doen zijn als volgt, waar isoX en isoY isometrische x - en y-coördinaten vormen, en cartX en cartY Cartesiaanse x - en y-coördinaten vertegenwoordigen:

1
//Cartesian to isometric:

2
3
isoX = cartX - cartY;
4
isoY = (cartX + cartY) / 2;
1
//Isometric to Cartesian:

2
3
cartX = (2 * isoY + isoX) / 2;
4
cartY = (2 * isoY - isoX) / 2;

Deze functies laten zien hoe je kunt converteren van het ene systeem naar het andere:

1
function isoTo2D(pt:Point):Point{
2
  var tempPt:Point = new Point(0, 0);
3
  tempPt.x = (2 * pt.y + pt.x) / 2;
4
  tempPt.y = (2 * pt.y - pt.x) / 2;
5
  return(tempPt);
6
}
1
function twoDToIso(pt:Point):Point{
2
  var tempPt:Point = new Point(0,0);
3
  tempPt.x = pt.x - pt.y;
4
  tempPt.y = (pt.x + pt.y) / 2;
5
  return(tempPt);
6
}

De pseudocode voor de lus ziet er dan als volgt:

1
for(i, loop through rows)
2
  for(j, loop through columns)
3
    x = j * tile width
4
    y = i * tile height
5
    tileType = levelData[i][j]
6
    placetile(tileType, twoDToIso(new Point(x, y)))
isolevel screenshotisolevel screenshotisolevel screenshot

Onze muur-omsloten grasland in een isometrische weergave.

Als voorbeeld, laten we eens kijken hoe een typische 2D positie wordt omgezet naar een isometrische positie:

1
2D point = [100, 100];
2
// twoDToIso(2D point) will be calculated as below
3
isoX = 100 - 100; // = 0
4
isoY = (100 + 100) / 2;  // = 100
5
Iso point == [0, 100];

Ook een input van [0, 0] zal resulteren in [0, 0], en [10, 5] zal [5, 7.5].

De bovenstaande methode ons in staat stelt om te maken een directe correlatie tussen de 2D niveau gegevens en de isometrische coördinaten. We kunnen van de tegel coördinaten vinden in de niveau gegevens uit de cartesiaanse coördinaten met behulp van deze functie:

1
function getTileCoordinates(pt:Point, tileHeight:Number):Point{
2
  var tempPt:Point = new Point(0, 0);
3
  tempPt.x = Math.floor(pt.x / tileHeight);
4
  tempPt.y = Math.floor(pt.y / tileHeight);
5
  return(tempPt);
6
}

(Hier, wij in wezen veronderstellen dat tegel hoogte en tegel breedte zijn gelijk, net als in de meeste gevallen.)

Vandaar, van een paar schermcoördinaten (isometrische), kan vinden we tegel coördinaten door te bellen naar:

1
getTileCoordinates(isoTo2D(screen point), tile height);

Dit scherm punt zou kunnen zijn, bijvoorbeeld een muis klik op positie of de positie van een pick-up.

Tip: Een andere methode van plaatsing is de Zigzag model, waarin een andere aanpak helemaal.

Verplaatsen in Isometrische Coördinaten

Beweging is heel eenvoudig: je manipuleren je gegevens van de game wereld in Cartesiaanse coördinaten en gewoon gebruik maken van de bovenstaande functies voor het bijwerken van het op het scherm. Bijvoorbeeld, als je een karakter voorwaarts in de positieve y-richting te verplaatsen wilt, kunt je gewoon de eigenschap y stapsgewijs wijzigen en haar positie vervolgens omzetten in isometrische coördinaten:

1
y = y + speed;
2
placetile(twoDToIso(new Point(x, y)))

Diepte Sorteren

Naast normale plaatsing, zullen we moeten zorgen voor diepte sorteren voor het tekenen van de isometrische wereld. Dit zorgt ervoor dat onderdelen dichter naar de speler objecten verder weg zijn getekend.

De eenvoudigste diepte sorteermethode is gewoon het gebruik van de Cartesische waarde van de y-coördinaat, zoals vermeld in deze Quick Tip: verdere omhoog het scherm het object is, hoe eerder het moet worden getrokken. Dit werkt goed, zolang we hebben niet alle sprites die bezetten meer dan een ruimte van één tegel.

De meest efficiënte manier van diepte sorteren voor isometrische werelden is te breken alle tegels in één-tegel standaardafmetingen en niet toe te staan van grotere afbeeldingen. Bijvoorbeeld, hier is een tegel die niet passen in de standaard blokgrootte - Zie hoe kunnen we opsplitsen in meerdere tegels die elke aan de tegel afmetingen:

split big tilesplit big tilesplit big tile

Een grote afbeelding is opgesplitst in meerdere tegels van isometrische standaardafmetingen

4. Het Maken van de Kunst

Isometrische kunst pixel kunst kan zijn, maar het niet hoeft te worden. Bij de behandeling met isometrische pixelart, vertelt RhysD de gids je bijna alles wat die je moet weten. Enkele theorie kan worden teruggevonden op Wikipedia.

Bij het maken van isometrische art, zijn de algemene regels

  • Beginnen met een lege isometrische raster en voldoen aan de pixel perfecte precisie.
  • Proberen te breken van kunst in één isometrische tegel beelden.
  • Probeer ervoor te zorgen dat elke tegel een van beide is beloopbaar of niet-beloopbaar. Het zou ingewikkeld zijn als wij nodig hebt aan één tegel die zowel walkable en niet-walkable gebieden bevat.
  • De meeste tegels moet naadloos naast elkaar in een of meer richtingen.
  • Schaduwen kunnen lastig uit te voeren, tenzij we gebruik maken van een gelaagde benadering waar we schaduwen op de grond laag tekenen en tekent je de held (of bomen of andere objecten) op de bovenste laag. Als de aanpak die je niet multi-gelaagde, zorg ervoor dat schaduwen vallen aan de voorzijde, zodat ze niet zal op, zeg, de held vallen wanneer hij achter een boom staat.
  • In het geval dat je nodig hebt om een groter is dan de standaard isometrische tegel tegel-afbeelding te gebruiken, probeer te gebruiken van een dimensie die een veelvoud van de grootte van de tegel iso is. Het is beter om een gelaagde benadering in dergelijke gevallen, waar we de kunst in verschillende stukken die zijn gebaseerd op de hoogte opsplitsen kunt. Bijvoorbeeld, een boom kan worden gesplitst in drie stukken: de wortel, de romp en het gebladerte. Dit maakt het gemakkelijker om te sorteren diepten als we stukken in de bijbehorende lagen die overeenkomt met hun hoogten kunnen trekken.

Isometrische tegels die groter dan de één tegel afmetingen zijn zal kwesties maken met diepte sorteren. Enkele van de kwesties worden besproken in deze links:


5. Isometrische Tekens

Uitvoering van tekens in isometrische weergave is niet ingewikkeld als het klinkt. Kunst karakter dient te worden gemaakt volgens bepaalde normen. Eerst moeten we bepalen hoeveel bewegingsrichtingen zijn toegestaan ​​in onze game - meestal bieden games four-way beweging of beweging met acht richtingen

Eight-way navigation directions in top-down and isometric viewsEight-way navigation directions in top-down and isometric viewsEight-way navigation directions in top-down and isometric views

Acht-richtingen navigatie-instructies in top-down en isometrische weergaven.

Voor een weergave van bovenaf, kunnen we maken van een set van teken animaties in één richting, en draait je hen eenvoudig voor alle anderen. Voor isometrische tekens moeten we elke animatie opnieuw renderen in elk van de toegestane richtingen - dus voor beweging met acht richtingen moeten we acht animaties maken voor elke actie. Voor het gemak van begrip duiden we meestal de richtingen als Noord, noordwesten, West, Zuidwest, Zuid, South-East, East, en Noord-Oosten, tegen de klok, in die volgorde.

spriteSheetspriteSheetspriteSheet

Een isometrische teken geconfronteerd in verschillende richtingen.

We plaatst je tekens op dezelfde manier dat we tegels plaatsen. De beweging van een karakter wordt bereikt door berekening van het verkeer in Cartesiaanse coördinaten en vervolgens te converteren naar isometrische coördinaten. Laten we aannemen dat we zijn met behulp van het toetsenbord om te controleren van het teken.

We zullen twee variabelen, dX en dY, op basis van de directionele toetsen instellen. Standaard deze variabelen 0 zal worden, en zal worden bijgewerkt volgens de grafiek hieronder, waar U, D, R en L de omhoog duiden, omlaag, links en rechts pijltoetsen, respectievelijk. Een waarde van 1 onder een sleutel geeft die toets wordt gedrukt; 0 betekent dat de sleutel niet wordt ingedrukt.

1
  Key       Pos
2
U D R L    dX dY
3
================
4
0 0 0 0     0  0
5
1 0 0 0     0  1
6
0 1 0 0     0 -1
7
0 0 1 0     1  0
8
0 0 0 1    -1  0
9
1 0 1 0     1  1
10
1 0 0 1    -1  1
11
0 1 1 0     1 -1
12
0 1 0 1    -1 -1

Nu, met behulp van de waarden van dX en dY, we kunnen updaten het Cartesiaanse coördinaten zo dus:

1
newX = currentX + (dX * speed);
2
newY = currentY + (dY * speed);

Dus dX en dY staan voor de verandering in de x - en y-positie van het teken, gebaseerd op de toetsen die worden ingedrukt.

Zoals we al hebben besproken, kunnen we gemakkelijk de nieuwe isometrische coördinaten, berekenen:

1
Iso = twoDToIso(new Point(newX, newY))

Zodra we de nieuwe isometrische positie hebben, moeten we dat doen verhuizen teken naar deze positie. Op basis van de waarden die wij voor dX en dY hebben, kunnen we beslissen welke richting het teken wordt geconfronteerd met en gebruiken van het bijbehorende teken art.

Botsing Detectie

Botsingdetectie wordt gedaan door te controleren of de tile op de berekende nieuwe positie een niet-walkable tegel is. Dus, zodra we de nieuwe positie vinden, wij onmiddellijk de teken er niet verplaatsen, maar eerst te controleren om te zien welke tegel die ruimte inneemt.

1
tile coordinate = getTileCoordinates(isoTo2D(iso point), tile height);
2
if (isWalkable(tile coordinate)) {
3
  moveCharacter();
4
} else {
5
  //do nothing;
6
}

In de functie isWalkable() controleren we of de waarde van de array niveau gegevens op bepaalde coördinaat een walkable tegel of niet is. We moeten oppassen voor het bijwerken van de richting waarin het teken wordt geconfronteerd - zelfs als hij niet beweegt, zoals in het geval van hem raken van een niet-walkable tegel.

Diepte Sorteren Met Tekens

Overwegen een karakter en een boom tegel in de isometrische wereld.

Voor goed begrip diepte sorteren, moeten we begrijpen dat wanneer het karakter van de x - en y-coördinaten minder dan die van de boom zijn, de boom het teken overlapt. Wanneer het karakter van de x - en y-coördinaten groter dan die van de boom zijn, overlapt het teken de boom.

Als ze de dezelfde x-coördinaat, hebben dan wij beslissen op basis van de y-coördinaat alleen: welke is de hogere y-coördinaat de andere overlapt. Als ze hetzelfde y-coördinaat hebben dan wij beslissen op basis van de x-coördinaat alleen: welke is de hogere x-coördinaat overlapt anderzijds.

Een vereenvoudigde versie van dit is gewoon sequentieel te trekken de niveaus vanaf de verste tegel - dat wil zeggen, tegel[0][0] - teken vervolgens alle tegels in elke rij één voor één. Als een karakter een tegel neemt, we trekken de tegel van de grond eerst en dan maken de karakter-tegel. Dit werkt prima, omdat het karakter niet kan een tegel van de muur bezetten.

Diepte sorteren moet worden gedaan telkens een tegel wijzigingen positie. Bijvoorbeeld, moeten we om het te doen wanneer tekens verplaatsen. We updaten dan de weergegeven scène, na het uitvoeren van de sorteerbewerking diepte, zodat de wijzigingen van de diepte.


6. Hebben een Go!

Nu, benut je nieuwe kennis door het creëren van een werkend prototype, met opties voor toetsenbordbediening en goede diepte sorteren en botsing detectie. Hier is mijn demo:

Klik hierop om aan te geven van de SWF focus, en vervolgens met de pijltoetsen. Klik hier voor de full-sized versie.

Misschien vindt je deze klasse nut handig (ik heb het geschreven in AS3, maar je moet zitten kundig te begrijpen in elke andere programmeertaal):

1
package com.csharks.juwalbose
2
{
3
  import flash.display.Sprite;
4
	import flash.geom.Point;
5
6
	public class IsoHelper
7
	{
8
		/**

9
		 * convert an isometric point to 2D

10
		 * */
11
		public static function isoTo2D(pt:Point):Point{
12
			//gx=(2*isoy+isox)/2;

13
			//gy=(2*isoy-isox)/2

14
			var tempPt:Point=new Point(0,0);
15
			tempPt.x=(2*pt.y+pt.x)/2;
16
			tempPt.y=(2*pt.y-pt.x)/2;
17
			return(tempPt);
18
		}
19
		/**

20
		 * convert a 2d point to isometric

21
		 * */
22
		public static function twoDToIso(pt:Point):Point{
23
			//gx=(isox-isoxy;

24
			//gy=(isoy+isox)/2

25
			var tempPt:Point=new Point(0,0);
26
			tempPt.x=pt.x-pt.y;
27
			tempPt.y=(pt.x+pt.y)/2;
28
			return(tempPt);
29
		}
30
31
		/**

32
		 * convert a 2d point to specific tile row/column

33
		 * */
34
		public static function getTileCoordinates(pt:Point, tileHeight:Number):Point{
35
			var tempPt:Point=new Point(0,0);
36
			tempPt.x=Math.floor(pt.x/tileHeight);
37
			tempPt.y=Math.floor(pt.y/tileHeight);
38
39
			return(tempPt);
40
		}
41
42
		/**

43
		 * convert specific tile row/column to 2d point

44
		 * */
45
		public static function get2dFromTileCoordinates(pt:Point, tileHeight:Number):Point{
46
			var tempPt:Point=new Point(0,0);
47
			tempPt.x=pt.x*tileHeight;
48
			tempPt.y=pt.y*tileHeight;
49
50
			return(tempPt);
51
		}
52
53
	}
54
}

Als je echt vast zit, is hier de volledige code van mijn demo (in Flash en AS3 tijdlijn code mag wordengegeven):

1
// Uses senocular's KeyObject class

2
// http://www.senocular.com/flash/actionscript/?file=ActionScript_3.0/com/senocular/utils/KeyObject.as

3
4
import flash.display.Sprite;
5
import com.csharks.juwalbose.IsoHelper;
6
import flash.display.MovieClip;
7
import flash.geom.Point;
8
import flash.filters.GlowFilter;
9
import flash.events.Event;
10
import com.senocular.utils.KeyObject;
11
import flash.ui.Keyboard;
12
import flash.display.Bitmap;
13
import flash.display.BitmapData;
14
import flash.geom.Matrix;
15
import flash.geom.Rectangle;
16
17
var levelData=[[1,1,1,1,1,1],
18
[1,0,0,2,0,1],
19
[1,0,1,0,0,1],
20
[1,0,0,0,0,1],
21
[1,0,0,0,0,1],
22
[1,1,1,1,1,1]];
23
24
var tileWidth:uint = 50;
25
var borderOffsetY:uint = 70;
26
var borderOffsetX:uint = 275;
27
28
var facing:String = "south";
29
var currentFacing:String = "south";
30
var hero:MovieClip=new herotile();
31
hero.clip.gotoAndStop(facing);
32
var heroPointer:Sprite;
33
var key:KeyObject = new KeyObject(stage);//Senocular KeyObject Class

34
var heroHalfSize:uint=20;
35
36
//the tiles

37
var grassTile:MovieClip=new TileMc();
38
grassTile.gotoAndStop(1);
39
var wallTile:MovieClip=new TileMc();
40
wallTile.gotoAndStop(2);
41
42
//the canvas

43
var bg:Bitmap = new Bitmap(new BitmapData(650,450));
44
addChild(bg);
45
var rect:Rectangle=bg.bitmapData.rect;
46
47
//to handle depth

48
var overlayContainer:Sprite=new Sprite();
49
addChild(overlayContainer);
50
51
//to handle direction movement

52
var dX:Number = 0;
53
var dY:Number = 0;
54
var idle:Boolean = true;
55
var speed:uint = 5;
56
var heroCartPos:Point=new Point();
57
var heroTile:Point=new Point();
58
59
//add items to start level, add game loop

60
function createLevel()
61
{
62
	var tileType:uint;
63
	for (var i:uint=0; i<levelData.length; i++)
64
	{
65
		for (var j:uint=0; j<levelData[0].length; j++)
66
		{
67
			tileType = levelData[i][j];
68
			placeTile(tileType,i,j);
69
			if (tileType == 2)
70
			{
71
				levelData[i][j] = 0;
72
			}
73
		}
74
	}
75
	overlayContainer.addChild(heroPointer);
76
	overlayContainer.alpha=0.5;
77
	overlayContainer.scaleX=overlayContainer.scaleY=0.5;
78
	overlayContainer.y=290;
79
	overlayContainer.x=10;
80
	depthSort();
81
	addEventListener(Event.ENTER_FRAME,loop);
82
}
83
84
//place the tile based on coordinates

85
function placeTile(id:uint,i:uint,j:uint)
86
{
87
var pos:Point=new Point();
88
	if (id == 2)
89
	{
90
91
		id = 0;
92
		pos.x = j * tileWidth;
93
		pos.y = i * tileWidth;
94
		pos = IsoHelper.twoDToIso(pos);
95
		hero.x = borderOffsetX + pos.x;
96
		hero.y = borderOffsetY + pos.y;
97
		//overlayContainer.addChild(hero);

98
		heroCartPos.x = j * tileWidth;
99
		heroCartPos.y = i * tileWidth;
100
		heroTile.x=j;
101
		heroTile.y=i;
102
		heroPointer=new herodot();
103
		heroPointer.x=heroCartPos.x;
104
		heroPointer.y=heroCartPos.y;
105
106
	}
107
	var tile:MovieClip=new cartTile();
108
	tile.gotoAndStop(id+1);
109
	tile.x = j * tileWidth;
110
	tile.y = i * tileWidth;
111
	overlayContainer.addChild(tile);
112
}
113
114
//the game loop

115
function loop(e:Event)
116
{
117
	if (key.isDown(Keyboard.UP))
118
	{
119
		dY = -1;
120
	}
121
	else if (key.isDown(Keyboard.DOWN))
122
	{
123
		dY = 1;
124
	}
125
	else
126
	{
127
		dY = 0;
128
	}
129
	if (key.isDown(Keyboard.RIGHT))
130
	{
131
		dX = 1;
132
		if (dY == 0)
133
		{
134
			facing = "east";
135
		}
136
		else if (dY==1)
137
		{
138
			facing = "southeast";
139
			dX = dY=0.5;
140
		}
141
		else
142
		{
143
			facing = "northeast";
144
			dX=0.5;
145
			dY=-0.5;
146
		}
147
	}
148
	else if (key.isDown(Keyboard.LEFT))
149
	{
150
		dX = -1;
151
		if (dY == 0)
152
		{
153
			facing = "west";
154
		}
155
		else if (dY==1)
156
		{
157
			facing = "southwest";
158
			dY=0.5;
159
			dX=-0.5;
160
		}
161
		else
162
		{
163
			facing = "northwest";
164
			dX = dY=-0.5;
165
		}
166
	}
167
	else
168
	{
169
		dX = 0;
170
		if (dY == 0)
171
		{
172
			//facing="west";

173
		}
174
		else if (dY==1)
175
		{
176
			facing = "south";
177
		}
178
		else
179
		{
180
			facing = "north";
181
		}
182
	}
183
	if (dY == 0 && dX == 0)
184
	{
185
		hero.clip.gotoAndStop(facing);
186
		idle = true;
187
	}
188
	else if (idle||currentFacing!=facing)
189
	{
190
		idle = false;
191
		currentFacing = facing;
192
		hero.clip.gotoAndPlay(facing);
193
	}
194
	if (! idle && isWalkable())
195
	{
196
		heroCartPos.x +=  speed * dX;
197
		heroCartPos.y +=  speed * dY;
198
		heroPointer.x=heroCartPos.x;
199
		heroPointer.y=heroCartPos.y;
200
201
		var newPos:Point = IsoHelper.twoDToIso(heroCartPos);
202
		//collision check

203
		hero.x = borderOffsetX + newPos.x;
204
		hero.y = borderOffsetY + newPos.y;
205
		heroTile=IsoHelper.getTileCoordinates(heroCartPos,tileWidth);
206
		depthSort();
207
		//trace(heroTile);

208
	}
209
	tileTxt.text="Hero is on x: "+heroTile.x +" & y: "+heroTile.y;
210
}
211
212
//check for collision tile

213
function isWalkable():Boolean{
214
	var able:Boolean=true;
215
	var newPos:Point =new Point();
216
	newPos.x=heroCartPos.x +  (speed * dX);
217
	newPos.y=heroCartPos.y +  (speed * dY);
218
	switch (facing){
219
		case "north":
220
			newPos.y-=heroHalfSize;
221
		break;
222
		case "south":
223
			newPos.y+=heroHalfSize;
224
		break;
225
		case "east":
226
			newPos.x+=heroHalfSize;
227
		break;
228
		case "west":
229
			newPos.x-=heroHalfSize;
230
		break;
231
		case "northeast":
232
			newPos.y-=heroHalfSize;
233
			newPos.x+=heroHalfSize;
234
		break;
235
		case "southeast":
236
			newPos.y+=heroHalfSize;
237
			newPos.x+=heroHalfSize;
238
		break;
239
		case "northwest":
240
			newPos.y-=heroHalfSize;
241
			newPos.x-=heroHalfSize;
242
		break;
243
		case "southwest":
244
			newPos.y+=heroHalfSize;
245
			newPos.x-=heroHalfSize;
246
		break;
247
	}
248
	newPos=IsoHelper.getTileCoordinates(newPos,tileWidth);
249
	if(levelData[newPos.y][newPos.x]==1){
250
		able=false;
251
	}else{
252
		//trace("new",newPos);

253
	}
254
	return able;
255
}
256
257
//sort depth & draw to canvas

258
function depthSort()
259
{
260
	bg.bitmapData.lock();
261
	bg.bitmapData.fillRect(rect,0xffffff);
262
	var tileType:uint;
263
	var mat:Matrix=new Matrix();
264
	var pos:Point=new Point();
265
	for (var i:uint=0; i<levelData.length; i++)
266
	{
267
		for (var j:uint=0; j<levelData[0].length; j++)
268
		{
269
			tileType = levelData[i][j];
270
			//placeTile(tileType,i,j);

271
272
			pos.x = j * tileWidth;
273
			pos.y = i * tileWidth;
274
			pos = IsoHelper.twoDToIso(pos);
275
			mat.tx = borderOffsetX + pos.x;
276
			mat.ty = borderOffsetY + pos.y;
277
			if(tileType==0){
278
				bg.bitmapData.draw(grassTile,mat);
279
			}else{
280
				bg.bitmapData.draw(wallTile,mat);
281
			}
282
			if(heroTile.x==j&&heroTile.y==i){
283
				mat.tx=hero.x;
284
				mat.ty=hero.y;
285
				bg.bitmapData.draw(hero,mat);
286
			}
287
288
		}
289
	}
290
	bg.bitmapData.unlock();
291
//add character rectangle

292
}
293
createLevel();

Registratie Punten

Speciale aandacht besteden aan de registratiepunten van de tegels en de held. (Registratiepunten kunnen worden beschouwd als de oorsprong punten voor elke bijzondere sprite.) Deze vallen meestal niet in de afbeelding, maar zijn eerder de linkerbovenhoek van het selectiekader van de sprite.

We zullen onze tekencode moeten wijzigen om de registratiepunten correct te corrigeren, voornamelijk voor de held.

Botsing Detectie

Een ander interessant punt om op te merken is dat we berekenen botsingdetectie op basis van het punt waar de held is.

Maar de held volume, heeft en kan niet nauwkeurig worden vertegenwoordigd door één aanspreekpunt, dus we de held te vertegenwoordigen als een rechthoek en een selectievakje voor botsingen tegen elke hoek van deze rechthoek moeten zodat er geen overlappingen met andere tegels en dus geen diepte artefacten.

Snelkoppelingen

In de demo Ververs ik gewoon de scène opnieuw elk frame op basis van de nieuwe positie van de held. We vinden de tegel die de held inneemt en de held op de top van de tegel van de grond te trekken wanneer de rendering lussen die tegels bereiken.

Maar indien wij dichterbij kijken, zullen we vinden dat er is geen behoefte om te doorlopen alle tegels in dit geval. De gras tegels en de boven  en linkerrand wandtegels worden altijd getekend voordat de held wordt getekend, zodat we niet steeds hoeven ze helemaal te vernieuwen. Ook zijn de onderste en rechtse wandtegels altijd voor de held en dus getekend na de held is getekend

In wezen moeten, alleen wij uitvoeren van diepte sorteren tussen de muur binnen het actieve gebied en de held - dat wil zeggen twee tegels. Deze soorten snelkoppelingen te merken zal je helpen besparen een heleboel verwerkingstijd, die kan worden cruciaal voor de prestaties.


Conclusie

Onderhand, hebt je een grote basis voor het bouwen van isometrische spellen van je eigen: kunt je het renderen van de wereld en de objecten daarin, vertegenwoordigen niveau gegevens in eenvoudige 2D arrays, converteren tussen Cartesiaanse en isometrische coordiates, en deal met begrippen zoals diepte sorteren en animatie van het karakter. Geniet van het maken van isometrische werelden!

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.