Advertisement
  1. Game Development
  2. Phaser

Memulai Dengan Phaser: Membangun 'Monster Wants Candy'

Scroll to top
Read Time: 23 min

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

Dalam tutorial ekstra panjang ini, saya akan menjabarkan sumber untuk Monster Wants Candy, sebuah gim multi-platform yang saya dan rekan saya buat dengan Phaser, mesin gim HTML5. Dengan cara ini, anda akan mendapatkan pengantar praktis tentang mesin, dan akan mempelajari konsep yang dapat anda gunakan untuk membuat game seluler dan browser HTML5 anda sendiri.

Pengenalan

Jika anda ingin membuat game HTML5, ada baiknya memilih kerangka kerja atau mesin. Anda tentu saja dapat melakukannya dalam JavaScript biasa, tetapi menggunakan kerangka kerja sangat mempercepat pengembangan, dan menangani hal-hal yang tidak begitu menarik tetapi harus dilakukan.

Phaser adalah kerangka pengembangan game HTML5 baru yang panas dengan komunitas khusus, jadi jika anda belum pernah mendengarnya sebelumnya, Anda pasti harus mencobanya!

Postingan Terkait

Apa itu Phaser?

Phaser adalah kerangka kerja untuk membangun game HTML5 desktop dan mobile. Itu dibuat oleh Photon Storm. Kerangka kerja ini ditulis dalam JavaScript murni, tetapi juga berisi file definisi TypeScript, jika anda suka itu.

Kode Phaser sangat didasarkan pada platform Flixel Flash gamedev, sehingga pengembang Flash dapat merasa seperti di rumah. Di bawah tenda, ia menggunakan mesin Pixi.js untuk menangani render segala sesuatu di layar menggunakan Canvas, atau WebGL jika memungkinkan.

Phaser logoPhaser logoPhaser logo

Ini cukup baru, tetapi berkembang pesat dengan bantuan komunitas aktif di forum HTML5GameDevs. Sudah ada banyak tutorial dan artikel yang tersedia, dan anda juga dapat memeriksa dokumentasi resmi, dan banyak koleksi contoh yang dapat sangat membantu selama pengembangan. Ini bersumber terbuka dan tersedia secara bebas di GitHub, sehingga anda dapat langsung terjun ke kode sumber dan belajar darinya.

Bangunan stabil terbaru Phaser, pada saat penulisan adalah versi 2.0.7.

Apa itu Monster Wants Candy?

Ketika saya mulai mengerjakan sebuah game, saya memikirkan ide inti terlebih dahulu dan mencoba dengan cepat membuat prototipe yang berfungsi. Dalam studi kasus ini, kami mulai dengan demo permainan yang cukup sederhana bernama Monster Wants Candy.

Alih-alih bekerja dari prototipe, saya akan menunjukkan kepada anda struktur proyek terlebih dahulu, sehingga anda dapat memahami keseluruhan gagasan. Kami kemudian akan melalui langkah-langkah kronologis game kami: dari memuat aset hingga membuat menu utama dan loop game yang sebenarnya. Anda dapat melihat demo Monster Wants Candy sekarang untuk melihat apa yang akan kami kerjakan bersama.

Pengkodean ditangani oleh Andrzej Mazur dari Enclave Games (itu saya!) dan semua aset grafis dibuat oleh Robert Podgórski dari Blackmoon Design.

Monster Wants Candy demoMonster Wants Candy demoMonster Wants Candy demo

Kisah Monster Wants Candy itu sederhana: seorang raja jahat telah menculik cintamu dan kamu harus mengumpulkan cukup banyak permen untuk mendapatkannya kembali. Cara bermainnya juga sederhana: manisannya jatuh dan anda bisa mengetuknya untuk memakannya. Semakin banyak poin yang anda dapatkan dari memakan permen, semakin baik. Jika anda kehilangan apapun dan mereka jatuh dari layar, anda akan kehilangan nyawa dan permainan akan berakhir.

Seperti yang anda lihat, ini adalah permainan yang sangat sederhana, tetapi strukturnya lengkap. Anda akan menemukan bahwa penggunaan kerangka kerja yang paling penting adalah untuk tugas-tugas seperti memuat gambar, merender sprite, dan mendeteksi aktivitas pengguna. Ini juga merupakan titik awal yang baik untuk menyalin kode, mulai mengutak-atiknya, dan membangun permainan anda sendiri.

Pengaturan dan Struktur Proyek

Anda dapat membaca artikel praktis ini dari pembuat kerangka sendiri tentang cara memulai Phaser, atau anda dapat menyalin file phaser.min.js dari repo GitHub ke direktori proyek anda dan mulai bekerja dari awal. Anda tidak memerlukan IDE — anda cukup meluncurkan file index.html di browser anda dan langsung melihat perubahan yang anda buat pada kode sumber.

Folder proyek kami berisi file index.html yang mencakup struktur HTML5 dan semua file JavaScript yang diperlukan. Ada dua subfolder: img yang menyimpan semua aset grafik kami dan src yang menyimpan kode sumber permainan.

Berikut tampilan struktur folder:

Folder structure for Monster Wants Candy Phaser projectFolder structure for Monster Wants Candy Phaser projectFolder structure for Monster Wants Candy Phaser project

Di folder src, Anda akan menemukan file JavaScript — di sinilah keajaiban terjadi. Dalam tutorial ini, saya akan menjelaskan tujuan dan konten setiap file di folder itu.

Anda dapat melihat sumber untuk setiap file di repo GitHub untuk tutorial ini.

Index.html

Mari kita mulai dengan file index.html. Itu terlihat seperti situs web HTML5 dasar, tetapi alih-alih menambahkan teks dan banyak elemen HTML, kami menginisialisasi kerangka kerja Phaser, yang akan merender semuanya menjadi elemen Canvas.

1
<!DOCTYPE html>
2
<html>
3
<head>
4
  <meta charset="utf-8" />
5
	<title>Monster Wants Candy demo</title>
6
	<style> body { margin: 0; background: #B4D9E7; } </style>
7
	<script src="src/phaser.min.js"></script>
8
	<script src="src/Boot.js"></script>
9
	<script src="src/Preloader.js"></script>
10
	<script src="src/MainMenu.js"></script>
11
	<script src="src/Game.js"></script>
12
</head>
13
<body>
14
<script>
15
(function() {
16
	var game = new Phaser.Game(640, 960, Phaser.AUTO, 'game');
17
	game.state.add('Boot', Candy.Boot);
18
	game.state.add('Preloader', Candy.Preloader);
19
	game.state.add('MainMenu', Candy.MainMenu);
20
	game.state.add('Game', Candy.Game);
21
	game.state.start('Boot');
22
})();
23
</script>
24
</body>
25
</html>

Kami mendefinisikan struktur biasa dokumen HTML dengan doctype dan beberapa informasi <head> : pengkodean charset, judul halaman, dan gaya CSS. Biasanya, kami akan mereferensikan file CSS eksternal di mana kami menempatkan semua styling, tetapi kami tidak membutuhkannya di sini — seperti yang saya sebutkan sebelumnya, semuanya akan dirender pada elemen Canvas, jadi kami tidak akan memiliki elemen HTML untuk gaya. .

Hal terakhir yang harus dilakukan adalah memasukkan semua file JavaScript kami: dari file phaser.min.js dengan kode sumber kerangka Phaser, ke semua file kami sendiri yang berisi kode permainan. Adalah baik untuk mengurangi jumlah permintaan di browser dengan menggabungkan semua file JavaScript menjadi satu, sehingga permainan anda dimuat lebih cepat, tetapi untuk tujuan tutorial ini, kami hanya akan memuatnya secara terpisah.

Mari kita pindah ke isi tag <body>, tempat kita menginisialisasi kerangka kerja dan memulai permainan kita. Kode berada di dalam fungsi memanggil dirinya sendiri; baris pertama terlihat seperti ini:

1
var game = new Phaser.Game(640, 960, Phaser.AUTO, 'game');

Kode ini akan menginisialisasi Phaser dengan beberapa default:

640 adalah lebar kanvas permainan dalam piksel dan 960 adalah tinggi permainan.

Phaser.AUTO memberi tahu kerangka kerja bagaimana kami ingin permainan kami dirender ke Kanvas. Ada tiga opsi: CANVAS, WEBGL dan AUTO. Yang pertama menjalankan permainan kami dalam konteks 2D Kanvas; yang kedua menggunakan WebGL untuk membuatnya di mana mungkin (kebanyakan desktop sekarang, tetapi dukungan seluler semakin baik); dan yang ketiga menyerahkan keputusan ini ke kerangka kerja yang akan memeriksa apakah WebGL didukung dan memutuskan apakah permainan dapat di-render dalam konteks ini — jika tidak bisa, maka rendering 2D Kanvas akan digunakan.

Inisialisasi kerangka kerja akan ditugaskan ke objek tunggal bernama game, yang akan kita gunakan saat mereferensikan kejadian Phaser.

Baris selanjutnya adalah tentang menambahkan status ke permainan kami:

1
game.state.add('Boot', Candy.Boot);

'Boot' adalah nama negara dan Candy.Boot adalah objek (didefinisikan pada langkah-langkah selanjutnya) yang akan dieksekusi ketika kita memulai keadaan itu. Kami menambahkan status untuk Boot (konfigurasi), Preloader (memuat aset), MainMenu (Anda dapat menebaknya; menu utama game kami) dan Game (loop utama game). Baris terakhir, game.state.start ('Boot'), memulai status Boot, sehingga fungsi yang tepat dari objek Candy.Boot akan dieksekusi.

Seperti yang anda dapat lihat, ada satu objek game JavaScript utama yang dibuat, dengan banyak objek lain yang ditugaskan untuk tujuan khusus. Di game kami, kami memiliki objek Boot, Preloader, MainMenu, dan Game yang akan menjadi status game kami, dan kami mendefinisikannya dengan menggunakan prototipe mereka. Ada beberapa nama fungsi khusus di dalam objek yang disediakan untuk kerangka itu sendiri (preload (), create (), update (), dan render ()), tetapi kita juga dapat mendefinisikan kita sendiri (startGame (), spawnCandy (), managePause ()). Jika anda tidak yakin anda memahami semua ini, maka jangan khawatir — saya akan menjelaskan semuanya menggunakan contoh kode nanti.

Permainan

Mari kita lupakan status Boot, Preloader dan MainMenu untuk saat ini. Mereka akan dijelaskan secara rinci nanti; yang harus anda ketahui saat ini adalah bahwa kondisi Boot akan mengurus konfigurasi dasar permainan, Preloader akan memuat semua aset grafis, dan MainMenu akan menunjukkan layar yang memungkinkan anda memulai permainan.

Mari kita fokus pada permainan itu sendiri dan melihat bagaimana status kode Game terlihat. Namun, sebelum kita membahas seluruh kode Game.js, mari kita bicara tentang konsep permainan itu sendiri dan bagian terpenting dari logika dari sudut pandang pengembang.

Mode Potrait

Permainan dimainkan dalam mode potret, artinya pemain memegang ponsel mereka secara vertikal untuk memainkannya.

Rotate device promptRotate device promptRotate device prompt

Dalam mode ini, tinggi layar lebih besar dari lebarnya — berbeda dengan mode lansekap, di mana lebar layar lebih besar daripada tingginya. Ada beberapa jenis permainan yang bekerja lebih baik dalam mode potrait (seperti Monster Wants Candy), jenis permainan yang bekerja lebih baik dalam mode landscape (termasuk permainan platformer seperti Craigen), dan bahkan beberapa jenis permainan yang bekerja di kedua mode tersebut, meskipun biasanya jauh lebih sulit untuk membuatnya kode game tersebut.

Game.js

Sebelum kita melihat kode sumber file game.js, mari kita bicara tentang strukturnya. Ada dunia yang diciptakan untuk kita, dan ada karakter pemain di dalam yang tugasnya adalah merebut permen.

Dunia permainan: Dunia di belakang monster itu statis. Ada gambar Candyland di latar belakang, kita bisa melihat monster di latar depan, dan ada juga antarmuka pengguna.

Karakter pemain: Demo ini sengaja sangat sederhana dan dasar, jadi monster kecil itu tidak melakukan apa-apa selain menunggu permen. Tugas utama pemain adalah mengumpulkan permen.

Permen: Mekanik inti permainan ini adalah menangkap permen sebanyak mungkin. Permen muncul di tepi atas layar, dan pemain harus mengetuk (atau mengklik) permen tersebut saat jatuh. Jika ada permen yang jatuh dari bagian bawah layar, itu dihapus, dan karakter pemain menerima kerusakan. Kami tidak memiliki sistem nyawa yang diimplementasikan, jadi setelah itu permainan langsung berakhir dan pesan yang sesuai ditampilkan.

Oke, mari kita lihat struktur kode file Game.js kita sekarang:

1
Candy.Game = function(game) {
2
	// ...

3
};
4
Candy.Game.prototype = {
5
	create: function() {
6
		// ...

7
	},
8
	managePause: function() {
9
		// ...

10
	},
11
	update: function() {
12
		// ...

13
	}
14
};
15
Candy.item = {
16
	spawnCandy: function(game) {
17
		// ...

18
	},
19
	clickCandy: function(candy) {
20
		// ...

21
	},
22
	removeCandy: function(candy) {
23
		// ...

24
	}
25
};

Ada tiga fungsi yang didefinisikan dalam prototipe Candy.Game:

  • create () menangani inisialisasi
  • managePause () menjeda dan membatalkan permainan
  • update () mengelola loop game utama dengan setiap centang

Kami akan membuat objek berguna yang disebut item untuk mewakili satu permen. Ini akan memiliki beberapa metode yang berguna:

  • spawnCandy () menambahkan permen baru ke dunia game
  • clickCandy () dipecat ketika pengguna mengklik atau mengetuk permen
  • removeCandy () menghapusnya

Mari kita lewati mereka:

1
Candy.Game = function(game) {
2
	this._player = null;
3
	this._candyGroup = null;
4
	this._spawnCandyTimer = 0;
5
	this._fontStyle = null;
6
	Candy._scoreText = null;
7
	Candy._score = 0;
8
	Candy._health = 0;
9
};

Di sini, kami menyiapkan semua variabel yang akan kami gunakan nanti dalam kode.

Dengan mendefinisikan this._name, kami membatasi penggunaan variabel ke ruang lingkup Candy.Game, yang berarti mereka tidak dapat digunakan di negara-negara lain — kita tidak membutuhkannya di sana, jadi mengapa memaparkannya?

Dengan mendefinisikan Candy._name, kami mengizinkan penggunaan variabel-variabel itu di negara bagian dan objek lain, jadi, misalnya, Candy._score dapat ditingkatkan dari Candy.item.clickCandy () fungsi.

Objek diinisialisasi ke null, dan variabel yang kita butuhkan untuk perhitungan diinisialisasi dengan nol.

Kita dapat beralih ke konten Candy.Game.prototype:

1
create: function() {
2
	this.physics.startSystem(Phaser.Physics.ARCADE);
3
	this.physics.arcade.gravity.y = 200;
4
5
	this.add.sprite(0, 0, 'background');
6
	this.add.sprite(-30, Candy.GAME_HEIGHT-160, 'floor');
7
	this.add.sprite(10, 5, 'score-bg');
8
	this.add.button(Candy.GAME_WIDTH-96-10, 5, 'button-pause', this.managePause, this);
9
10
	this._player = this.add.sprite(5, 760, 'monster-idle');
11
	this._player.animations.add('idle', [0,1,2,3,4,5,6,7,8,9,10,11,12], 10, true);
12
	this._player.animations.play('idle');
13
14
	this._spawnCandyTimer = 0;
15
	Candy._health = 10;
16
17
	this._fontStyle = { font: "40px Arial", fill: "#FFCC00", stroke: "#333", strokeThickness: 5, align: "center" };
18
	Candy._scoreText = this.add.text(120, 20, "0", this._fontStyle);
19
20
	this._candyGroup = this.add.group();
21
	Candy.item.spawnCandy(this);
22
},

Pada awal fungsi create (), kami mengatur sistem fisika ARCADE — ada beberapa yang tersedia di Phaser, tetapi ini adalah yang paling sederhana. Setelah itu, kami menambahkan gravitasi vertikal ke permainan. Lalu kami menambahkan tiga gambar: latar belakang, lantai tempat monster itu berdiri, dan latar belakang skor UI. Item keempat yang kami tambahkan adalah tombol Pause, Perhatikan bahwa kami menggunakan variabel Candy.GAME_WIDTH dan Candy.GAME_HEIGHT, yang didefinisikan dalam Candy.Preloader () tetapi tersedia di seluruh kode game.

Lalu kami membuat monster, avatar pemain. Itu adalah bidadari dengan bingkai — spritesheet. Agar terlihat seperti dia berdiri dan bernapas dengan tenang, kita dapat menghidupkannya.

Fungsi animations.add () membuat animasi dari bingkai yang tersedia, dan fungsi tersebut mengambil empat parameter:

  • nama animasi (sehingga kami dapat referensi nanti)
  • tabel dengan semua bingkai yang ingin kita gunakan (kita hanya bisa menggunakan beberapa di antaranya jika kita mau)
  • framerate
  • sebuah bendera untuk menentukan apakah akan mengulang animasi dan memutarnya tanpa batas.

Jika kita ingin memulai animasi kita, kita harus menggunakan animations.play () berfungsi dengan nama yang ditentukan.

Kami kemudian mengatur spawnCandyTimer ke 0 (bersiap-siap untuk menghitung) dan health monster menjadi 10.

Gaya Teks

Dua baris berikutnya mari kita tunjukkan beberapa teks di layar. this.add.text () fungsi mengambil empat parameter: posisi absolut kiri dan atas pada layar, string teks aktual dan objek konfigurasi. Kita dapat memformat teks sesuai dengan menggunakan sintaks seperti CSS di objek konfigurasi itu.

Konfigurasi untuk font kami terlihat seperti ini:

1
this._fontStyle = { 
2
    font: "40px Arial", 
3
    fill: "#FFCC00", 
4
    stroke: "#333", 
5
    strokeThickness: 5, 
6
    align: "center" 
7
};

Dalam hal ini, fontnya adalah Arial, tingginya 40 piksel, warnanya kuning, ada goresan yang ditentukan (dengan warna dan ketebalan), dan teksnya rata tengah.

Setelah itu, kita mendefinisikan candyGroup dan menelurkan permen pertama.

Menjeda Game

Fungsi jeda terlihat seperti ini

1
managePause: function() {
2
	this.game.paused = true;
3
	var pausedText = this.add.text(100, 250, "Game paused.\nTap anywhere to continue.", this._fontStyle);
4
	this.input.onDown.add(function(){
5
		pausedText.destroy();
6
		this.game.paused = false;
7
	}, this);
8
},

Kami mengubah status this.game.paused untuk true setiap kali tombol jeda diklik, menampilkan prompt yang sesuai untuk pemain, dan mengatur pendengar acara untuk klik pemain atau mengetuk layar. Ketika klik atau ketuk itu terdeteksi, kami menghapus teks dan mengatur this.game.paused menjadi false.

Variabel yang paused dalam objek game adalah spesial di Phaser, karena menghentikan setiap animasi atau kalkulasi dalam game, jadi semuanya dibekukan hingga kami membatalkan permainan dengan mengaturnya menjadi false.

Loop Pembaruan

Nama fungsi update() adalah salah satu kata yang disimpan di Phaser. Saat Anda menulis fungsi dengan nama itu, itu akan dieksekusi pada setiap frame permainan Anda dapat mengelola perhitungan di dalamnya berdasarkan berbagai kondisi.

1
update: function() {
2
	this._spawnCandyTimer += this.time.elapsed;
3
	if(this._spawnCandyTimer > 1000) {
4
		this._spawnCandyTimer = 0;
5
		Candy.item.spawnCandy(this);
6
	}
7
	this._candyGroup.forEach(function(candy){
8
		candy.angle += candy.rotateMe;
9
	});
10
	if(!Candy._health) {
11
		this.add.sprite((Candy.GAME_WIDTH-594)/2, (Candy.GAME_HEIGHT-271)/2, 'game-over');
12
		this.game.paused = true;
13
	}
14
}

Setiap kutu di dunia gim, kami menambahkan waktu yang telah berlalu sejak kutu sebelumnya ke variabel spawnCandyTimer untuk melacaknya. Pernyataan if memeriksa apakah sudah waktunya untuk mengatur ulang timer dan memunculkan permen baru ke dunia game — kami melakukan ini setiap detik (yaitu, setiap kali kami melihat bahwa spawnCandyTimer telah melewati 1000 milidetik). Kemudian, kita beralih melalui grup permen dengan semua objek permen di dalamnya (kita bisa memiliki lebih dari satu di layar) menggunakan forEach, dan menambahkan jumlah tetap (disimpan dalam nilai rotateMe objek permen) ke variabel angle permen, sehingga mereka masing-masing berputar pada kecepatan tetap ini sambil jatuh. Hal terakhir yang kami lakukan adalah memeriksa apakah health telah turun ke 0 — jika demikian, maka kami menampilkan game dari layar dan menghentikan game.

Mengelola Acara Permen

Untuk memisahkan logika permen dari Game utama, kami menggunakan objek bernama item yang berisi fungsi yang akan kami gunakan: spawnCandy (), klikCandy () dan removeCandy (). Kami menyimpan beberapa variabel yang terkait dengan permen di objek Game agar lebih mudah digunakan, sementara yang lain hanya ditentukan dalam fungsi item untuk kemudahan perawatan yang lebih baik.

1
spawnCandy: function() {
2
	var dropPos = Math.floor(Math.random()*Candy.GAME_WIDTH);
3
	var dropOffset = [-27,-36,-36,-38,-48];
4
	var candyType = Math.floor(Math.random()*5);
5
	var candy = game.add.sprite(dropPos, dropOffset[candyType], 'candy');
6
	candy.animations.add('anim', [candyType], 10, true);
7
    candy.animations.play('anim');
8
9
	game.physics.enable(candy, Phaser.Physics.ARCADE);
10
	candy.inputEnabled = true;
11
	candy.events.onInputDown.add(this.clickCandy, this);
12
13
	candy.checkWorldBounds = true;
14
	candy.events.onOutOfBounds.add(this.removeCandy, this);
15
	candy.anchor.setTo(0.5, 0.5);
16
	candy.rotateMe = (Math.random()*4)-2;
17
	game._candyGroup.add(candy);
18
},

Fungsi dimulai dengan mendefinisikan tiga nilai:

  • koordinat x acak untuk menjatuhkan permen dari (antara nol dan lebar Kanvas)
  • koordinat y untuk menjatuhkan permen, berdasarkan tingginya (yang akan ditentukan kemudian berdasarkan jenis permennya)
  • jenis permen acak (kami memiliki lima gambar berbeda untuk digunakan)

Kami kemudian menambahkan satu permen sebagai sprite, dengan posisi awal dan gambar seperti yang ditentukan di atas. Hal terakhir yang kami lakukan di blok ini adalah menetapkan bingkai animasi baru yang akan digunakan saat permen muncul.

Selanjutnya, kami mengaktifkan tubuh permen untuk mesin fisika, sehingga dapat jatuh secara alami dari atas layar ketika gravitasi diatur. Kemudian, kami mengaktifkan input pada permen untuk diklik atau disadap, dan mengatur pendengar acara untuk tindakan itu.

Untuk memastikan bahwa permen akan memunculkan suatu peristiwa ketika meninggalkan batas layar yang kita atur checkWorldBounds menjadi true. events.onOutOfBounds () adalah fungsi yang akan dipanggil ketika permen kami keluar dari layar; kami membuatnya memanggil removeCandy () pada gilirannya. Mengatur jangkar ke permen kami di tengah yang tepat memungkinkan kami memutar di sekitar porosnya, sehingga akan berputar secara alami. Kami mengatur variabel rotateMe di sini sehingga kami dapat menggunakannya dalam loop update () untuk memutar permen; kami memilih nilai antara -2 dan 2. Baris terakhir menambahkan permen kami yang baru dibuat ke grup permen, sehingga kami dapat mengulanginya.

Mari beralih ke fungsi selanjutnya, clickCandy ():

1
clickCandy: function(candy) {
2
	candy.kill();
3
	Candy._score += 1;
4
	Candy._scoreText.setText(Candy._score);
5
},

Yang ini mengambil satu permen sebagai parameter dan menggunakan metode Phaser kill () untuk menghapusnya. Kami juga meningkatkan skor dengan 1 dan memperbarui teks skor.

Menyetel ulang permen juga pendek dan mudah:

1
removeCandy: function(candy) {
2
	candy.kill();
3
	Candy._health -= 10;
4
},

Fungsi removeCandy () diaktifkan jika permen menghilang di bawah layar tanpa diklik. Objek candy dihapus, dan pemain kehilangan 10 poin kesehatan. (Dia punya 10 di awal, jadi kehilangan satu permen yang jatuh pun mengakhiri permainan.)

Prototipe dan Status Game

Kami telah belajar tentang mekanisme permainan, gagasan inti, dan bagaimana permainannya. Sekarang saatnya untuk melihat bagian lain dari kode: penskalaan layar, memuat aset, mengelola penekanan tombol, dan sebagainya.

Kami sudah tahu tentang status permainan, jadi mari kita lihat persis bagaimana tampilannya, satu demi satu:

Boot.js

Boot.js adalah file JavaScript tempat kita akan mendefinisikan objek permainan utama — sebut saja Candy (tetapi anda dapat memberi nama apa pun yang anda inginkan). Berikut kode sumber file Boot.js:

1
var Candy = {};
2
Candy.Boot = function(game) {};
3
Candy.Boot.prototype = {
4
	preload: function() {
5
		this.load.image('preloaderBar', 'img/loading-bar.png');
6
	},
7
	create: function() {
8
		this.input.maxPointers = 1;
9
		this.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL;
10
		this.scale.pageAlignHorizontally = true;
11
		this.scale.pageAlignVertically = true;
12
		this.scale.setScreenSize(true);
13
		this.state.start('Preloader');
14
	}
15
};

Seperti yang Anda lihat, kami mulai dengan var Candy = {} yang menciptakan objek global untuk game kami. Semuanya akan disimpan di dalam, jadi kami tidak akan mengasapi namespace global.

Kode Candy.Boot = fungsi (permainan) {} membuat fungsi baru yang disebut Boot () (digunakan dalam index.html) yang menerima objek game sebagai parameter (juga dibuat oleh framework di index.html).

Kode Candy.Boot.prototype = {} adalah cara untuk mendefinisikan konten Candy.Boot menggunakan prototipe:

1
Candy.Boot.prototype = {
2
	preload: function() {
3
		// code

4
	},
5
	create: function() {
6
		// code

7
	}
8
};

Ada beberapa nama khusus untuk fungsi di Phaser, seperti yang saya sebutkan sebelumnya; preload () dan create () adalah dua di antaranya. preload () digunakan untuk memuat aset apa pun dan create () dipanggil tepat sekali (setelah preload ()), sehingga anda dapat meletakkan kode yang akan digunakan sebagai setup untuk objek di sana, seperti untuk mendefinisikan variabel atau menambahkan sprite .

Objek Boot kami berisi dua fungsi ini, sehingga dapat direferensikan dengan menggunakan Candy.Boot.preload () dan Candy.Boot.create(), masing-masing. Seperti yang anda lihat di sumber kode lengkap file Boot.js, fungsi preload () memuat gambar preloader ke dalam kerangka kerja:

1
preload: function() {
2
	this.load.image('preloaderBar', 'img/loading-bar.png');
3
},

Parameter pertama dalam this.load.image () adalah nama yang kami berikan ke gambar bar pemuatan, dan yang kedua adalah path ke file gambar dalam struktur proyek kami.

Tetapi mengapa kita memuat gambar dalam file Boot.js, padahal Preload.js seharusnya melakukannya untuk kita? Nah, kita membutuhkan gambar bilah pemuatan untuk menunjukkan status semua gambar lain yang dimuat dalam file Preload.js, jadi harus dimuat sebelumnya, sebelum yang lainnya.

Opsi Penskalaan

Fungsi create () berisi beberapa pengaturan spesifik Phaser untuk input dan penskalaan:

1
create: function() {
2
	this.input.maxPointers = 1;
3
	this.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL;
4
	this.scale.pageAlignHorizontally = true;
5
	this.scale.pageAlignVertically = true;
6
	this.scale.setScreenSize(true);
7
	this.state.start('Preloader');
8
}

Baris pertama, yang menetapkan input.maxPointers ke 1, menentukan bahwa kami tidak akan menggunakan multi-sentuh, karena kami tidak membutuhkannya di game kami.

Pengaturan scale.scaleMode mengontrol penskalaan game kami. Opsi yang tersedia adalah: EXACT_FIT, NO_SCALE dan SHOW_ALL; Anda dapat menghitung melalui mereka dan menggunakan nilai 0, 1, atau 2, masing-masing. Opsi pertama akan mengatur skala game ke semua ruang yang tersedia (lebar dan tinggi 100%, tidak ada rasio yang dipertahankan); yang kedua akan menonaktifkan penskalaan sepenuhnya; dan yang ketiga akan memastikan bahwa permainan sesuai dengan dimensi yang diberikan, tetapi semuanya akan ditampilkan di layar tanpa menyembunyikan fragmen (dan rasio akan dipertahankan).

Mengatur scale.pageAlignHorizontally dan scale.pageAlignVertically ke true akan menyelaraskan permainan kami baik secara horizontal dan vertikal, sehingga akan ada jumlah ruang kosong yang sama di sisi kiri dan kanan elemen Canvas; hal yang sama berlaku untuk atas dan bawah.

Memanggil scale.setScreenSize (true) 'Aktifkan' skala kami.

Baris terakhir, state.start ('Preloader'), mengeksekusi status berikutnya — dalam kasus ini, status Preloader.

Preloader.js

File Boot.js yang baru saja kami lalui memiliki fungsi preload () satu-baris yang sederhana dan banyak kode dalam fungsi create (), tetapi Preloader.js terlihat sangat berbeda: kami memiliki banyak gambar untuk dimuat, dan create() Fungsi hanya akan digunakan untuk pindah ke negara lain ketika semua aset dimuat.

Berikut kode file Preloader.js:

1
Candy.Preloader = function(game){
2
	Candy.GAME_WIDTH = 640;
3
	Candy.GAME_HEIGHT = 960;
4
};
5
Candy.Preloader.prototype = {
6
	preload: function() {
7
		this.stage.backgroundColor = '#B4D9E7';
8
		this.preloadBar = this.add.sprite((Candy.GAME_WIDTH-311)/2,
9
			(Candy.GAME_HEIGHT-27)/2, 'preloaderBar');
10
		this.load.setPreloadSprite(this.preloadBar);
11
12
		this.load.image('background', 'img/background.png');
13
		this.load.image('floor', 'img/floor.png');
14
		this.load.image('monster-cover', 'img/monster-cover.png');
15
		this.load.image('title', 'img/title.png');
16
		this.load.image('game-over', 'img/gameover.png');
17
		this.load.image('score-bg', 'img/score-bg.png');
18
		this.load.image('button-pause', 'img/button-pause.png');
19
20
		this.load.spritesheet('candy', 'img/candy.png', 82, 98);
21
		this.load.spritesheet('monster-idle',
22
			'img/monster-idle.png', 103, 131);
23
		this.load.spritesheet('button-start',
24
			'img/button-start.png', 401, 143);
25
	},
26
	create: function() {
27
		this.state.start('MainMenu');
28
	}
29
};

Itu dimulai mirip dengan file Boot.js sebelumnya; kita mendefinisikan objek Preloader dan menambahkan definisi untuk dua fungsi (preload () dan create ()) ke prototipe-nya. Di dalam objek Prototype kami mendefinisikan dua variabel: Candy.GAME_WIDTH dan Candy.GAME_HEIGHT; ini mengatur lebar dan tinggi default layar game, yang akan digunakan di tempat lain dalam kode.

Tiga baris pertama dalam fungsi preload () bertanggung jawab untuk mengatur warna latar panggung (ke #B4D9E7, biru muda), menampilkan sprite dalam game, dan mendefinisikannya sebagai default untuk fungsi khusus yang disebut setPreloadSprite () yang akan menunjukkan perkembangan aset pemuatan.

Mari kita lihat fungsi add.sprite ():

1
this.preloadBar = this.add.sprite((640-311)/2, (960-27)/2, 'preloaderBar');

Seperti yang Anda lihat, kami memberikan tiga nilai: posisi kiri absolut gambar (bagian tengah layar dicapai dengan mengurangkan lebar gambar dari lebar panggung dan membagi dua hasilnya), posisi teratas mutlak gambar (dihitung sama), dan nama gambar (yang sudah kami muat di file Boot.js).

Memuat spritesheets

Beberapa baris berikutnya adalah tentang menggunakan load.image () (yang sudah Anda lihat) untuk memuat semua aset grafis ke dalam permainan.

Tiga yang terakhir sedikit berbeda:

1
this.load.spritesheet('candy', 'img/candy.png', 82, 98);

Fungsi ini, load.spritesheet (), alih-alih memuat gambar tunggal, urus koleksi lengkap gambar di dalam satu file — spritesheet. Dua parameter tambahan diperlukan untuk memberi tahu fungsi ukuran satu gambar dalam sprite.

Candy spritesheetCandy spritesheetCandy spritesheet

Dalam hal ini, kami memiliki lima jenis permen di dalam satu file candy.png. Seluruh gambar adalah 410x98px, tetapi item tunggal diatur ke 82x98px, yang dimasukkan dalam load.spritesheet () fungsi. Spritesheet pemain dimuat dengan cara yang sama.

Fungsi kedua, create (), memulai status permainan berikutnya, yaitu MainMenu. Ini berarti bahwa menu utama permainan akan ditampilkan tepat setelah semua gambar dari fungsi preload () telah dimuat.

MainMenu.js

File ini adalah tempat kami akan membuat beberapa gambar yang terkait dengan game, dan di mana pengguna akan mengklik tombol Start untuk meluncurkan loop game dan memainkan game.

1
Candy.MainMenu = function(game) {};
2
Candy.MainMenu.prototype = {
3
	create: function() {
4
		this.add.sprite(0, 0, 'background');
5
		this.add.sprite(-130, Candy.GAME_HEIGHT-514, 'monster-cover');
6
		this.add.sprite((Candy.GAME_WIDTH-395)/2, 60, 'title');
7
		this.add.button(Candy.GAME_WIDTH-401-10, Candy.GAME_HEIGHT-143-10,
8
			'button-start', this.startGame, this, 1, 0, 2);
9
	},
10
	startGame: function() {
11
		this.state.start('Game');
12
	}
13
};

Strukturnya terlihat mirip dengan file JavaScript sebelumnya. Prototipe objek MainMenu tidak memiliki fungsi preload (), karena kita tidak memerlukannya — semua gambar sudah dimuat dalam file Preload.js.

Ada dua fungsi yang didefinisikan dalam prototipe: create () (again) dan startGame (). Seperti yang saya sebutkan sebelumnya, nama yang pertama adalah spesifik untuk Phaser, sedangkan yang kedua adalah milik kita.

Mari kita lihat startGame () terlebih dahulu:

1
startGame: function() {
2
	this.state.start('Game');
3
}

Fungsi ini menangani satu hal saja — meluncurkan loop game — tetapi itu tidak diluncurkan secara otomatis atau setelah aset dimuat. Kami akan menugaskannya ke tombol dan menunggu input pengguna.

1
create: function() {
2
	this.add.sprite(0, 0, 'background');
3
	this.add.sprite(-130, Candy.GAME_HEIGHT-514, 'monster-cover');
4
	this.add.sprite((Candy.GAME_WIDTH-395)/2, 60, 'title');
5
	this.add.button(Candy.GAME_WIDTH-401-10, Candy.GAME_HEIGHT-143-10,
6
		'button-start', this.startGame, this, 1, 0, 2);
7
},

Metode create () memiliki tiga add.sprite () Fungsi Phaser yang sudah kita kenal: mereka menambahkan gambar ke tahap yang terlihat dengan memposisikannya secara mutlak. Menu utama kami akan berisi latar belakang, monster kecil di sudut, dan judul permainan.

Tombol-tombol

Ada juga objek yang sudah kami gunakan dalam kondisi Game, tombol:

1
this.startButton = this.add.button(Candy.GAME_WIDTH-401-10, Candy.GAME_HEIGHT-143-10,
2
	'button-start', this.startGame, this, 1, 0, 2);

Tombol ini sepertinya lebih rumit daripada metode yang kita lihat sejauh ini. Kami melewati delapan argumen berbeda untuk membuatnya: posisi kiri, posisi teratas, nama gambar (atau sprite), fungsi yang akan dieksekusi setelah tombol diklik, konteks di mana fungsi ini dieksekusi, dan indeks gambar di spritesheet tombol.

Beginilah tampilan spritesheet tombol, dengan status yang berlabel:

Start button spritesheet

Ini sangat mirip dengan spritesheet candy.png yang kami gunakan sebelumnya, kecuali diatur secara vertikal.

Penting untuk diingat bahwa tiga digit terakhir yang diteruskan ke fungsi — 1, 0, 2 — adalah status tombol yang berbeda: lebih dari (hover), keluar (normal), dan turun (sentuh / klik) masing-masing. Kami memiliki status normal, arahkan dan klik pada spritesheet button.png, jadi kami mengubah urutan dalam fungsi add.button () dari 0, 1, 2 hingga 1, 0, 2 untuk mencerminkannya.

Itu dia! Anda sekarang tahu dasar-dasar kerangka permainan Phaser; Selamat!

Game Selesai

Game demo yang digunakan dalam artikel telah berevolusi menjadi permainan lengkap dan selesai yang dapat anda mainkan di sini. Seperti yang anda lihat, ada live, pencapaian, skor tinggi, dan fitur menarik lainnya yang diimplementasikan, tetapi sebagian besar didasarkan pada pengetahuan yang telah anda pelajari dengan mengikuti tutorial ini.

Monster Wants Candy full gameMonster Wants Candy full gameMonster Wants Candy full game

Anda juga dapat membaca posting blog 'pembuatan' singkat untuk mempelajari tentang asal-usul game itu sendiri, kisah di baliknya, dan beberapa fakta menyenangkan.

Sumber daya

Membangun game HTML5 untuk perangkat seluler telah meledak dalam beberapa bulan terakhir. Teknologi ini semakin baik dan semakin baik, dan ada alat dan layanan yang muncul hampir setiap hari — ini adalah waktu terbaik untuk terjun ke pasar.

Kerangka kerja seperti Phaser memberi anda kemampuan untuk membuat game yang berjalan dengan sempurna di berbagai perangkat yang berbeda. Berkat HTML5, anda dapat menargetkan tidak hanya browser seluler dan desktop, tetapi juga sistem operasi dan platform asli yang berbeda.

Ada banyak sumber daya saat ini yang dapat membantu Anda memasuki pengembangan game HTML5, misalnya daftar Pemula Gamedev HTML5 ini atau artikel Memulai Dengan Pengembangan Game HTML5 ini. Jika anda memerlukan bantuan, anda dapat menemukan sesama pengembang di forum HTML5GameDevs atau langsung di saluran #BBG di Freenode IRC. Anda juga dapat memeriksa status buku yang akan datang tentang Firefox OS dan game HTML5, tetapi masih dalam tahap awal penulisan. Bahkan ada buletin mingguan Gamedev.js yang dapat anda ikuti, untuk mendapatkan berita terbaru.

Kesimpulan

Ini adalah perjalanan panjang melalui setiap baris kode demo Monster Wants Candy, tapi saya harap ini akan membantu anda mempelajari Phaser, dan bahwa dalam waktu dekat anda akan membuat game-game keren menggunakan framework.

Kode sumber yang digunakan dalam artikel ini juga tersedia secara bebas di GitHub, sehingga anda dapat mempelajarinya atau hanya mengunduhnya dan melakukan apa pun yang anda inginkan. Jangan ragu untuk memodifikasinya dan buat game anda sendiri di atasnya, dan pastikan untuk mengunjungi forum Phaser jika anda memerlukan sesuatu selama pengembangan.

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.