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

Oprette isometrisk verdener: En Primer for Spilu Dviklere

Scroll to top
Read Time: 20 min

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

I denne vejledning giver jeg dig et bredt overblik over, hvad du behøver at vide for at skabe isometriske verdener. Du vil lære de isometriske projektion er, og hvordan til at repræsentere isometrisk niveauer som 2D arrays. Vi vil formulere relationer mellem visningen og logik, således at vi kan nemt manipulere objekter på skærmen og håndtag flise-baseret kollisions. Vi vil også se på sortering af dybde og karakter animation.

Relaterede Indlæg

Vil endnu flere tips om at skabe isometrisk verdener? Tjek den opfølgn-ing post, Oprette Isometrisk Verdener: En Primer for Gamedevs, Fortsat, og Juwals bog, Stær Spil Udvikling Essentials.


1. De Isometriske Verden

Isometrisk er en visning, der bruges til at skabe en illusion af 3D for et ellers 2D spil - undertiden benævnt pseudo 3D eller 2.5 D. Disse billeder (taget fra Diablo 2 og Age of Empires) illustrerer, hvad jeg mener:

diablodiablodiablo

Diablo 2
AOEAOEAOE

Age of Empires

Gennemføre en isometrisk kan gøres på mange måder, men for nemheds skyld vil jeg fokusere på en flise-baseret tilgang, som er den mest effektive og udbredte metode. Jeg har overlejret hvert skærmbillede ovenfor med en diamant gitter viser hvor terrænet er delt op i fliser.


2. Flise-Baserede Spil

I den flise-baseret tilgang, er hver visuelle element opdelt i mindre dele, kaldet fliser, en standard størrelse. Disse fliser vil blive arrangeret til at danne spilverdenen forudbestemte niveau data - normalt en 2D array.

Relaterede Stillinger

For eksempel Lad os betragte en standard top-down 2D visning med to fliser - en græs flise og en væg flise - som vist her:

base 2d tiles

Nogle simple fliser

Disse fliser er hver af samme størrelse som hinanden, og hver firkant, så flise højde og flise bredde er den samme.

For en plan med græsarealer afgrænses på alle sider af vægge, vil den plan data 2D array ligne indeværende:

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]]

Her, 0 angiver en græs flise og 1 betegner en væg flise. Arrangere fliser niveau data vil producere den nedenstående niveau billede:

2d level simple

Et simpelt niveau, der vises i en top-down visning.

Vi kan forbedre dette ved at tilføje hjørnefliser og separate vandrette og lodrette vægfliser, der kræver fem ekstra fliser:

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

Forbedret niveau med flise numre

Jeg håber, at begrebet den flise-baseret tilgang er nu klar. Dette er en enkel 2D gitter gennemførelse, som vi kunne kode som så:

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)

Her antager vi at flise bredde og flise højde er lige (og det samme for alle fliser), og matche fliser billeder dimensioner. Så er flise bredde og flise højde for dette eksempel både 50px, som udgør den samlede niveau størrelse af 300x300px - dvs, seks rækker og seks kolonner af fliser måling 50x50px hver.

I en normal flise-baseret tilgang gennemføre vi enten en top-down udsigt eller side; for en isometrisk skal vi gennemføre de isometriske projektion.


3. Isometriske Projektion

Den bedste tekniske forklaring på hvad "isometriske projektion" betyder, så vidt jeg ved, er fra denne artikel af Clint Bellanger:

Vi vinkel vores kamera langs to akser (svinge kameraet 45 grader til den ene side, derefter 30 grader ned). Dette skaber en diamant (rhombus) formet gitter hvor gitter rum er dobbelt så bredt som de er høje. Denne stil blev populariseret af strategispil og action RPG. Hvis vi ser på en terning i denne visning, tre sider er synlige (top og to modstående sider).

Selv om det lyder en smule kompliceret, er faktisk gennemfører denne visning ligetil. Skal vi forstå er forholdet mellem 2D og de isometriske plads - det vil sige forholdet mellem niveau data og se; transformation fra top-down "Kartesiske" koordinater til isometrisk koordinater.

the_isometric_gridthe_isometric_gridthe_isometric_grid

Kartesiske gitter vs isometriske gitter.

(Vi overvejer ikke en sekskantet flise baseret teknik, som er en anden måde at gennemføre isometrisk verdener.)

Placere Isometrisk Fliser

Lad mig forsøge at forenkle forholdet mellem niveau data gemmes som en 2D array og isometrisk - det vil sige, hvordan vi omdanne kartesiske koordinater til isometrisk koordinater.

Vi vil forsøge at skabe isometrisk til vores væg-lukket græsarealer niveau data:

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]]

I dette scenario kan vi bestemme en walkable område ved at kontrollere, om arrayelement er 0 ved at koordinere, der derved viser græs. 2D-visning gennemførelsen af de ovennævnte plan var en ligetil iteration med to løkker, placere kvadratiske fliser modregning hver med faste flise højde og flise breddeværdier.

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)

For isometrisk forbliver koden den samme, men placeTile() funktionen ændringer.

For en isometrisk skal vi beregne de tilsvarende isometrisk koordinater inde sløjfer.
Ligninger til at gøre dette er saaledes, hvor isoX og isoY repræsenterer isometrisk x - og y-koordinater, og cartX og cartY repræsenterer kartesiske x - og y-koordinaterne:

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;

Disse funktioner viser, hvordan du kan konvertere fra et system til et andet:

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
}

Pseudokode for løkken så ligner dette:

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

Vores wall-lukket græsarealer i en isometrisk visning.

Som et eksempel, lad os se, hvordan en typisk 2D holdning bliver omdannet til en isometrisk position:

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];

Ligeledes et input af [0, 0] vil resultere i [0, 0], og [10, 5] vil give [5, 7,5].

Ovenstående metode gør det muligt for os at skabe en direkte sammenhæng mellem den 2D plan data og isometrisk koordinaterne. Vi kan finde den flise koordinater i niveau data fra sin kartesiske koordinater ved hjælp af denne funktion:

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
}

(Her, vi hovedsageligt antager at flise højde og flise bredde er lig, som i de fleste tilfælde.)

Derfor, fra et par af skærmen (isometrisk) koordinater, kan vi finde flise koordinater ved at ringe:

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

Denne skærm punkt kunne være, siger, en mus klik eller en pick-up brugsstilling.

Tip: En anden metode til placering er Zigzag-model, som tager en anden tilgang helt.

Bevæger sig i Isometrisk Koordinater

Bevægelse er meget nemt: du manipulere dataene spillets verden med kartesiske koordinater og bare bruge ovenstående funktioner for at opdatere det på skærmen. For eksempel, hvis du vil flytte en karakter frem i den positive y-retning, kan du blot forøge egenskaben y og derefter konvertere sin holdning til isometrisk koordinater:

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

Dybde Sortering

Ud over normale placering, bliver vi nødt til at tage sig af dybde sortering for tegning af isometrisk verden. Det sikrer, at emner tættere til spilleren er trukket oven på varer længere væk.

Den enkleste dybde sorteringsmetoden er simpelthen at bruge værdien kartesiske y-koordinaten som nævnt i denne hurtige Tip: yderligere op skærmen objektet er, jo tidligere det bør drages. Dette arbejde godt, så længe vi ikke har nogen sprites, der indtager mere end en enkelt flise plads.

Den mest effektive måde at dybde sortering for isometrisk verdener er at bryde alle brikkerne i single-flise standardmål og ikke at tillade større billeder. For eksempel, her er en flise, hvilke gør ikke passe ind i standard flisestørrelsen - Se hvordan vi kan opdele det i flere fliser, der hver især passer flise dimensioner:

split big tilesplit big tilesplit big tile

Et stort billede er opdelt i flere fliser af isometrisk standardmål

4. At Skabe Kunst

Isometrisk kunst kan være pixel kunst, men det behøver ikke at være. Når der beskæftiger sig med isometrisk pixel art, fortæller Rhysd's guide dig næsten alt hvad du behøver at vide. Nogle teori kan findes på Wikipedia så godt.

Når du opretter isometrisk kunst, er de generelle regler

  • Starte med en tom isometriske gitter og overholde pixel perfekt præcision.
  • Forsøge at bryde kunst i enkelt isometrisk flise billeder.
  • Forsøge at sikre, at hver flise er enten walkable eller ikke-walkable. Det bliver kompliceret, hvis vi har brug for til at rumme en enkelt flise, der indeholder både walkable og ikke-walkable områder.
  • De fleste fliser skal problemfrit flise i en eller flere retninger.
  • Skygger kan være vanskelig at gennemføre, medmindre vi bruge en lagdelt tilgang, hvor vi trækker skygger på jorden lag og derefter tegne helten (eller træer eller andre objekter) på det øverste lag. Hvis den metode, du bruger, ikke er multi-lag, gør sikker på skygger falder på forsiden, således at de ikke vil falde på, siger, helten, når han står bag et træ.
  • I tilfælde du skal bruge en flise billede større end standard isometrisk flisestørrelsen, prøve at bruge en dimension, som er et multiplum af flisestørrelsen iso. Det er bedre at have en lagdelt tilgang i sådanne tilfælde, hvor vi kan opdele kunsten i forskellige stykker baseret på dens højde. For eksempel, et træ kan opdeles i tre stykker: roden, stammen og løv. Dette gør det nemmere at sortere dybder, som vi kan trække stykker i tilsvarende lag, som svarer til deres højder.

Isometrisk fliser, der er større end enkelt flise dimensioner vil skabe problemer med dybde sortering. Nogle af spørgsmålene, der diskuteres i disse links:


5. Isometrisk Tegn

Gennemføre tegn i isometrisk er ikke kompliceret, som det kan lyde. Karakter kunst skal være oprettet i henhold til bestemte standarder. Først vi bliver nødt til at fastsætte, hvor mange retninger af bevægelse er tilladt i vores spil - normalt spil vil give fire-vejs bevægelse eller ottevejs bevægelse.

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

8-vejs navigation retninger i top-down og isometriske visninger.

For en top-down se, kunne vi skabe et sæt af tegn animationer står i én retning, og blot rotere dem for alle de andre. For isometrisk karakter kunst skal vi igen gøre hver animation i hver af de tilladte retninger - så for otte-måde bevægelse skal vi oprette otte animationer til hver handling. For at lette forståelse betegne vi normalt retninger som Nord, Nord-Vest, Vest, Sydvest, Syd, Syd-Ost, Mod Ost, og Nord-Ost, mod uret, i nævnte rækkefølge.

spriteSheetspriteSheetspriteSheet

En isometrisk karakter står i forskellige retninger.

Vi placerer tegn på samme måde, vi lægge fliser. Flytning af en karakter opnås ved beregning af bevægelse i kartesiske koordinater og derefter konvertere til isometrisk koordinater. Lad os antage, vi bruger tastaturet til at styre karakteren.

Vi vil sætte to variabler, dX og dY, baseret på retningstasterne trykket. Som standard disse variabler vil være 0, og vil blive opdateret som pr nedenfor diagram, hvor U, D, F og L betegne op, ned, højre og venstre piletasterne, henholdsvis. En værdi af 1 under en nøgle repræsenterer nøglen er trykket; 0 angiver, at nøglen ikke trykkes.

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, ved hjælp af værdierne af dX og dY, vi kan opdatere de kartesiske koordinater som så:

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

dX og dY står for ændring i x - og y-position karakter, baseret på at taster trykkes ned.

Vi kan let beregne de nye isometrisk koordinater, som vi allerede har drøftet:

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

Når vi har den nye isometriske position, skal vi flytte karakteren til denne position. Baseret på de værdier, vi har for dX og dY, kan vi afgøre, hvilken retning karakteren står og bruge den tilsvarende karakter kunst.

Sammenstød Afsløring

Kollisionsdetektion sker ved at kontrollere, om fliser på den beregnede nye position er en ikke-walkable flise. Så når vi finder den nye holdning, vi straks flytte ikke karakter der, men første check at se, hvad flise indtager rummet.

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

I funktion isWalkable() undersøge vi, om niveau data array-værdi på en given koordinat er en walkable flise eller ej. Vi skal sørge for at opdatere den retning hvor karakteren står over for - selv om han ikke flyttes, som i tilfælde af ham at ramme en ikke-walkable flise.

Dybde Sortering Med Tegn

Overveje et tegn og et træ fliser i isometrisk verden.

For at forstå korrekt dybde sortering, må vi forstå, at når figurens x - og y-koordinaterne er mindre end dem af træet, træet overlapper karakter. Når figurens x - og y-koordinaterne er større end i træet, overlapper tegnet træet.

Når de har den samme x-koordinat, så vi beslutter, baseret på den y-koordinat alene: der har højere y-koordinaten overlapper den anden. Når de har samme y-koordinat, så vi beslutter, baseret på den x-koordinat alene: der har højere x-koordinaten overlapper den anden.

En forenklet version af dette er at bare sekventielt tegn niveauer fra den fjerneste flise,   flise [0] [0] - derefter tegne alle brikkerne i hver række én efter én. Hvis en person indtager en flise, vi drage jorden flise først og derefter gøre karakter flise. Dette vil fungere fint, fordi karakteren ikke kan indtage en væg flise.

Dybde sortering skal gøres, hver gang nogen flise ændringer holdning. For eksempel, skal vi gøre det når som helst tegn flytte. Vi opdaterer derefter viste scenen, efter at have udført den dybde form, for at afspejle ændringerne, dybde.


6. Har en Go!

Nu, sætte din nye viden til god brug ved at skabe en fungerende prototype, med tastatur kontrol og korrekt dybde sortering og sammenstød afsløring. Her er min demo:

Klik her for at give SWF fokus og derefter bruge piletasterne. Klik her for fuld-størrelse version.

Du kan finde denne nytte klasse nyttig (jeg har skrevet det i AS3, men du skal kunne forstå det i alle andre programmeringssprog):

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
}

Hvis du virkelig sidder fast, er her den fulde kode fra min demo (i Flash og AS3 tidslinje kodeform):

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();

Registrering Point

Tage særlige hensyn til punkterne registrering af fliser og helten. (Registrering punkter kan opfattes som oprindelse point for hver særlig sprite.) Disse generelt ikke vil falde inde i billedet, men snarere vil være øverst venstre hjørne af den sprite afgrænsningsrammen.

Vi bliver nødt til at ændre vores tegning kode for at lave registrering point korrekt, primært til helten.

Sammenstød Afsløring

Et andet interessant punkt at bemærke er, at vi beregne kollisions baseret på det punkt, hvor helten er.

Men helten har volumen, og ikke kan præcist repræsenteres af et enkelt punkt, så vi skal repræsentere helten som et rektangel og check for kollisioner mod hvert hjørne af dette rektangel, således at der er ingen overlapning med andre fliser og dermed ingen dybde artefakter.

Genveje

I demoen gentegne jeg simpelthen scene igen hver ramme baseret på den nye placering af helten. Vi finde den flise, som helten indtager og tegne helt på toppen af jorden flise, når rendering sløjfer når fliserne.

Men hvis vi ser nærmere, vil vi finde, at der er ingen grund til at sløjfe gennem alle brikkerne i dette tilfælde. Græs fliser og de øverste og venstre vægfliser er altid tegnet før helten er trukket, så vi ikke behøver at gentegne dem på alle. Også, de nederste og højre vægfliser er altid foran helten og dermed trukket efter helten er trukket.

Det væsentlige, så vi kun skal udføre dybde sortering mellem væg inde i det aktive område og hero - det vil sige to fliser. At mærke disse typer af genveje vil hjælpe dig med at spare en masse behandlingstid, som kan være afgørende for ydeevne.


Konklusion

Ved nu, bør du have en stor grundlag for bygning isometrisk spil på din egen: du kan gøre verden og objekter i det, repræsenterer niveau data i simple 2D arrays, konvertere mellem kartesiske og isometrisk coordiates og beskæftige sig med begreber som dybde sortering og karakteranimation. Nyd skaber isometrisk verdener!

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.