Advertisement
  1. Code
  2. Games

Buat RPG Top-Down di Flixel: Ruangan Pertama Anda

Scroll to top
Read Time: 34 min

Indonesian (Bahasa Indonesia) translation by Suci Rohini (you can also view the original English article)

Dalam tutorial ini kita akan beralih dari bertanya "Apa itu Flixel?" untuk memiliki ruangan dalam ruangan dan karakter yang dikontrol keyboard dalam gaya permainan peran top-down (pikirkan Zelda).


Pratinjau Hasil Akhir

Mari kita lihat hasil akhir yang akan kita upayakan:


Langkah 1: Memahami Struktur Proyek

Untuk orang-orang visual di antara kita, mari kita lihat bagaimana semuanya akan diatur sehingga sisanya akan masuk akal.

list of all the source files and folders used in the projectlist of all the source files and folders used in the projectlist of all the source files and folders used in the project

Pada dasarnya, kita memiliki semua karya seni disimpan di folder assets dan semua file ActionScript kita disimpan di folder src. Jika Anda ingin menggunakan tutorial ini sebagai dasar untuk mesin gim Anda sendiri, folder topdown berisi hal-hal umum (a.k.a engine) dan folder tutorial menunjukkan cara menggunakannya.

Anda mungkin akan melihat dengan cepat bahwa file seni memiliki nama yang sangat panjang. Daripada menunjukkan kepada Anda tutorial yang diisi dengan kotak merah yang menarik (puncak kemampuan artistik saya), kita akan menggunakan beberapa karya seni open source dari OpenGameArt. Setiap file diberi nama untuk menunjukkan sumber, artis, dan lisensi. Jadi, misalnya, armor (opengameart - Redshrike - ccby30).png berarti itu adalah gambar armor, diunduh dari OpenGameArt, dibuat oleh artis yang dikenal sebagai Redshrike, dan menggunakan lisensi CC-BY-30 (Creative Commons Attribution).

Singkat cerita - file seni ini dapat digunakan untuk tujuan apa pun selama kita menautkan kembali ke situs dan memberikan penghargaan kepada artis.

Berikut adalah deskripsi setiap file sumber dalam proyek:

  • topdown/TopDownEntity.as - kelas dasar untuk setiap sprite bergerak di RPG top-down kita
  • topdown/TopDownLevel.as - kelas dasar untuk level RPG top-down
  • tutorial/Assets.as - mengimpor gambar apa saja yang perlu kita gunakan dalam tutorial ini
  • tutorial/IndoorHouseLevel.as - mendefinisikan ruang dalam ruangan dengan beberapa benda yang tergeletak di sekitarnya
  • tutorial/Player.as - Ranger animasi yang dikontrol keyboard
  • tutorial/PlayState.as - Status Flixel yang mengontrol permainan kita
  • Default.css - file kosong yang diperlukan untuk mencegah kompiler Flex memberikan peringatan
  • Main.as - titik masuk untuk aplikasi
  • Preloader.as - Preloader Flixel

Sekarang mari kita mulai bisnis!


Langkah 2: Firing Up Flixel

Flixel adalah mesin game 2D untuk ActionScript 3. Mengutip beranda:

Flixel adalah perpustakaan pembuat game open source yang sepenuhnya gratis untuk penggunaan pribadi atau komersial.

Hal terpenting yang perlu diketahui tentang Flixel adalah bahwa ia dirancang untuk menggunakan gambar bitmap (grafik raster) alih-alih grafis vektor gaya Flash. Anda dapat menggunakan klip video Flash, tetapi butuh sedikit pijatan. Karena saya tidak merasa ingin memijat hari ini, kita akan menggunakan gambar untuk semua karya seni.

Flixel hadir dengan tool yang menciptakan proyek boneka untuk Anda. Tool ini membuat tiga file yang ada di root dari proyek: Default.css, Main.as, dan Preloader.as. Ketiga file ini menjadi dasar untuk hampir semua proyek di Flixel. Karena Default.css ada di sana untuk menghindari peringatan kompiler, mari kita lihat Main.as.

1
2
package
3
{
4
  import org.flixel.*;
5
	import tutorial.*;
6
7
	[SWF(width="480", height="480", backgroundColor="#ffffff")]
8
	[Frame(factoryClass="Preloader")]
9
	public class Main extends FlxGame
10
	{
11
		/**

12
		 * Constructor

13
		 */
14
		public function Main() {
15
			super(240, 240, PlayState, 2);
16
		}
17
	}
18
}

Hanya ada tiga baris yang penting di sini. Pertama, kita memberi tahu Flash untuk menggunakan jendela 480x480 dengan background putih. Kemudian kita memberi tahu Flash untuk menggunakan kelas Preloader kita saat memuat. Akhirnya, kita memberi tahu Flixel untuk menggunakan jendela 240x240 (memperbesar dengan faktor 2 untuk membuat segalanya terlihat lebih besar) dan menggunakan PlayState setelah semuanya siap.

Biarkan saya berbagi sepatah kata cepat tentang keadaan Flixel. Di Flixel, keadaan seperti jendela, tetapi Anda hanya dapat memilikinya satu per satu. Jadi, misalnya, Anda bisa memiliki status untuk menu utama game Anda (MainMenu), dan ketika pengguna mengklik tombol Start Game Anda beralih ke PlayState. Karena kita ingin game segera dijalankan, kita hanya perlu satu status (PlayState).

Selanjutnya adalah Preloader.as.

1
2
package
3
{
4
	import org.flixel.system.FlxPreloader;
5
6
	public class Preloader extends FlxPreloader
7
	{
8
		/**

9
		 * Constructor

10
		 */
11
		public function Preloader():void {
12
			className = "Main";
13
			super();
14
		}
15
	}
16
}

Tidak banyak yang bisa dilihat di sini. Karena kita memperluas dari FlxPreloader, Flixel benar-benar hanya mengurusnya. Satu-satunya hal yang perlu diperhatikan adalah bahwa jika Anda mengubah Main ke beberapa nama lain, Anda harus mengubah className di sini pada baris yang disorot.

Kita hampir melihat sesuatu di layar sekarang. Yang kita butuhkan adalah keadaan Flixel untuk membuat bola bergulir, jadi inilah PlayState.as.

1
2
package tutorial
3
{
4
	import org.flixel.*;
5
6
	/**

7
	 * State for actually playing the game

8
	 * @author Cody Sandahl

9
	 */
10
	public class PlayState extends FlxState
11
	{
12
		/**

13
		 * Create state

14
		 */
15
		override public function create():void {
16
			FlxG.mouse.show();
17
		}
18
	}
19
}

Jika Anda mengompilasi kode ini, Anda akan mendapatkan layar hitam luar biasa dengan kursor mouse. Jangan takut, ini akan lebih baik dari sini.


Langkah 3: Membuat tingkat dasar

Sekarang setelah Flixel aktif dan berjalan, saatnya membuat level RPG top-down. Saya ingin memberi Anda kelas yang dapat digunakan kembali sehingga Anda dapat membuat level Anda sendiri, jadi kita benar-benar akan membuat kelas level umum yang bisa digunakan untuk membuat sesuatu yang lebih menarik nanti. Ini adalah topdown/TopDownLevel.as.

1
2
package topdown 
3
{
4
	import org.flixel.*;
5
	/**

6
	 * Base class for all levels

7
	 * @author Cody Sandahl

8
	 */
9
	public class TopDownLevel extends FlxGroup
10
	{
11
		/**

12
		 * Map

13
		 */
14
		public var state:FlxState; // state displaying the level

15
		public var levelSize:FlxPoint; // width and height of level (in pixels)

16
		public var tileSize:FlxPoint; // default width and height of each tile (in pixels)

17
		public var numTiles:FlxPoint; // how many tiles are in this level (width and height)

18
		public var floorGroup:FlxGroup; // floor (rendered beneath the walls - no collisions)

19
		public var wallGroup:FlxGroup; // all the map blocks (with collisions)

20
		public var guiGroup:FlxGroup; // gui elements

21
		
22
		/**

23
		 * Player

24
		 */
25
		public var player:TopDownEntity;
26
		public var playerStart:FlxPoint = new FlxPoint(120, 120);
27
		
28
		/**

29
		 * Constructor

30
		 * @param	state		State displaying the level

31
		 * @param	levelSize	Width and height of level (in pixels)

32
		 * @param	blockSize	Default width and height of each tile (in pixels)

33
		 */
34
		public function TopDownLevel(state:FlxState, levelSize:FlxPoint, tileSize:FlxPoint):void {
35
			super();
36
			this.state = state;
37
			this.levelSize = levelSize;
38
			this.tileSize = tileSize;
39
			if (levelSize && tileSize)
40
				this.numTiles = new FlxPoint(Math.floor(levelSize.x / tileSize.x), Math.floor(levelSize.y / tileSize.y));
41
			// setup groups

42
			this.floorGroup = new FlxGroup();
43
			this.wallGroup = new FlxGroup();
44
			this.guiGroup = new FlxGroup();
45
			// create the level

46
			this.create();
47
		}
48
		
49
		/**

50
		 * Create the whole level, including all sprites, maps, blocks, etc

51
		 */
52
		public function create():void {
53
			createMap();
54
			createPlayer();
55
			createGUI();
56
			addGroups();
57
			createCamera();
58
		}
59
		
60
		/**

61
		 * Create the map (walls, decals, etc)

62
		 */
63
		protected function createMap():void {
64
		}
65
		
66
		/**

67
		 * Create the player, bullets, etc

68
		 */
69
		protected function createPlayer():void {
70
			player = new TopDownEntity(playerStart.x, playerStart.y);
71
		}
72
		
73
		/**

74
		 * Create text, buttons, indicators, etc

75
		 */
76
		protected function createGUI():void {
77
		}
78
		
79
		/**

80
		 * Decide the order of the groups. They are rendered in the order they're added, so last added is always on top.

81
		 */
82
		protected function addGroups():void {
83
			add(floorGroup);
84
			add(wallGroup);
85
			add(player);
86
			add(guiGroup);
87
		}
88
		
89
		/**

90
		 * Create the default camera for this level

91
		 */
92
		protected function createCamera():void {
93
			FlxG.worldBounds = new FlxRect(0, 0, levelSize.x, levelSize.y);
94
			FlxG.camera.setBounds(0, 0, levelSize.x, levelSize.y, true);
95
			FlxG.camera.follow(player, FlxCamera.STYLE_TOPDOWN);
96
		}
97
		
98
		/**

99
		 * Update each timestep

100
		 */
101
		override public function update():void {
102
			super.update();
103
			FlxG.collide(wallGroup, player);
104
		}
105
	}
106
}

Semua variabel memiliki deskripsi sendiri dalam kode sumber, jadi saya tidak akan membuat Anda bosan dengan pengulangan yang terlalu banyak. Namun, saya harus menjelaskan kelompok di Flixel.

Kami memiliki tiga grup yang didefinisikan di sini: floorGroup, wallGroup, dan guiGroup. Flixel menggunakan grup untuk menentukan dalam urutan apa untuk membuat sprite (untuk memutuskan apa yang ada di atas ketika mereka tumpang tindih) dan untuk menangani tabrakan. Kita ingin pemain dapat berjalan di lantai (tidak perlu tabrakan), Namun juga ingin dinding dan benda (benturan pasti diperlukan) sehingga kita membutuhkan dua kelompok. Kita juga memerlukan grup terpisah untuk interface pengguna (guiGroup) sehingga kita dapat memastikan itu diberikan di atas segalanya.

Grup diberikan sesuai urutan yang ditambahkan, yang ditentukan dalam fungsi addGroups() kita. Karena kita ingin guiGroup selalu di atas, panggil add(guiGroup) setelah semua grup lainnya. Jika Anda membuat grup sendiri dan lupa memanggil add(), mereka tidak akan muncul di layar.

Di konstruktor, kita menyimpan beberapa nilai yang berguna (seperti jumlah petak di level) dan memanggil create(). Fungsi create() menunjukkan kepada Anda apa yang masuk ke level Flixel - peta, pemain, antarmuka, grup (untuk mengontrol urutan render dan tabrakan), dan tampilan kamera. Masing-masing memiliki fungsi sendiri untuk membantu menjaga hal-hal lebih mudah dibaca sehingga kita dapat menggunakan kembali fungsi umum. Misalnya, lihat createCamera().

1
2
/**

3
 * Create the default camera for this level

4
 */
5
protected function createCamera():void {
6
    FlxG.worldBounds = new FlxRect(0, 0, levelSize.x, levelSize.y);
7
    FlxG.camera.setBounds(0, 0, levelSize.x, levelSize.y, true);
8
    FlxG.camera.follow(player, FlxCamera.STYLE_TOPDOWN);
9
}

Kita tidak perlu mengubah fungsi ini untuk membuat level dalam ruangan kita sendiri. Flixel memiliki kamera internal untuk permainan top-down (FlxCamera.STYLE_TOPDOWN). Yang benar-benar dilakukan di sini adalah memberi tahu kamera untuk tidak meninggalkan level (dengan memanggil setBounds()) dan memberi tahu kamera untuk mengikuti pemain (dengan memanggil follow()) jika level lebih besar dari layar dan memerlukan pengguliran. Ini akan bekerja untuk hampir semua jenis level, sehingga kita dapat menyimpannya di sini daripada mengkode ulang untuk setiap level kita.

Satu-satunya hal yang perlu diperhatikan adalah update().

1
2
/**

3
 * Update each timestep

4
 */
5
override public function update():void {
6
    super.update();
7
    FlxG.collide(wallGroup, player);
8
}

FlxG.collide(wallGroup, player) menyebabkan pemain menabrak dinding daripada berjalan melewatinya. Karena kita tidak memanggil FlxG.collide(floorGroup, player), pemain dapat berjalan di seluruh lantai dengan sedikit tabrakan yang terlihat (hal yang sama juga untuk guiGroup).

Akhirnya, kita perlu membuat PlayState menggunakan level mewah kita.

1
2
package tutorial
3
{
4
	import org.flixel.*;
5
	import topdown.*;
6
7
	/**

8
	 * State for actually playing the game

9
	 * @author Cody Sandahl

10
	 */
11
	public class PlayState extends FlxState
12
	{
13
		/**

14
		 * Constants

15
		 */
16
		public static var LEVEL_SIZE:FlxPoint = new FlxPoint(240, 240); // level size (in pixels)

17
		public static var BLOCK_SIZE:FlxPoint = new FlxPoint(16, 16); // block size (in pixels)

18
		
19
		/**

20
		 * Current level

21
		 * NOTE: "public static" allows us to get info about the level from other classes

22
		 */
23
		public static var LEVEL:TopDownLevel = null;
24
		
25
		/**

26
		 * Create state

27
		 */
28
		override public function create():void {
29
			FlxG.mouse.show();
30
			// load level

31
			LEVEL = new TopDownLevel(this, LEVEL_SIZE, BLOCK_SIZE);
32
			this.add(LEVEL);
33
		}
34
	}
35
}

Ingatlah untuk memanggil this.add(LEVEL) kecuali Anda ingin menatap layar hitam selamanya. Sebagai komentar menyatakan, saya menggunakan public static var LEVEL sebagai kenyamanan untuk masa depan. Misalkan Anda menambahkan beberapa kecerdasan buatan ke game Anda dan AI Anda perlu tahu di mana pemain berada; dengan cara ini, Anda dapat memanggil PlayState.LEVEL.player dan menjaga semuanya tetap menyenangkan dan mudah. Ini belum tentu cara tercantik untuk melakukan sesuatu, tetapi itu akan menyelesaikan pekerjaan jika digunakan dengan hemat.


Langkah 4: Membuat Entitas Dasar

Entitas adalah sesuatu yang perlu ditampilkan dan dapat bergerak. Ini bisa menjadi pemain, karakter yang dikendalikan komputer, atau mungkin bahkan sesuatu seperti panah. Karena ada banyak entitas pada level, kita ingin kelas generik yang dapat digunakan untuk menghemat waktu. Lihatlah topdown/TopDownEntity.as.

1
2
package topdown
3
{
4
	import org.flixel.*;
5
	
6
	/**

7
	 * A moveable object in the game (player, enemy, NPC, etc)

8
	 * @author Cody Sandahl

9
	 */
10
	public class TopDownEntity extends FlxSprite
11
	{
12
		/**

13
		 * Constants

14
		 */
15
		public static const SIZE:FlxPoint = new FlxPoint(16, 18); // size in pixels

16
		
17
		/**

18
		 * Constructor

19
		 * @param	X	X location of the entity

20
		 * @param	Y	Y location of the entity

21
		 */
22
		public function TopDownEntity(X:Number = 100, Y:Number = 100):void {
23
			super(X, Y);
24
			makeGraphic(SIZE.x, SIZE.y, 0xFFFF0000); // use this if you want a generic box graphic by default

25
		}
26
	}
27
}

Perhatikan bahwa kita memperluas dari FlxSprite. Ini memberikan kami akses ke banyak kekuatan di belakang Flixel. makeGraphic() menciptakan bitmap persegi panjang dengan ukuran yang diberikan (16 x 18 dalam hal ini), menggunakan warna Anda lewat di. Warna ini dalam format 0xAARRGGBB, jadi 0xFFFF0000 berarti kita membuat kotak merah solid (saya memperingatkan Anda tentang kemampuan artistik saya). Anda bisa dipusingkan dengan nilai ini untuk melihat bagaimana perubahan warnanya. Bahkan, kita sekarang memiliki sesuatu selain layar kosong!

our first entity - a black screen with a red box in the middleour first entity - a black screen with a red box in the middleour first entity - a black screen with a red box in the middle

Masih tidak terlalu mengasyikkan, tapi setidaknya kita bisa melihat sesuatu, bukan?


Langkah 5: Membuat Ruangan Dalam Ruangan

Saya tidak tahu tentang Anda, tetapi saya lelah melihat backgound hitam itu. Mari kita membuatnya terlihat seperti ruangan. Inilah tutorial/IndoorHouseLevel.as.

1
2
package tutorial 
3
{
4
	import org.flixel.*;
5
	import topdown.*;
6
	
7
	/**

8
	 * A basic indoor scene

9
	 * @author Cody Sandahl

10
	 */
11
	public class IndoorHouseLevel extends TopDownLevel
12
	{
13
		/**

14
		 * Floor layer

15
		 */
16
		protected static var FLOORS:Array = new Array(
17
			0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
18
			0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
19
			0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
20
			0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
21
			0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
22
			0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
23
			0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
24
			0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
25
			0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
26
			0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
27
			0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
28
			0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
29
			0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
30
			0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
31
			0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
32
		);
33
		
34
		/**

35
		 * Wall layer

36
		 */
37
		protected static var WALLS:Array = new Array(
38
			1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3,
39
			6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8,
40
			6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8,
41
			6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8,
42
			6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8,
43
			6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8,
44
			6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8,
45
			6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8,
46
			6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8,
47
			6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8,
48
			6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8,
49
			6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8,
50
			6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8,
51
			6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8,
52
			2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2
53
		);
54
		
55
		/**

56
		 * Constructor

57
		 * @param	state		State displaying the level

58
		 * @param	levelSize	Width and height of level (in pixels)

59
		 * @param	blockSize	Default width and height of each tile (in pixels)

60
		 */
61
		public function IndoorHouseLevel(state:FlxState, levelSize:FlxPoint, blockSize:FlxPoint):void {
62
			super(state, levelSize, blockSize);
63
		}
64
		
65
		/**

66
		 * Create the map (walls, decals, etc)

67
		 */
68
		override protected function createMap():void {
69
			var tiles:FlxTilemap;
70
			// floors

71
			tiles = new FlxTilemap();
72
			tiles.loadMap(
73
				FlxTilemap.arrayToCSV(FLOORS, 15), // convert our array of tile indices to a format flixel understands

74
				Assets.FLOORS_TILE, // image to use

75
				tileSize.x, // width of each tile (in pixels)

76
				tileSize.y, // height of each tile (in pixels)

77
				0, // don't use auto tiling (needed so we can change the rest of these values)

78
				0, // starting index for our tileset (0 = include everything in the image)

79
				0, // starting index for drawing our tileset (0 = every tile is drawn)

80
				uint.MAX_VALUE // which tiles allow collisions by default (uint.MAX_VALUE = no collisions)

81
			);
82
			floorGroup.add(tiles);
83
			// walls

84
			// FFV: make left/right walls' use custom collision rects

85
			tiles = new FlxTilemap();
86
			tiles.loadMap(
87
				FlxTilemap.arrayToCSV(WALLS, 15), // convert our array of tile indices to a format flixel understands

88
				Assets.WALLS_TILE, // image to use

89
				tileSize.x, // width of each tile (in pixels)

90
				tileSize.y // height of each tile (in pixels)

91
			);
92
			wallGroup.add(tiles);
93
		}
94
	}
95
}

Hal pertama yang Anda perhatikan adalah dua array angka raksasa, FLOORS dan WALLS. Array ini menentukan layer peta kita. Angka-angka adalah indeks ubin berdasarkan karya seni yang digunakan. Saya telah memperbesar gambar yang digunakan untuk dinding kita untuk menunjukkan kepada Anda apa yang saya bicarakan.

sprite sheet for the walls in our level

Perhatikan bahwa nol kosong (tidak menghasilkan apa-apa). Gambar lantai, di sisi lain, hanya satu ubin, diulang, saat ini. Itu berarti kita ingin menggambar setiap ubin (termasuk nol). Jadi jika Anda melihat fungsi createMap(), kode kita untuk memuat di lantai lebih panjang dari kode untuk memuat di dinding.

Kita mulai dengan FlxTilemap.arrayToCSV(FLOORS, 15), yang mengubah susunan besar kita menjadi format suka Flixel (CSV). Angka di akhir memberi tahu Flixel berapa banyak nilai di setiap baris. Selanjutnya kita beri tahu Flixel gambar mana yang harus digunakan (Assets.FLOORS_TILE - Saya akan menjelaskan tentang apa itu pada langkah selanjutnya). Setelah menentukan ukuran setiap blok dalam gambar, kita memiliki empat nilai lebih untuk lantai daripada untuk dinding. Karena kita ingin semua ubin (termasuk nol) digambar untuk lantai, kita harus memberikan nilai tambahan ini.

Satu-satunya yang sedikit aneh adalah yang terakhir: uint.MAX_VALUE. Setiap nomor ubin (nol melalui jumlah ubin di gambar) yang berada di atas angka yang diteruskan pada parameter ini akan ditandai untuk tabrakan. Segala sesuatu di bawah angka ini akan mengabaikan tabrakan secara default. Jadi, jika Anda memiliki tembok yang bisa dilalui pemain, Anda bisa meletakkannya di akhir gambar Anda (indeks tinggi) dan menggunakan nilai ini untuk membuat Flixel mengabaikan tabrakan. Karena kita tidak pernah ingin tabrakan terjadi dengan lantai, kita menggunakan uint.MAX_VALUE karena setiap indeks ubin akan berada di bawah nilai ini dan karenanya tidak akan ada tabrakan.

Akhirnya, kita harus ingat untuk menambahkan tilemaps kita ke grup atau mereka tidak akan muncul di layar. Sebelum kita dapat menjalankan proyek, kita perlu memuat karya seni kita.


Langkah 6: Loading Aset

Karena menggunakan gambar, kita perlu memberi tahu Flash tentang mereka. Salah satu cara yang lebih mudah untuk melakukan ini adalah dengan menanamkannya di SWF Anda. Inilah cara kita melakukannya dalam proyek ini (ditemukan di tutorial/Assets.as).

1
2
package tutorial
3
{
4
	import flash.utils.ByteArray;
5
	/**

6
	 * Embeds and imports all assets for the game

7
	 * @author Cody Sandahl

8
	 */
9
	public class Assets
10
	{
11
		// sprites

12
		[Embed(source = "../../assets/sprites/ranger (opengameart - Antifarea - ccby30).png")] public static var RANGER_SPRITE:Class;
13
		[Embed(source = "../../assets/sprites/rug1 (opengameart - Redshrike - ccby30).png")] public static var RUG1_SPRITE:Class;
14
		[Embed(source = "../../assets/sprites/rug2 (opengameart - Redshrike - ccby30).png")] public static var RUG2_SPRITE:Class;
15
		[Embed(source = "../../assets/sprites/bookcase (opengameart - Redshrike - ccby30).png")] public static var BOOKCASE_SPRITE:Class;
16
		[Embed(source = "../../assets/sprites/chair_down (opengameart - Redshrike - ccby30).png")] public static var CHAIRDOWN_SPRITE:Class;
17
		[Embed(source = "../../assets/sprites/chair_left (opengameart - Redshrike - ccby30).png")] public static var CHAIRLEFT_SPRITE:Class;
18
		[Embed(source = "../../assets/sprites/chair_right (opengameart - Redshrike - ccby30).png")] public static var CHAIRRIGHT_SPRITE:Class;
19
		[Embed(source = "../../assets/sprites/chair_up (opengameart - Redshrike - ccby30).png")] public static var CHAIRUP_SPRITE:Class;
20
		[Embed(source = "../../assets/sprites/table_round (opengameart - Redshrike - ccby30).png")] public static var TABLEROUND_SPRITE:Class;
21
		[Embed(source = "../../assets/sprites/armor (opengameart - Redshrike - ccby30).png")] public static var ARMOR_SPRITE:Class;
22
		[Embed(source = "../../assets/sprites/bed (opengameart - Redshrike - ccby30).png")] public static var BED_SPRITE:Class;
23
24
		// tiles

25
		[Embed(source = "../../assets/tiles/walls (opengameart - daniel siegmund - ccby30).png")] public static var WALLS_TILE:Class;
26
		[Embed(source = "../../assets/tiles/floor_wood (opengameart - Redshrike - ccby30).png")] public static var FLOORS_TILE:Class;
27
	}
28
}

Saya memberikan Anda semua karya seni sekaligus, karena itu tidak terlalu rumit setelah Anda terbiasa. Mari kita lihat garis-garis yang disorot. Di sini kita memuat dalam dua gambar: satu untuk dinding kita dan satu untuk lantai kita. Jika Anda ingat pada langkah terakhir, kita memberi tahu Flixel untuk menggunakan Assets.WALLS_TILE dan Assets.FLOORS_TILE saat memuat di layer peta. Di sinilah kita mendefinisikan variabel-variabel itu.

Perhatikan bahwa kami menggunakan path relatif ke file Assets.as. Anda juga dapat menyematkan hal-hal seperti file XML, file SWF, dan banyak aset lainnya. Namun, yang kita butuhkan hanyalah gambar. Untuk informasi lebih lanjut tentang menanamkan aset dalam Flash, lihat artikel ini dari blog Nightspade.

Sekarang setelah gambar disematkan dan dapat diakses, kita dapat memberi tahu PlayState.as untuk menggunakan level model baru.

1
2
/**

3
 * Create state

4
 */
5
override public function create():void {
6
    FlxG.mouse.show();
7
    // load level

8
    LEVEL = new IndoorHouseLevel(this, LEVEL_SIZE, BLOCK_SIZE);
9
    this.add(LEVEL);
10
}

Kita mengubah garis yang disorot dari menggunakan TopDownLevel ke menggunakan IndoorHouseLevel baru. Sekarang jika Anda menjalankan proyek, Anda akan melihat sesuatu yang sedikit lebih mirip ruangan.

an indoor room with wooden floors, stone walls, and a red square player in the middlean indoor room with wooden floors, stone walls, and a red square player in the middlean indoor room with wooden floors, stone walls, and a red square player in the middle

Langkah 7: Menambahkan Objek dan Eye Candy

Itu mungkin sebuah ruangan, tapi itu ruangan yang membosankan. Mari kita merapikannya dengan furnitur. Pertama, kita membutuhkan beberapa grup lagi dan beberapa variabel di dalam IndoorHouseLevel.

1
2
/**

3
 * Custom groups

4
 */
5
protected var decalGroup:FlxGroup; // extra decorative elements (no collisions)

6
protected var objectGroup:FlxGroup; // objects and obstacles (with collisions)

7
8
/**

9
 * Game objects

10
 */
11
protected var bookcase:FlxSprite;
12
protected var armor:FlxSprite;
13
protected var table:FlxSprite;
14
protected var bed:FlxSprite;

decalGroup akan memungkinkan kita untuk menambahkan beberapa permadani (permen mata murni visual), sementara objectGroup akan memungkinkan kita untuk menambahkan beberapa furnitur yang akan menghalangi pemain. Variabel lain adalah potongan furnitur yang akan kita tambahkan sebentar lagi.

Selanjutnya, kita perlu menambahkan objek-objek ini ke level. Kita menambahkan garis yang disorot dan semua yang ada di bawahnya.

1
2
/**

3
 * Create the map (walls, decals, etc)

4
 */
5
override protected function createMap():void {
6
    var tiles:FlxTilemap;
7
    // floors

8
    tiles = new FlxTilemap();
9
    tiles.loadMap(
10
        FlxTilemap.arrayToCSV(FLOORS, 15), // convert our array of tile indices to a format flixel understands

11
        Assets.FLOORS_TILE, // image to use

12
        tileSize.x, // width of each tile (in pixels)

13
        tileSize.y, // height of each tile (in pixels)

14
        0, // don't use auto tiling (needed so we can change the rest of these values)

15
        0, // starting index for our tileset (0 = include everything in the image)

16
        0, // starting index for drawing our tileset (0 = every tile is drawn)

17
        uint.MAX_VALUE // which tiles allow collisions by default (uint.MAX_VALUE = no collisions)

18
    );
19
    floorGroup.add(tiles);
20
    // walls

21
    // FFV: make left/right walls' use custom collision rects

22
    tiles = new FlxTilemap();
23
    tiles.loadMap(
24
        FlxTilemap.arrayToCSV(WALLS, 15), // convert our array of tile indices to a format flixel understands

25
        Assets.WALLS_TILE, // image to use

26
        tileSize.x, // width of each tile (in pixels)

27
        tileSize.y // height of each tile (in pixels)

28
    );
29
    wallGroup.add(tiles);
30
    // objects

31
    createObjects();
32
}
33
34
/**

35
 * Add all the objects, obstacles, etc to the level

36
 */
37
protected function createObjects():void {
38
    var sprite:FlxSprite;
39
    // create custom groups

40
    decalGroup = new FlxGroup();
41
    objectGroup = new FlxGroup();
42
    // decals (decorative elements that have no functionality)

43
    sprite = new FlxSprite(
44
        16, // x location

45
        16, // y location

46
        Assets.RUG1_SPRITE // image to use

47
    );
48
    decalGroup.add(sprite);
49
    
50
    sprite = new FlxSprite(
51
        11 * tileSize.x, // x location (using tileSize to align it with a tile)

52
        1.5 * tileSize.y, // y location (showing that you don't need to line up with a tile)

53
        Assets.RUG2_SPRITE // image to use

54
    );
55
    decalGroup.add(sprite);
56
    // objects and obstacles

57
    // NOTE: this group gets tested for collisions

58
    bookcase = new FlxSprite(
59
        32, // x location

60
        0, // y location (showing that you can overlap with the walls if you want)

61
        Assets.BOOKCASE_SPRITE // image to use

62
    );
63
    bookcase.immovable = true; // don't allow the player to move this object

64
    objectGroup.add(bookcase);
65
    
66
    table = new FlxSprite(192, 192, Assets.TABLEROUND_SPRITE);
67
    table.immovable = true;
68
    objectGroup.add(table);
69
    
70
    sprite = new FlxSprite(176, 192, Assets.CHAIRRIGHT_SPRITE);
71
    sprite.immovable = true;
72
    objectGroup.add(sprite);
73
    
74
    sprite = new FlxSprite(216, 192, Assets.CHAIRLEFT_SPRITE);
75
    sprite.immovable = true;
76
    objectGroup.add(sprite);
77
    
78
    armor = new FlxSprite(192, 0, Assets.ARMOR_SPRITE);
79
    armor.immovable = true;
80
    objectGroup.add(armor);
81
    
82
    bed = new FlxSprite(16, 192, Assets.BED_SPRITE);
83
    bed.immovable = true;
84
    objectGroup.add(bed);
85
}

Saya menggunakan fungsi tambahan, createObjects(), hanya untuk membuat hal-hal lebih mudah dibaca. Komentar menjelaskan setiap objek individu, tetapi izinkan saya menawarkan beberapa pengamatan umum. Pertama, kita harus selalu ingat untuk memanggil add() untuk setiap objek atau tidak akan ditampilkan. Selain itu, kita perlu menggunakan grup yang tepat (mapGroup, floorGroup, decalGroup, objectGroup, dll.) Saat memanggil add() atau itu akan mengacaukan pesanan render dan deteksi tabrakan kita.

Juga perhatikan semua berbagai cara kita dapat memutuskan di mana menempatkan objek dan decals kita. Kita dapat mengkode nilai secara keras (seperti yang kita lakukan dengan permadani pertama), kita dapat menggunakan tileSize untuk menyelaraskannya dengan lantai dan ubin dinding (seperti yang kita lakukan dengan permadani kedua), dan kita dapat mencampur dan mencocokkan dengan isi hati kita. Ketahuilah bahwa Flixel tidak akan mendeteksinya jika kita menempatkan sesuatu di luar level atau menindih objek lain - itu mengasumsikan kita tahu apa yang kita lakukan.

Sekarang kita perlu menampilkan grup baru kita dalam urutan yang benar dan menangani tabrakan. Tambahkan fungsi-fungsi ini ke bagian bawah IndoorHouseLevel.

1
2
/**

3
 * Decide the order of the groups. They are rendered in the order they're added, so last added is always on top.

4
 */
5
override protected function addGroups():void {
6
    add(floorGroup);
7
    add(wallGroup);
8
    add(decalGroup);
9
    add(objectGroup);
10
    add(player);
11
    add(guiGroup);
12
}
13
14
/**

15
 * Update each timestep

16
 */
17
override public function update():void {
18
    super.update(); // NOTE: map -> player collision happens in super.update()

19
    FlxG.collide(objectGroup, player);
20
}

Karena kita ingin grup baru di-render di atas lantai dan dinding, kita perlu sepenuhnya melakukan kembali fungsi addGroups() yang dimiliki di TopDownLevel. Kita juga perlu menambahkan deteksi tabrakan untuk furnitur kita di objectGroup. Sekali lagi, karena kita tidak memanggil FlxG.collide() untuk decalGroup, pemain tidak akan terhalang oleh permadani yang mengesankan. Sekarang kamar kita terlihat sedikit kurang kosong.

an indoor room with wooden floors, stone walls, furniture, and a red square player in the middlean indoor room with wooden floors, stone walls, furniture, and a red square player in the middlean indoor room with wooden floors, stone walls, furniture, and a red square player in the middle

Langkah 8: Membuat Pemain Kita

Saya terus berbicara tentang tabrakan, tetapi sulit untuk bertabrakan dengan kotak merah tidak bergerak. Selama tiga langkah berikutnya akan menambahkan kontrol keyboard ke kotak merah kita sebelum akhirnya menjadikannya sprite animasi yang tepat. Mari kita buat tutorial/Player.as.

1
2
package tutorial 
3
{
4
	import org.flixel.*;
5
	import topdown.*;
6
	/**

7
	 * Player-controlled entity

8
	 * @author Cody Sandahl

9
	 */
10
	public class Player extends TopDownEntity
11
	{
12
		/**

13
		 * Constructor

14
		 * @param	X	X location of the entity

15
		 * @param	Y	Y location of the entity

16
		 */
17
		public function Player(X:Number=100, Y:Number=100):void {
18
			super(X, Y);
19
		}
20
	}
21
}

Ini adalah kerangka yang akan digunakan untuk menyempurnakan pemain yang lebih menarik. Sekarang kita memiliki pemutar khusus, kita perlu menggunakannya di IndoorHouseLevel. Tambahkan fungsi ini di akhir kelas.

1
2
/**

3
 * Create the player

4
 */
5
override protected function createPlayer():void {
6
    player = new Player(playerStart.x, playerStart.y);
7
}

Ini berubah dari menggunakan TopDownEntity ke menggunakan Player. Sekarang mari kita buat kotak merah ini bergerak.


Langkah 9: Menambahkan Kontrol Keyboard

Karena kita mungkin ingin entitas selain Player dapat dipindahkan, kita akan menambahkan beberapa fungsionalitas ke TopDownEntity. Ini versi baru.

1
2
package topdown
3
{
4
	import org.flixel.*;
5
	
6
	/**

7
	 * A moveable object in the game (player, enemy, NPC, etc)

8
	 * @author Cody Sandahl

9
	 */
10
	public class TopDownEntity extends FlxSprite
11
	{
12
		/**

13
		 * Constants

14
		 */
15
		public static const SIZE:FlxPoint = new FlxPoint(16, 18); // size in pixels

16
		public static const RUNSPEED:int = 80;
17
		
18
		/**

19
		 * Constructor

20
		 * @param	X	X location of the entity

21
		 * @param	Y	Y location of the entity

22
		 */
23
		public function TopDownEntity(X:Number = 100, Y:Number = 100):void {
24
			super(X, Y);
25
			makeGraphic(SIZE.x, SIZE.y, 0xFFFF0000); // use this if you want a generic box graphic by default

26
			// movement

27
			maxVelocity = new FlxPoint(RUNSPEED, RUNSPEED);
28
			drag = new FlxPoint(RUNSPEED * 4, RUNSPEED * 4); // decelerate to a stop within 1/4 of a second

29
		}
30
		
31
		/**

32
		 * Update each timestep

33
		 */
34
		public override function update():void {
35
			updateControls();
36
			super.update();
37
		}
38
		
39
		/**

40
		 * Check keyboard/mouse controls

41
		 */
42
		protected function updateControls():void {
43
			acceleration.x = acceleration.y = 0; // no gravity or drag by default

44
		}
45
		
46
		/**

47
		 * Move entity left

48
		 */
49
		public function moveLeft():void {
50
			facing = LEFT;
51
			acceleration.x = -RUNSPEED * 4; // accelerate to top speed in 1/4 of a second

52
		}
53
		
54
		/**

55
		 * Move entity right

56
		 */
57
		public function moveRight():void {
58
			facing = RIGHT;
59
			acceleration.x = RUNSPEED * 4; // accelerate to top speed in 1/4 of a second

60
		}
61
		
62
		/**

63
		 * Move entity up

64
		 */
65
		public function moveUp():void {
66
			facing = UP;
67
			acceleration.y = -RUNSPEED * 4; // accelerate to top speed in 1/4 of a second

68
		}
69
		
70
		/**

71
		 * Move playe rdown

72
		 */
73
		public function moveDown():void {
74
			facing = DOWN;
75
			acceleration.y = RUNSPEED * 4; // accelerate to top speed in 1/4 of a second

76
		}
77
	}
78
}

Kita telah menambahkan konstanta baru, RUNSPEED, yang menentukan seberapa cepat entitas bergerak. Lalu kita atur maxVelocity dan drag (perlambatan) di konstruktor. Setelah itu, kita memanggil updateControls() setiap frame sehingga dapat memeriksa keyboard, mouse, atau AI (tergantung kebutuhan). Akhirnya, kita menambahkan beberapa fungsi pembantu untuk bergerak di setiap arah. Perhatikan bahwa kita memperbarui facing pada masing-masingnya. Ini adalah cara praktis untuk mengetahui animasi mana yang akan digunakan nanti.

Sekarang kita harus benar-benar menggunakan keyboard di dalam Player. Tambahkan fungsi ini setelah konstruktor.

1
2
/**

3
 * Check for user input to control this entity

4
 */
5
override protected function updateControls():void {
6
    super.updateControls();
7
    // check keys

8
    // NOTE: this accounts for someone pressing multiple arrow keys at the same time (even in opposite directions)

9
    var movement:FlxPoint = new FlxPoint();
10
    if (FlxG.keys.pressed("LEFT"))
11
        movement.x -= 1;
12
    if (FlxG.keys.pressed("RIGHT"))
13
        movement.x += 1;
14
    if (FlxG.keys.pressed("UP"))
15
        movement.y -= 1;
16
    if (FlxG.keys.pressed("DOWN"))
17
        movement.y += 1;
18
    // check final movement direction

19
    if (movement.x < 0)
20
        moveLeft();
21
    else if (movement.x > 0)
22
        moveRight();
23
    if (movement.y < 0)
24
        moveUp();
25
    else if (movement.y > 0)
26
        moveDown();
27
}

Jadi setiap frame kita periksa tombol apa yang ditekan. Flixel memungkinkan kita untuk menguji kunci dengan berbagai cara. Di sini kita menggunakan pressed(), yang true selama kunci ditekan. Jika kita menggunakan justPressed(), itu hanya akan menjadi true segera setelah pemain menekan tombol, bahkan jika tombol ditekan setelah itu. Itu akan terbalik jika kami menggunakan justReleased().

Seperti yang saya nyatakan dalam komentar, saya ingin menangani kasus di mana pengguna menekan kiri dan kanan (misalnya) secara bersamaan dengan tidak bergerak. Gerakan incrementing atau decrementing movement.x berdasarkan yang ditekan panah memungkinkan kita untuk melakukan itu karena movement.x akan menjadi nol jika kedua kiri dan kanan sedang ditekan.

Jika Anda menjalankan proyek sekarang, Anda harus dapat memindahkan kotak merah di sekitar dengan tombol panah dan melihat tabrakan terjadi di antara kotak, dan dinding atau furnitur (tetapi bukan karpet).

Anda mungkin akan melihat bahwa kotak itu tidak mengarah ke dinding kiri dan kanan. Ini adalah aspek yang lebih esoterik dari Flixel. Flixel menggunakan deteksi tabrakan yang agak sederhana (tetapi memungkinkan kita untuk membuatnya lebih rumit jika kita mau). Karena gambar yang digunakan untuk semua dinding memiliki ukuran yang sama (16x16), Flixel menggunakan 16x16 sebagai ukuran tumbukan meskipun sebagian besar gambar dinding kiri dan kanan transparan. Memperbaiki perilaku itu berada di luar cakupan tutorial ini, tetapi hal itu dapat dilakukan.

an indoor room with wooden floors, stone walls, furniture, and a red square player colliding with a table in the corneran indoor room with wooden floors, stone walls, furniture, and a red square player colliding with a table in the corneran indoor room with wooden floors, stone walls, furniture, and a red square player colliding with a table in the corner

Langkah 10: Menambahkan Animasi

Saya berjanji kita tidak akan bertahan dengan kotak merah (meskipun menawan), jadi di sini kita pergi dengan sprite animasi. Karena kita mungkin menginginkan kemampuan untuk menghidupkan entitas yang akan datang, kita akan menambahkan fungsionalitas dasar ke TopDownEntity alih-alih Player. Berikut adalah fungsi konstruktor, createAnimation(), dan update() baru untuk TopDownEntity.

1
2
/**

3
 * Constructor

4
 * @param	X	X location of the entity

5
 * @param	Y	Y location of the entity

6
 */
7
public function TopDownEntity(X:Number = 100, Y:Number = 100):void {
8
    super(X, Y);
9
    makeGraphic(SIZE.x, SIZE.y, 0xFFFF0000); // use this if you want a generic box graphic by default

10
    // movement

11
    maxVelocity = new FlxPoint(RUNSPEED, RUNSPEED);
12
    drag = new FlxPoint(RUNSPEED * 4, RUNSPEED * 4); // decelerate to a stop within 1/4 of a second

13
    // animations

14
    createAnimations();
15
}
16
17
/**

18
 * Create the animations for this entity

19
 * NOTE: these will be different if your art is different

20
 */
21
protected function createAnimations():void {
22
    addAnimation("idle_up", [1]);
23
    addAnimation("idle_right", [5]);
24
    addAnimation("idle_down", [9]);
25
    addAnimation("idle_left", [13]);
26
    addAnimation("walk_up", [0, 1, 2], 12); // 12 = frames per second for this animation

27
    addAnimation("walk_right", [4, 5, 6], 12);
28
    addAnimation("walk_down", [8, 9, 10], 12);
29
    addAnimation("walk_left", [12, 13, 14], 12);
30
    addAnimation("attack_up", [16, 17, 18, 19], 12, false); // false = don't loop the animation

31
    addAnimation("attack_right", [20, 21, 22, 23], 12, false);
32
    addAnimation("attack_down", [24, 25, 26, 27], 12, false);
33
    addAnimation("attack_left", [28, 29, 30, 31], 12, false);
34
}
35
36
/**

37
 * Update each timestep

38
 */
39
public override function update():void {
40
    updateControls();
41
    updateAnimations();
42
    super.update();
43
}

FlxSprite mengasumsikan bahwa, jika kita menghidupkan, kita memiliki banyak bingkai animasi yang disimpan dalam satu gambar (disebut sprite sheet). Meskipun kotak merah tidak memiliki bingkai untuk dianimasikan, karya seni yang akan digunakan tidak. Jika Anda menggunakan karya seni yang diatur secara berbeda di gim Anda sendiri, Anda perlu mengubah nomor bingkai ini. Selain itu, jika Anda melihat animasi yang menganggur, Anda akan melihat bahwa kita harus memasukkan array indeks bingkai walaupun kita hanya memiliki satu.

Inilah lembar sprite ranger yang akan digunakan untuk pemain supaya Anda bisa melihat lebih jelas.

all the animations used by the ranger, with frame numbers labeled

Perhatikan bahwa kita menyertakan beberapa bingkai kosong dalam lembar sprite. Ini terutama untuk kenyamanan dalam mengedit animasi karena animasi bawah (menyerang) memiliki satu bingkai lebih dari empat animasi teratas kita (berjalan). Perhatikan juga bahwa kita menggunakan bingkai tengah dari masing-masing animasi berjalan sebagai animasi idle kita.

Selama semua entitas kita dalam game menggunakan nomor bingkai yang sama ini, tidak perlu mengubah kode animasi. Jika karya seni kita menggunakan jumlah frame yang berbeda untuk menghidupkan dan menyerang, kita akan memperbarui frame yang dilewatkan ke addAnimation(). Karena animasi berada dalam fungsinya sendiri - createAnimations(), Anda juga dapat mengganti fungsi ini untuk membuat beberapa entitas memiliki animasi yang berbeda dari yang lain.

Kita juga membuat fungsi baru lainnya untuk menampilkan animasi yang tepat: updateAnimations().

1
2
/**

3
 * Based on current state, show the correct animation

4
 * FFV: use state machine if it gets more complex than this

5
 */
6
protected function updateAnimations():void {
7
    // use abs() so that we can animate for the dominant motion

8
    // ex: if we're moving slightly up and largely right, animate right

9
    var absX:Number = Math.abs(velocity.x);
10
    var absY:Number = Math.abs(velocity.y);
11
    // determine facing

12
    if (velocity.y < 0 && absY >= absX)
13
        facing = UP;
14
    else if (velocity.y > 0 && absY >= absX)
15
        facing = DOWN;
16
    else if (velocity.x > 0 && absX >= absY)
17
        facing = RIGHT;
18
    else if (velocity.x < 0 && absX >= absY)
19
        facing = LEFT
20
    // up

21
    if (facing == UP) {
22
        if (velocity.y != 0 || velocity.x != 0)
23
            play("walk_up");
24
        else
25
            play("idle_up");
26
    }
27
    // down

28
    else if (facing == DOWN) {
29
        if (velocity.y != 0 || velocity.x != 0)
30
            play("walk_down");
31
        else
32
            play("idle_down");
33
    }
34
    // right

35
    else if (facing == RIGHT) {
36
        if (velocity.x != 0)
37
            play("walk_right");
38
        else
39
            play("idle_right");
40
    }
41
    // left

42
    else if (facing == LEFT) {
43
        if (velocity.x != 0)
44
            play("walk_left");
45
        else
46
            play("idle_left");
47
    }
48
}

Ini lebih sulit daripada rumit. Pada dasarnya kita menghitung berapa banyak kita bergerak secara vertikal dan horizontal. Mana pun yang memiliki lebih banyak gerakan, kita menggunakan animasi arah itu. Ini akan berperan jika Anda bergerak pada suatu sudut dan tiba-tiba menabrak sesuatu. Arah mana pun Anda masih bisa bergerak akan menentukan animasi yang digunakan.

Kita hanya punya satu hal lagi yang harus dilakukan sebelum kita akhirnya bisa mengeluarkan kotak merah dari kesengsaraannya. Kita perlu memberitahu Player untuk menggunakan sprite sheet ranger.

1
2
/**

3
 * Constructor

4
 * @param	X	X location of the entity

5
 * @param	Y	Y location of the entity

6
 */
7
public function Player(X:Number=100, Y:Number=100):void {
8
    super(X, Y);
9
    loadGraphic(
10
        Assets.RANGER_SPRITE, // image to use

11
        true, // animated

12
        false, // don't generate "flipped" images since they're already in the image

13
        TopDownEntity.SIZE.x, // width of each frame (in pixels)

14
        TopDownEntity.SIZE.y // height of each frame (in pixels)

15
    );
16
}

Sekali lagi kita akan pergi ke kelas Assets kita untuk menarik gambar yang kita inginkan. Komentar memberi tahu Anda apa yang terjadi, tetapi izinkan saya memberi tahu Anda sedikit tentang "membalik" gambar. Alih-alih menghasilkan animasi yang berbeda saat bepergian ke kiri/kanan dan atas/bawah, Flixel cukup membalik animasi "kanan" untuk menjadikannya "kiri" dan membalik animasi "atas" untuk membuatnya "turun" (atau sebaliknya). Animasi "atas" dan "turun" kita terlihat sangat berbeda (dan sudah memiliki karya seni dengan semua arah), jadi kita memberi tahu Flixel untuk tidak repot-repot membalik animasi.

Sekarang kita memiliki level RPG top-down indoor sejati!

an indoor room with wooden floors, stone walls, furniture, and an animated ranger player running down the left sidean indoor room with wooden floors, stone walls, furniture, and an animated ranger player running down the left sidean indoor room with wooden floors, stone walls, furniture, and an animated ranger player running down the left side

Langkah 11: Menambahkan GUI

Sebagai bonus, mari kita lihat bagaimana cara menambahkan elemen GUI ke layar. Kita akan menambahkan serangkaian instruksi sederhana di bagian atas sehingga pengguna tahu apa yang harus dilakukan setelah mereka memuat level ini. Mari tambahkan GUI ke IndoorHouseLevel.

1
2
/**

3
 * Create text, buttons, indicators, etc

4
 */
5
override protected function createGUI():void {
6
    var instructions:FlxText = new FlxText(0, 0, levelSize.x, "Use ARROW keys to walk around");
7
    instructions.alignment = "center";
8
    guiGroup.add(instructions);
9
}

Ini menambahkan area teks di bagian atas layar yang selebar level dan menggunakan perataan center. Seperti biasa, harus ditambahkan ke grup yang tepat agar dapat muncul.


Kesimpulan

Sekarang Anda memiliki semua yang Anda butuhkan untuk membuat level RPG top-down Anda sendiri. Terima kasih untuk tetap bersama saya dan buat sesuatu yang menyenangkan!

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.