Advertisement
  1. Game Development
  2. Isometric Games

Membuat Dunia Isometrik: Hal Dasar untuk Pengembang Game

Scroll to top
Read Time: 19 min

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

Dalam tutorial ini, saya akan memberi Anda gambaran luas tentang apa yang perlu Anda ketahui untuk menciptakan dunia isometrik. Anda akan belajar apa proyeksi isometrik, dan bagaimana merepresentasikan level isometrik sebagai array 2D. Kami akan merumuskan hubungan antara tampilan dan logika, sehingga kami dapat dengan mudah memanipulasi objek di layar dan menangani deteksi tabrakan berbasis-ubin. Kami juga akan melihat pengurutan kedalaman dan animasi karakter.

Tulisan Terkait

Ingin tips lebih lanjut tentang menciptakan dunia isometrik? Periksa posting tindak lanjut, Membuat Dunia Isometrik: A Primer untuk Gamedevs, Lanjutan, dan buku Juwal, Starling Game Development Essentials.


1. Dunia Isometrik

Tampilan isometrik adalah metode tampilan yang digunakan untuk menciptakan ilusi 3D untuk game 2D yang lain - kadang-kadang disebut sebagai pseudo 3D atau 2.5D. Gambar-gambar ini (diambil dari Diablo 2 dan Age of Empires) menggambarkan apa yang saya maksud:

diablodiablodiablo

Diablo 2
AOEAOEAOE

Age of Empires

Menerapkan pandangan isometrik dapat dilakukan dengan banyak cara, tetapi demi kesederhanaan saya akan fokus pada pendekatan berbasis ubin, yang merupakan metode yang paling efisien dan banyak digunakan. Saya telah menumpangkan setiap tangkapan layar di atas dengan kotak berlian yang menunjukkan bagaimana dataran dibagi menjadi ubin.


2. Game Berbasis Ubin

Dalam pendekatan berbasis ubin, setiap elemen visual dipecah menjadi bagian-bagian yang lebih kecil, yang disebut ubin, dengan ukuran standar. Ubin-ubin ini akan disusun untuk membentuk dunia permainan sesuai dengan data level yang ditentukan sebelumnya - biasanya sebuah array 2D.

Sebagai contoh, marilah kita mempertimbangkan tampilan 2D top-down standar dengan dua ubin - ubin rumput dan ubin dinding - seperti yang ditunjukkan di sini:

base 2d tiles

Beberapa ubin sederhana

Ubin ini masing-masing memiliki ukuran yang sama satu sama lain, dan masing-masing persegi, sehingga tinggi ubin dan lebar ubin adalah sama.

Untuk tingkat dengan padang rumput tertutup di semua sisi oleh dinding, tingkat 2D array data akan terlihat seperti ini:

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

Di sini, 0 menunjukkan ubin rumput dan 1 menunjukkan ubin dinding. Mengatur ubin sesuai dengan data level akan menghasilkan gambar tingkat di bawah ini:

2d level simple

Tingkat sederhana, ditampilkan dalam tampilan top-down.

Kami dapat meningkatkan ini dengan menambahkan ubin sudut dan ubin dinding vertikal dan horizontal terpisah, membutuhkan lima ubin tambahan:

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

Tingkat yang disempurnakan dengan nomor ubin

Saya harap konsep pendekatan berbasis ubin sekarang sudah jelas. Ini adalah implementasi grid 2D langsung, yang bisa kami kode seperti ini:

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)

Di sini kita mengasumsikan bahwa lebar petak dan tinggi petak adalah sama (dan sama untuk semua ubin), dan cocok dengan dimensi gambar petak. Jadi, lebar ubin dan tinggi ubin untuk contoh ini adalah 50px, yang merupakan total ukuran total 300x300px - yaitu, enam baris dan enam kolom ubin berukuran masing-masing 50x50px.

Dalam pendekatan berbasis ubin yang normal, kami juga menerapkan tampilan top-down atau tampilan samping; untuk pandangan isometrik kita perlu menerapkan proyeksi isometrik.


3. Proyeksi Isometrik

Penjelasan teknis terbaik tentang "proyeksi isometrik" berarti, sejauh yang saya tahu, adalah dari artikel ini oleh Clint Bellanger:

Kami mengarahkan kamera kami sepanjang dua sumbu (ayunkan kamera 45 derajat ke satu sisi, lalu 30 derajat ke bawah). Ini menciptakan grid berbentuk berlian (belah ketupat) di mana ruang grid dua kali lebar karena mereka tinggi. Gaya ini dipopulerkan oleh game strategi dan aksi RPG. Jika kita melihat kubus dalam pandangan ini, tiga sisi terlihat (atas dan dua sisi yang menghadap).

Meskipun terdengar agak rumit, sebenarnya menerapkan pandangan ini sangat mudah. Yang perlu kita pahami adalah hubungan antara ruang 2D dan ruang isometrik - yaitu hubungan antara data tingkat dan tampilan; transformasi dari koordinat "Cartesian" top-down ke koordinat isometrik.

the_isometric_gridthe_isometric_gridthe_isometric_grid

Grid Cartesian vs grid isometrik.

(Kami tidak mempertimbangkan teknik berbasis hexagonal tile, yang merupakan cara lain untuk menerapkan dunia isometrik.)

Menempatkan Ubin Isometrik

Biarkan saya mencoba menyederhanakan hubungan antara data level yang disimpan sebagai array 2D dan tampilan isometrik - yaitu, bagaimana kita mengubah koordinat Cartesian menjadi koordinat isometrik.

Kami akan mencoba untuk membuat tampilan isometrik untuk data tingkat padang rumput di dinding tertutup:

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

Dalam skenario ini kita dapat menentukan area berjalan dengan memeriksa apakah elemen array adalah 0 pada koordinat itu, dengan demikian menunjukkan rumput. Implementasi tampilan 2D dari tingkat di atas adalah iterasi langsung dengan dua loop, menempatkan ubin persegi mengimbangi masing-masing dengan tinggi ubin tetap dan nilai lebar ubin.

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)

Untuk tampilan isometrik, kode tetap sama, tetapi fungsi placeTile() berubah.

Untuk tampilan isometrik kita perlu menghitung koordinat isometrik yang sesuai di dalam loop. 
Persamaan untuk melakukan ini adalah sebagai berikut, di mana isoX dan isoY mewakili isometrik koordinat x dan y, dan cartX dan cartY mewakili koordinat x dan y Cartesian:

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;

Fungsi-fungsi ini menunjukkan bagaimana Anda dapat mengkonversi dari satu sistem ke sistem lainnya:

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
 }

Pseudocode untuk loop kemudian terlihat seperti ini:

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

Padang rumput kami yang tertutup dinding dalam pandangan isometrik.

Sebagai contoh, mari kita lihat bagaimana posisi 2D biasa dikonversi ke posisi isometrik:

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

Demikian pula, masukan [0, 0] akan menghasilkan [0, 0], dan [10, 5] akan memberikan [5, 7.5].

Metode di atas memungkinkan kami untuk membuat korelasi langsung antara data level 2D dan koordinat isometrik. Kita dapat menemukan koordinat ubin dalam data level dari koordinat Cartesiannya menggunakan fungsi ini:

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
 }

(Di sini, kita pada dasarnya mengasumsikan bahwa tinggi dan lebar petak ubin adalah sama, seperti dalam banyak kasus.)

Oleh karena itu, dari sepasang koordinat layar (isometrik), kita dapat menemukan koordinat ubin dengan memanggil:

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

Titik layar ini bisa berupa, katakanlah, posisi klik mouse atau posisi pick-up.

Tips: Metode penempatan lainnya adalah model Zigzag, yang mengambil pendekatan yang berbeda sama sekali.

Bergerak dalam Koordinat Isometrik

Gerakannya sangat mudah: Anda memanipulasi data dunia game Anda dalam koordinat Cartesian dan cukup gunakan fungsi di atas untuk memperbaruinya di layar. Misalnya, jika Anda ingin memindahkan karakter ke depan dalam arah y positif, Anda bisa langsung menaikkan properti y dan kemudian mengonversi posisinya menjadi koordinat isometrik:

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

Depth Sorting

Selain penempatan normal, kita perlu memperhatikan penyortiran mendalam untuk menggambar dunia isometrik. Ini memastikan bahwa item yang lebih dekat ke pemain ditarik di atas item yang lebih jauh.

Metode pengurutan kedalaman yang paling sederhana adalah hanya menggunakan nilai koordinat-Cartesian y, sebagaimana disebutkan dalam Tip Singkat ini: semakin jauh ke atas layar objek, maka sebelumnya harus ditarik.  Ini berfungsi dengan baik selama kita tidak memiliki sprite yang menempati lebih dari satu ruang ubin.

Cara penyortiran kedalaman yang paling efisien untuk dunia isometrik adalah memecah semua ubin menjadi dimensi satu-ubin standar dan tidak memungkinkan gambar yang lebih besar. Misalnya, di sini adalah ubin yang tidak sesuai dengan ukuran ubin standar - lihat bagaimana kita dapat membaginya menjadi beberapa ubin yang masing-masing sesuai dengan dimensi ubin:

split big tilesplit big tilesplit big tile

Gambar besar dibagi menjadi beberapa ubin dimensi isometrik standar

4. Menciptakan Seni

Seni isometrik dapat berupa seni piksel, tetapi tidak harus demikian. Ketika berhadapan dengan seni piksel isometrik, panduan RhysD memberi tahu Anda hampir semua hal yang perlu Anda ketahui. Beberapa teori dapat ditemukan di Wikipedia juga.

Saat membuat seni isometrik, aturan umumnya adalah

  • Mulai dengan grid isometrik kosong dan patuhi presisi sempurna piksel.
  • Coba pecah seni menjadi gambar ubin isometrik tunggal.
  • Cobalah untuk memastikan bahwa masing-masing ubin bisa berjalan kaki atau tidak bisa dijelajahi. Akan rumit jika kita perlu mengakomodasi satu petak yang berisi area yang dapat dijelajahi dan tidak dapat dijelajahi.
  • Kebanyakan ubin perlu berplatform mulus dalam satu atau lebih arah.
  • Bayangan bisa rumit untuk diterapkan, kecuali kita menggunakan pendekatan berlapis di mana kita menggambar bayangan di lapisan tanah dan kemudian menggambar pahlawan (atau pohon, atau objek lain) di lapisan atas. Jika pendekatan yang Anda gunakan tidak berlapis-lapis, pastikan bayangan jatuh ke depan sehingga mereka tidak akan jatuh, katakanlah, pahlawan ketika dia berdiri di belakang pohon.
  • Jika Anda perlu menggunakan gambar ubin yang lebih besar dari ukuran ubin isometrik standar, coba gunakan dimensi yang merupakan kelipatan dari ukuran ubin iso. Lebih baik memiliki pendekatan berlapis dalam kasus-kasus seperti itu, di mana kita dapat membagi seni menjadi bagian-bagian berbeda berdasarkan tingginya. Sebagai contoh, sebuah pohon dapat dibagi menjadi tiga bagian: akar, batang, dan dedaunan. Ini membuatnya lebih mudah untuk menyortir kedalaman karena kita dapat menggambar potongan di lapisan yang sesuai yang sesuai dengan ketinggian mereka.

Ubin isometrik yang lebih besar dari dimensi ubin tunggal akan menciptakan masalah dengan penyortiran kedalaman. Beberapa masalah dibahas dalam tautan ini:


5. Karakter Isometrik

Menerapkan karakter dalam tampilan isometrik tidaklah rumit karena mungkin terdengar. Seni karakter perlu dibuat sesuai dengan standar tertentu. Pertama kita harus memperbaiki berapa banyak gerakan yang diizinkan dalam permainan kita - biasanya game akan menyediakan gerakan empat arah atau gerakan delapan arah.

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

Arah navigasi delapan arah dalam tampilan top-down dan isometrik.

Untuk tampilan top-down, kita bisa membuat satu set animasi karakter yang menghadap ke satu arah, dan cukup memutar untuk semua yang lain. Untuk seni karakter isometrik, kita perlu membuat ulang setiap animasi di setiap arah yang diizinkan - jadi untuk gerakan delapan arah kita perlu membuat delapan animasi untuk setiap tindakan. Untuk memudahkan pemahaman kita biasanya menunjukkan arah sebagai Utara, Utara-Barat, Barat, Selatan-Barat, Selatan, Tenggara, Timur, dan Utara-Timur, berlawanan arah jarum jam, dalam urutan itu.

spriteSheetspriteSheetspriteSheet

Karakter isometrik menghadap ke arah yang berbeda.

Kami menempatkan karakter dengan cara yang sama seperti yang kami letakkan ubin. Pergerakan karakter dilakukan dengan menghitung gerakan dalam koordinat Cartesian dan kemudian mengkonversi ke koordinat isometrik. Mari kita asumsikan kita menggunakan keyboard untuk mengontrol karakter.

Kami akan menetapkan dua variabel, dX dan dY, berdasarkan tombol arah ditekan. Secara default, variabel ini akan menjadi 0, dan akan diperbarui sesuai grafik di bawah ini, di mana UDR dan L menunjukkan tombol panah AtasBawahKanan dan Kiri, masing-masing. Nilai 1 bawah kunci mewakili tombol yang ditekan; 0 menyiratkan bahwa kuncinya tidak ditekan.

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

Sekarang, dengan menggunakan nilai-nilai dX dan dY, kita dapat memperbarui koordinat Cartesian seperti:

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

Jadi dX dan dY mewakili perubahan posisi x dan y karakter, berdasarkan tombol yang ditekan.

Kita dapat dengan mudah menghitung koordinat isometrik baru, seperti yang telah kita bahas:

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

Begitu kita memiliki posisi isometrik yang baru, kita perlu memindahkan karakter ke posisi ini. Berdasarkan nilai-nilai yang kita miliki untuk dX dan dY, kita dapat memutuskan arah mana yang dihadapi karakter dan menggunakan seni karakter yang sesuai.

Deteksi Tabrakan

Deteksi tabrakan dilakukan dengan memeriksa apakah ubin pada posisi baru yang dihitung adalah ubin yang tidak dapat dijelajahi. Jadi, setelah kami menemukan posisi baru, kami tidak segera memindahkan karakter di sana, tetapi periksa dulu untuk melihat apa ubin menempati ruang itu.

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

Dalam fungsi isWalkable(), kita memeriksa apakah nilai array data level pada koordinat yang diberikan adalah ubin walkable atau tidak. Kita harus berhati-hati untuk memperbarui arah di mana karakter itu menghadapinya - bahkan jika dia tidak bergerak, seperti dalam kasusnya memukul ubin yang tidak bisa dijelajahi.

Depth Sorting Dengan Karakter

Pertimbangkan karakter dan ubin pohon di dunia isometrik.

Untuk memahami dengan benar pemilahan kedalaman, kita harus memahami bahwa bilamana x-dan y-koordinat karakter kurang dari pohon, pohon tumpang tindih dengan karakter. Setiap kali x-dan y-koordinat karakter lebih besar dari pohon, karakter tumpang tindih dengan pohon.

Ketika mereka memiliki x-koordinat yang sama, maka kami memutuskan berdasarkan y-koordinat saja: mana yang memiliki koordinat y tinggi tumpang tindih dengan yang lain. Ketika mereka memiliki koordinat y yang sama maka kami memutuskan berdasarkan x-koordinat saja: mana yang memiliki koordinat x lebih tinggi tumpang tindih dengan yang lain.

Versi yang disederhanakan dari ini adalah hanya secara berurutan menarik level mulai dari ubin terjauh - yaitu, tile[0][0] - kemudian tarik semua ubin di setiap baris satu per satu. Jika karakter menempati ubin, kami menggambar ubin tanah terlebih dahulu dan kemudian membuat ubin karakter. Ini akan berfungsi dengan baik, karena karakter tidak dapat menempati ubin dinding.

Penyortiran kedalaman harus dilakukan setiap kali ada petak yang berubah posisi. Misalnya, kita perlu melakukannya setiap kali karakter bergerak. Kami kemudian memperbarui adegan yang ditampilkan, setelah melakukan semacam kedalaman, untuk mencerminkan perubahan kedalaman.


6. Memiliki Go!

Sekarang, letakkan pengetahuan baru Anda untuk penggunaan yang baik dengan menciptakan prototipe kerja, dengan kontrol keyboard dan penyortiran kedalaman yang tepat dan deteksi tabrakan. Inilah demo saya:

Klik untuk memberi fokus SWF, lalu gunakan tombol panah. Klik di sini untuk versi ukuran penuh.

Anda mungkin menemukan kelas utilitas ini berguna (saya sudah menulisnya di AS3, tetapi Anda harus bisa memahaminya dalam bahasa pemrograman lainnya):

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
 }

Jika Anda benar-benar terjebak, inilah kode lengkap dari demo saya (dalam bentuk kode waktu Flash dan AS3):

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

Poin Pendaftaran

Berikan pertimbangan khusus untuk poin pendaftaran ubin dan pahlawan. (Titik registrasi dapat dianggap sebagai titik asal untuk setiap sprite tertentu.) Ini umumnya tidak akan jatuh di dalam gambar, melainkan akan menjadi sudut kiri atas kotak pembatas sprite.

Kami harus mengubah kode gambar kami untuk memperbaiki titik pendaftaran dengan benar, terutama untuk pahlawan.

Deteksi Tabrakan

Hal lain yang menarik untuk dicatat adalah kami menghitung deteksi tabrakan berdasarkan titik di mana pahlawan berada.

Tapi pahlawan memiliki volume, dan tidak dapat secara akurat diwakili oleh satu titik, jadi kita perlu mewakili pahlawan sebagai persegi panjang dan memeriksa tabrakan terhadap setiap sudut persegi panjang ini sehingga tidak ada tumpang tindih dengan ubin lainnya dan karenanya tidak ada artefak yang mendalam.

Pintasan

Dalam demo, saya hanya menggambar ulang adegan lagi setiap frame berdasarkan posisi baru sang pahlawan. Kami menemukan ubin yang ditempati pahlawan dan menggambar pahlawan di atas ubin tanah ketika loop render mencapai ubin tersebut.

Tetapi jika kita melihat lebih dekat, kita akan menemukan bahwa tidak perlu mengulang melalui semua ubin dalam kasus ini. Ubin rumput dan ubin dinding atas dan kiri selalu diambil sebelum pahlawan ditarik, jadi kita tidak perlu menggambar ulang sama sekali. Juga, ubin dinding bawah dan kanan selalu di depan pahlawan dan karenanya ditarik setelah pahlawan ditarik.

Pada dasarnya, kita hanya perlu melakukan pemilahan kedalaman antara dinding di dalam area aktif dan pahlawan - yaitu, dua ubin. Memperhatikan pintas semacam ini akan membantu Anda menghemat banyak waktu pemrosesan, yang dapat menjadi sangat penting untuk kinerja.


Kesimpulan

Sekarang, Anda harus memiliki dasar yang hebat untuk membangun permainan isometrik Anda sendiri: Anda dapat membuat dunia dan benda-benda di dalamnya, mewakili data tingkat dalam array 2D sederhana, mengkonversi antara Cartesian dan coordiates isometrik, dan menangani konsep-konsep seperti pengurutan kedalaman dan animasi karakter.  Nikmati menciptakan dunia isometrik!

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 Game Development 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.