Advertisement
  1. Web Design
  2. HTML5

Kembangkan Game Pertama Anda di Kanvas Dari Awal hingga Akhir

Scroll to top
Read Time: 58 min

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

Apakah Anda ikut serta dalam diskusi, atau membaca artikel tentang itu, Anda harus menyadari bahwa, meskipun masih merupakan spesifikasi, HTML5 siap digunakan... sekarang.

Ini tidak hanya meningkatkan struktur semantik web, tetapi juga menghidupkan API JavaScript baru untuk membuatnya lebih mudah untuk menanamkan teknologi video, audio, dan bahkan geolokasi (yang sangat menarik untuk aplikasi web seluler) dengan mudah.

Itu bukan tempat API berhenti. Mereka juga dapat digunakan untuk membangun game, dengan memanfaatkan Canvas 2D API. Dan itulah yang akan dilakukan hari ini.


Konsep

Gambar pratinjau artikel ini mungkin mengarahkan Anda ke arah apa yang akan dibuat, tetapi masih agak kabur, bukan?

Untuk menunjukkan potensi Canvas 2D API, kita akan membuat game, yang telah dibuat di Flash berkali-kali sebelumnya: game "tangkap objek yang jatuh".


Seperti yang Anda ketahui, sebagian besar permainan itu selalu terikat dengan tema tertentu, seperti menangkap buah di keranjang Anda sambil menghindari apel busuk, atau menangkap koin emas sambil menghindari batu yang tidak berharga.

Demi menjaga hal-hal sederhana dan karena sebagian besar dari Anda dengan background dalam desain web dan pengembangan sudah tahu apa kepanjangan dari RGB, kita akan menangkap balok jatuh dengan warna tertentu: red, green or blue

Kita juga akan menerapkan fitur yang agak unik: warna keranjang menunjukkan blok mana yang harus Anda tangkap. Misalnya, jika keranjang berwarna biru, Anda hanya harus menangkap balok biru.

Sekarang kita sudah siap konsep kita, kita harus memikirkan nama untuk mengikat semuanya. Pada titik ini, Anda dapat menggunakan nama-nama kreativitas dan curah pendapat Anda, seperti 'Zealof', 'Xymnia' dan 'Doogle' - tetapi ide yang lebih baik adalah dengan membuat nama yang terkait dengan permainan itu sendiri.

Karena poin utama dari permainan ini adalah 'menangkap' 'RGB' berwarna 'blok', nama yang bagus bisa menjadi sesuatu di sepanjang garis 'RGBlock' atau 'CatchBlock'. Saya memilih untuk memanggil game 'RGBCatcher', merujuk pada judul pekerjaan yang mungkin untuk pemain game.


Sketsa Sederhana

Apakah Anda sedang dalam tahap awal membangun game, atau mendesain situs web portofolio baru Anda, sketsa sederhana sangat berguna untuk dengan cepat menangkap tepi kasar sebuah ide untuk penggunaan selanjutnya.

Jauh sebelum saya mulai menulis satu baris kode, saya pertama kali membuat sketsa tentang bagaimana hal-hal akan terlihat.

Saya mengelilingi benda-benda dengan panah untuk menunjukkan kemampuan gerakan mereka, dan juga menambahkan titik-titik berwarna untuk membantu saya mengingat bahwa warna keranjang dan balok tidak seharusnya statis.

Semuanya juga harus memiliki semacam antarmuka, di mana pemain dapat membaca kesehatannya (HP) dan skor (PT) dari. Meskipun saya membuat sketsa asli dengan pensil sederhana di atas kertas, itu tampak mirip dengan gambar di bawah ini.


Jangan berlebihan membuat sketsa Anda; Anda harus menggunakannya sebagai alat untuk menangkap sisi kasar sebuah ide - bukan untuk melukis atau mendeskripsikan dengan kata-kata persis setiap langkah bagaimana hal-hal harus terlihat atau bekerja. Itulah tujuan mockup dan prototipe.


Persiapan

Bahkan sebelum kita mulai menulis satu baris JavaScript, kita harus menyiapkan tag kanvas HTML5 terlebih dahulu. canvas adalah tag HTML5 yang bekerja sama dengan Canvas 2D API. Itu benar: bekerja sama.

Ini adalah kesalahpahaman umum bahwa fungsi HTML5 baru tidak memerlukan JavaScript untuk dijalankan. HTML5 adalah — seperti versi sebelumnya — bahasa markup dan tidak mampu membuat web dinamis dengan sendirinya.


Organisasi proyek kita

Kurangnya organisasi jelas-jelas tidak boleh, jadi mari kita buat direktori baru dan menyebutnya persis sama dengan nama permainan — itu akan menampung semua file yang diperlukan untuk proyek kita.

Dalam direktori ini, Anda harus membuat direktori anak, yang disebut 'js', yang akan menampung file 'rgbcatcher.js'. Kami juga akan menulis JavaScript nanti, jadi sebaiknya Anda memiliki editor teks dengan setidaknya penyorotan sintaks dan dukungan nomor baris untuk kenyamanan Anda sendiri.

Di root direktori 'RGBCatcher', buat file teks, yang disebut 'indeks' dengan ekstensi HTML biasa dan buka dengan editor teks favorit Anda — index.html akan berfungsi sebagai titik awal untuk game kita.

HTML

Tidak ada hal-hal mewah yang terjadi dengan markup - hanya doctype HTML5, referensi ke rgbcatcher.js, dan tag kanvas dengan gaya yang sangat terbatas diterapkan harus melakukan trik.

1
<!DOCTYPE html> 
2
<html> 
3
<head> 
4
  <title>RGBCatcher</title> 
5
	<script src="js/rgbcatcher.js"></script> 
6
	 
7
	<style type="text/stylesheet"> 
8
		body, html { 
9
			margin: 0px; 
10
			padding: 0px; 
11
		} 
12
		 
13
		#canvas { 
14
			border: 1px solid #eee; 
15
			margin: 10px; 
16
		} 
17
	</style> 
18
</head> 
19
 
20
<body> 
21
	<canvas id="canvas" width="398" height="198"></canvas> 
22
</body> 
23
</html>

Seperti yang Anda lihat, doctype HTML5 secara signifikan lebih sederhana, dibandingkan dengan sebagian besar doctype aliran utama saat ini. Di antara hal-hal lain yang bermanfaat, DOCTYPE baru ini memungkinkan untuk instantiasi lebih cepat dari dokumen HTML yang sesuai standar.


JavaScript

Dalam sketsa, setiap objek instantiated memiliki tanda silang di dalamnya. Ingatlah ini: kita dapat dengan mudah membuat daftar definisi fungsi mana yang akan dibutuhkan:

  • Indikator kesehatan (dapat dihitung)
  • Penghitung skor (dapat dihitung)
  • Sebuah blok (bergerak)
  • Keranjang (dikontrol oleh pemain, juga dapat digerakkan)

Jadi mengapa hanya ada satu blok dalam daftar, sementara kita dapat melihat lima di sketsa? Meskipun JavaScript berbasis prototipe, kita dapat membuat instance banyak blok dari satu definisi blok dasar.


Pembungkus Obyek

Sekarang kita sudah menyiapkan daftar objek kita, seharusnya tidak terlalu sulit untuk membuat pembungkus untuk mereka.

1
 
2
var Basket = function() 
3
{ 
4
} 
5
 
6
var Block = function() 
7
{ 
8
} 
9
 
10
var Health = function() 
11
{ 
12
} 
13
 
14
var Score = function() 
15
{ 
16
}

Tunggu — objek blok dan keranjang keduanya bertipe 'bergerak' (koordinatnya dapat berubah), jadi bukankah berguna memiliki objek bergerak dasar, yang nantinya dapat diwarisi dari objek blok dan keranjang?

1
var Movable = function() 
2
{ 
3
}

Objek blok dan keranjang bukan satu-satunya objek yang memiliki fungsi tertentu yang sama — indikator kesehatan dan penghitung skor keduanya dirancang untuk melacak nilai tertentu: skor dan kesehatan, masing-masing.

1
var Countable = function() 
2
{ 
3
}

Itu dia. Meskipun mereka tidak memiliki fungsi sama sekali, pembungkus kita selesai!



Definisi Global

Sebelum melangkah lebih jauh, kita membutuhkan dua hal: beberapa variabel global yang dapat diakses dari fungsi apa pun yang memerlukannya, dan pembungkus global. Wrapper global ini berfungsi sebagai pengendali utama untuk game.

Secara umum, definisi global adalah hal yang buruk, karena JavaScript hanya memiliki satu namespace tunggal. Ini membuatnya lebih mudah bagi nama variabel dan fungsi untuk bertabrakan di antara berbagai skrip pada halaman yang sama, tetapi karena game ini cukup banyak dijalankan sebagai stand-alone, kita tidak perlu terlalu khawatir tentang hal itu.

Semua variabel global akan diletakkan di bagian atas file rgbcatcher.js. Pembungkus RGBCatcher global akan ditempatkan di bagian bawah.

1
// This will hold the DOM object of our canvas element 

2
var canvas; 
3
 
4
// This will hold the Canvas 2D API object  

5
var context; 
6
 
7
// This object will hold pressed keys to check against 

8
var keyOn = []; 
9
 
10
// [object definitions] 

11
 
12
RGBCatcher = new function() 
13
{ 
14
	// This is the object which holds our game-colors, the 'this' keyword is used to make it accessible from outside this function (aka a public property) 

15
	this.colors = [ 
16
		'#f00', // Red 

17
		'#0f0', // Green 

18
		'#00f', // Blue 

19
	]; 
20
}

Kita juga mendefinisikan fungsi rand global baru, yang akan memudahkan kita untuk mengambil angka acak dalam rentang tertentu. Ini akan, misalnya, digunakan untuk memilih warna acak dari array RGBCatcher.colors.

1
function rand(min, max)  
2
{ 
3
	return Math.random() * (max-min) + min; 
4
}

Fungsi Basis Bergerak

Karena baik keranjang dan objek blok adalah benda bergerak, yang berarti bahwa koordinatnya tidak konstan, pertama-tama kita akan menulis objek dasar yang dapat mereka warisi.

Objek dasar bergerak hanya boleh berisi fungsionalitas, yang akan hadir di kedua blok dan definisi fungsi keranjang.

Constructor

Konstruktor adalah rutin, yang dipanggil segera setelah objek baru dipakai dan sebelum objek memiliki prototipe yang ditugaskan padanya. Ini membuat konstruktor sangat berguna untuk menginisialisasi variabel spesifik objek, yang kemudian tersedia dalam lingkup objek.

Karena objek keranjang dan blok akan menampung properti yang berbeda, kita akan menulis konstruktor yang menerima array dan kemudian menggunakan array itu untuk mengatur variabel objek tertentu.

1
// Add a parameter called 'data' so we can access the contents of an argument used at the instantiation of the Player object in the constructor 

2
var Movable = function(data) 
3
{ 
4
	if (data === undefined) 
5
		return; 
6
 
7
	for (var i = 0; i < data.length; i++)  
8
	{ 
9
		var setting = data[i]; 
10
		 
11
		// By accessing 'this' (which refers to this very instance) as an array, we can set a new object-specific variable with the name of 'setting' to 'setting' its value 

12
		this[setting[0]] = setting[1]; 
13
	} 
14
	 
15
	// When this object is succesfully instantiated; it's alive! 

16
	this.alive = true; 
17
}

Memperbarui objek Movable

Proses pembaruan objek bergerak, yang terdiri dari fase gerakan dan menggambar, seharusnya hanya terjadi ketika objek bergerak masih hidup.

Karena gerakan aktual dan proses menggambar objek bergerak mungkin tergantung pada pewarisnya, objek bergerak dasar seharusnya tidak mendefinisikan mereka kecuali semua pewaris berbagi pembaruan atau metode menggambar yang sama.

1
Movable.prototype = { 
2
	update: function() 
3
	{ 
4
		if (this.alive) 
5
		{ 
6
			this.move(); 
7
			this.draw(); 
8
		} 
9
	} 
10
};

Basket

Sekarang kita telah mendefinisikan objek bergerak, kita hanya perlu menulis metode move dan draw tambahan untuk objek keranjang yang diwarisi darinya.

Variabel khusus keranjang

Sebelum kita mulai dengan metode objek keranjang, kita harus terlebih dahulu mendefinisikan beberapa variabel yang secara spesifik terkait dengan objek ini. Variabel-variabel ini akan dipegang oleh pembungkus RGBCatcher.

Variabel basket akan menyimpan instance objek keranjang. Variabel basketData adalah array yang menyimpan data tentang keranjang, seperti tinggi dan kecepatan gerakan horizontal.

1
var basket; 
2
 
3
var basketData = [ 
4
	['width', 30], // Width in pixels of the basket 

5
	['height', 10], // Height in pixels of the basket 

6
	['xSpeed', 1.1], // Horizontal movement speed in pixels 

7
	['color', undefined], // The color of the basket 

8
	['oldColor', undefined] // The old color of the basket, we can check against to prevent having the same basket color twice 

9
];

Constructor

Karena basis bergerak sudah memiliki konstruktor dan metode pembaruan yang diperlukan untuk fungsi basket, kita mewariskan jauh dari mengimplementasikannya.

1
var Basket = function(data)  
2
{ 
3
	Movable.call(this, data); 
4
} 
5
Basket.prototype = new Movable();

Mengatur Ulang Basket

Kita ingin memusatkan keranjang di bagian bawah layar, dan mengubah warna keranjang segera setelah permainan atau level baru dimulai.

Dengan membagi lebar keranjang dengan dua dan mengurangi nilai ini dari lebar kanvas, juga dibagi dua, sebagai hasilnya, kita akan mendapatkan basket yang berpusat sempurna.

Untuk menempatkan keranjang di bagian bawah layar, kita mengatur koordinat y ke ketinggian keranjang, dikurangi dari ketinggian kanvas.

1
Basket.prototype.reset = function() 
2
{ 
3
	this.x = canvas.width / 2 - this.width / 2; 
4
	this.y = canvas.height - this.height; 
5
}

Mengatur ulang warna basket sedikit lebih sulit.

Kita menggunakan loop sementara yang secara acak akan memilih warna dari array RGBCatcher.color publik pada setiap putaran.

Segera setelah warna dipilih yang tidak sama dengan warna keranjang yang lama, loop berhenti berputar dan oldColor baru ditugaskan.

1
Basket.prototype.reset = function() 
2
{ 
3
	// Reset the position 

4
	this.x = canvas.width / 2 - this.width / 2; 
5
	this.y = canvas.height - this.height; 
6
	 
7
	// Reset the color 

8
	while (this.color == this.oldColor) 
9
		this.color = RGBCatcher.colors[Math.round(rand(0, (RGBCatcher.colors.length-1)))]; 
10
	 
11
	// Change the old color to the current color (so that the while loop will stil work the next time this method is called) 

12
	this.oldColor = this.color; 
13
}

Memperbarui Basket

Karena objek yang bergerak telah membawa fungsionalitas untuk metode update, kita hanya perlu mendefinisikan metode tertentu untuk memindahkan dan menggambar keranjang.

Metode yang akan memindahkan keranjang, akan, tergantung pada input pengguna, menambah atau mengurangi kecepatan gerakan horizontal (basket.xSpeed) dari koordinat x saat ini keranjang.

Metode pemindahan juga bertanggung jawab untuk memindahkan keranjang kembali ke posisi terbaru yang memungkinkan jika keranjang bergerak keluar dari elemen kanvas viewport — ini mengharuskan kita untuk menerapkan rutin yang sangat sederhana untuk deteksi tabrakan.

Deteksi tabrakan tidak boleh diimplementasikan hanya dengan memeriksa apakah koordinat objek cocok dengan ekspresi tertentu. Ini mungkin bekerja untuk grafik yang 1px demi 1px, tetapi tampilan grafis dari sesuatu yang lebih besar dari itu akan benar-benar mati karena harus ada juga akun dengan lebar dan tinggi.

Itulah sebabnya sebagian besar waktu Anda harus mengurangi atau menambahkan bentuk lebarnya atau tinggi masing-masing koordinat x atau y untuk mencocokkan representasi grafis dari sebuah tabrakan.

1
Basket.prototype.move = function() 
2
{ 
3
	// 37 is the keycode representation of a left keypress 

4
	if (keyOn[37]) 
5
		this.x -= this.xSpeed; 
6
	 
7
	// 39 is the keycode representation of a right keypress 

8
	if (keyOn[39]) 
9
		this.x += this.xSpeed; 
10
 
11
	// If the x coordinate is lower than 0, which is less than the outer left position of the canvas, move it back to the outer left position of the canvas  

12
	if (this.x > 0) 
13
		this.x = 0; 
14
 
15
	// If the x coordinate plus the basket's width is greater than the canvas's width, move it back to the outer right position of the canvas 

16
	if (this.x + this.width < canvas.width) 
17
		this.x = canvas.width - this.width; 
18
}

Sekarang pengguna dapat memindahkan keranjangnya, satu-satunya hal yang masih harus kita lakukan adalah menulis metode yang memiliki kemampuan untuk menggambar keranjang di layar — ini adalah tempat Canvas 2D API melompat masuk.

Anda mungkin berpikir bahwa memanfaatkan API ini sangat canggih, tetapi sebenarnya tidak! Canvas 2D API dirancang sesederhana mungkin, namun sefungsional mungkin.

Kita hanya memerlukan dua dependensi Canvas 2D API: properti fillStyle dan metode fillRect.

Properti fillStyle digunakan untuk mendefinisikan warna isian untuk bentuk.
Nilai harus mengikuti spesifikasi CSS3 untuk warna — ini berarti Anda diizinkan untuk menggunakan 'hitam' tetapi juga setara dengan HEX, '#000' — tetapi juga bekerja dengan gradien dan pola tetapi kita akan menggunakan warna isian sederhana.w

Metode fillRect menggambar persegi panjang yang diisi, dengan memanfaatkan fillStyle sebagai warna isiannya, pada posisi (x, y) dan lebar dan tinggi piksel yang ditentukan.

Perhatikan bahwa parameter fillRect yang diperlukan dapat ditemukan sebagai properti di objek keranjang yang dipakai, jadi kita bisa menggunakan kata kunci this dalam kombinasi dengan nama parameter.

1
Basket.prototype.draw = function() 
2
{ 
3
	// The Basket object's 'color' atribute holds the color which our basket needs to be 

4
	context.fillStyle = this.color;  
5
 
6
	// The C2A fillRect method draws a filled rectangle (with fillStyle as its fill color) at position (x, y) with a set height and width. 

7
	// All these arguments can be found in the atributes of our basket object 

8
	context.fillRect(this.x, this.y, this.width, this.height); 
9
}

Definisi fungsi akhir

Itu dia; kita memiliki seluruh objek keranjang yang berjalan dan berjalan.

Yah, saya tidak akan menyebutnya berjalan, jadi mari kita atur lingkaran permainan dan berbagai elemen inti dari permainan kita, sehingga kita dapat menggunakan keranjang ini untuk digunakan!

1
var Basket = function(data)  
2
{ 
3
	Movable.call(this, data); 
4
} 
5
Basket.prototype = new Movable(); 
6
Basket.prototype.reset = function() 
7
{ 
8
	// Reset the position 

9
	this.x = canvas.width / 2 - this.width / 2; 
10
	this.y = canvas.height - this.height; 
11
	 
12
	// Reset the color 

13
	while (this.color == this.oldColor) 
14
		this.color = RGBCatcher.colors[Math.round(rand(0, (RGBCatcher.colors.length-1)))]; 
15
	 
16
	// Change the old color to the current color (so that the while loop will stil work the next time this method is called) 

17
	this.oldColor = this.color; 
18
} 
19
Basket.prototype.move = function() 
20
{ 
21
	// 37 is the keycode representation of a left keypress 

22
	if (keyOn[37]) 
23
		this.x -= this.xSpeed; 
24
	 
25
	// 39 is the keycode representation of a right keypress 

26
	if (keyOn[39]) 
27
		this.x += this.xSpeed; 
28
 
29
	// If the x coordinate is lower than 0, which is less than the outer left position of the canvas, move it back to the outer left position of the canvas  

30
	if (this.x < 0) 
31
		this.x = 0; 
32
 
33
	// If the x coordinate plus the basket's width is greater than the canvas's width, move it back to the outer right position of the canvas 

34
	if (this.x + this.width < canvas.width) 
35
		this.x = canvas.width - this.width; 
36
} 
37
Basket.prototype.draw = function() 
38
{ 
39
	// The Basket object's 'color' atribute holds the color which our basket needs to be 

40
	context.fillStyle = this.color;  
41
 
42
	// The C2A fillRect method draws a filled rectangle (with fillStyle as its fill color) at position (x, y) with a set height and width. 

43
	// All these arguments can be found in the atributes of our basket object 

44
	context.fillRect(this.x, this.y, this.width, this.height); 
45
}

RGBCatcher Wrapper

Untuk benar-benar mengimplementasikan fungsi dan prototipe yang baru saja ditulis, mereka harus dipakai dan dipanggil di suatu tempat. Inilah yang akan ditangani oleh penangan utama permainan: membuat instance dan memanggil hal-hal yang tepat pada waktu yang tepat.

Metode yang dijalankan

Metode run publik dari pembungkus RGBCatcher, akan menginisialisasi komponen game (seperti Canvas 2D API, sebuah instance dari objek keranjang dan sebagainya).
Ini juga akan mengatur pendengar acara global untuk acara keyup dan keydown sehingga penekanan tombol saat ini dapat dengan mudah diambil dari array global keyOn.

1
RGBCatcher = new function() 
2
{ 
3
	// Public 

4
	this.colors = [ 
5
		'#f00', 
6
		'#0f0', 
7
		'#00f', 
8
	]; 
9
	 
10
	// Private 

11
	var basketData = [ 
12
		['width', 30], 
13
		['height', 10], 
14
		['xSpeed', 1.1], 
15
		['color', '#f00'], 
16
		['oldColor', '#f00'] 
17
	]; 
18
	 
19
	var basket; 
20
 
21
	this.run = function() 
22
	{ 
23
		// Set the global 'canvas' object to the #canvas DOM object to be able to access its width, height and other attributes are 

24
		canvas = document.getElementById('canvas'); 
25
	 
26
		// This is where its all about; getting a new instance of the C2A object — pretty simple huh? 

27
		context = canvas.getContext('2d'); 
28
	 
29
		// Add an eventListener for the global keydown event 

30
		document.addEventListener('keydown', function(event) 
31
		{ 
32
			// Add the keyCode of this event to the global keyOn Array 

33
			// We can then easily check if a specific key is pressed by simply checking whether its keycode is set to true 

34
			keyOn[event.keyCode] = true; 
35
		}, false); 
36
	 
37
		// Add another eventListener for the global keyup event 

38
		document.addEventListener('keyup', function(event) 
39
		{ 
40
			// Set the keyCode of this event to false, to avoid an inifinite keydown appearance 

41
			keyOn[event.keyCode] = false; 
42
		}, false); 
43
		 
44
		// Instantiate the basket object and feed it the required basketData 

45
		basket = new Basket(basketData); 
46
		 
47
		// At the start of a new game, this method is called to set dynamic variables to their default values 

48
		resetGame(); 
49
	} 
50
}

Menyetel ulang game

Karena kita hanya memiliki satu objek terkait permainan untuk diatur ulang sejauh ini, yang merupakan objek basket, kita hanya perlu memanggil metode pengaturan ulang objek ini untuk mengatur ulang permainan.

1
RGBCatcher = new function() 
2
{ 
3
	// [...] 

4
 
5
	function resetGame() 
6
	{ 
7
		basket.resetPosition(); 
8
		basket.resetColor(); 
9
	} 
10
}

Perhatikan bahwa metode ini hanya boleh dipanggil pada awal permainan baru. Karena kita belum memiliki manajemen level, untuk saat ini dipanggil dari fungsi run RGBCatcher.

Siklus game

Siklus game bertanggung jawab untuk memperbarui dan menggambar ulang semua objek permainan hingga peristiwa tertentu terjadi, seperti pengguna kehabisan kesehatan.

Siklus game memiliki beragam format dan ukuran, tetapi urutan umum loop game adalah memeriksa input, memperbarui, dan memindahkan objek game, membersihkan layar, dan menggambar ulang semua objek game ke layar.


Dengan hanya memanggil metode update objek keranjang, yang sudah memproses sebagian besar langkah-langkah yang ditemukan di loop game.

Satu-satunya baris kode tambahan untuk ditulis adalah baris yang membersihkan layar. Karena layar kita adalah elemen kanvas, API 2D Kanvas diperlukan untuk mencapai ini.

Menghapus kanvas dengan memanfaatkan Canvas 2D API dilakukan melalui metode clearRect. Ia menerima empat parameter: koordinat x dan y untuk mulai membersihkan dari dan lebar dan tinggi untuk dihapus.

Karena kita perlu menghapus persegi panjang yang tumpang tindih dengan seluruh kanvas, kita mulai dari titik (0, 0) dan mengatur tinggi dan lebar masing-masing tinggi dan lebar elemen kanvas.

1
RGBCatcher = new function() 
2
{ 
3
	// [...] 

4
	 
5
	function gameLoop() 
6
	{ 
7
		// Clear the entire canvas 

8
		context.clearRect(0, 0, canvas.width, canvas.height); 
9
	} 
10
}

Sekarang setelah siklus game kita membersihkan kanvas di setiap putaran, juga disebut bingkai, kita hanya perlu memanggil metode pembaruan objek keranjang karena menangani proses pemindahan, pembaruan, dan menggambar ulang dengan sendirinya.

1
RGBCatcher = new function() 
2
{ 
3
	// [...] 

4
	 
5
	function gameLoop() 
6
	{ 
7
		context.clearRect(0, 0, canvas.width, canvas.height); 
8
		 
9
		basket.update(); 
10
	} 
11
}

Titik masuk

Satu-satunya hal yang masih hilang untuk melakukan uji coba pertama game ini, adalah titik masuk untuk loop game.

Satu-satunya tujuan entry point adalah untuk memulai loop game dan menentukan interval untuk setiap loop putarannya — setInterval adalah persis apa yang kita butuhkan.

1
RGBCatcher = new function() 
2
{ 
3
	// [...] 

4
	 
5
	// The variable associated with the setInterval ID 

6
	var interval; 
7
	 
8
	this.run = function() 
9
	{ 
10
		// [...] 

11
	 
12
		// Set the interval variable to the interval its ID so we can easily abort the game loop later 

13
		// The speed of the interval equals 30 frames per second, which should be enough to keep things running smoothly 

14
		interval = setInterval(gameLoop, 30/1000); 
15
	} 
16
}

Seperti yang mungkin Anda perhatikan, begitu dimasukkan, loop game kita berjalan tanpa batas. Kita pasti akan mengubahnya nanti, tetapi untuk saat ini kita masih dalam tahap awal proses pengembangan.


Ayo, Ayo!

Sekarang setelah objek master RGBCatcher wrapper dan keranjang telah ditentukan, akhirnya kita dapat menguji game prematur!

Karena permainan kami tergantung pada kanvas elemen, kita harus menunggu sampai ia dituliskan sebagai sebelum kita sebut RGBCatcher publik menjalankan metode.
Ini bisa dilakukan dengan memasukkan file JavaScript setelah markup elemen kanvas, tetapi jika Anda ingin menjaga JavaScript Anda di elemen-head, cukup lampirkan metode run ke window acara onload nya.

1
window.onload = function() 
2
{ 
3
	RGBCatcher.run(); 
4
}

Jalankan file index.html yang terletak di direktori RGBCatcher dan cobalah untuk memindahkan keranjang dengan memanfaatkan tombol panah kiri dan kanan.

Coba juga untuk menyegarkan halaman beberapa kali untuk memeriksa apakah warna keranjang benar-benar berubah atau tidak. Jangan khawatir jika Anda melihat warna yang berulang — warna yang lama tidak disimpan pada refresh halaman.

Jelas itu belum terlalu spektakuler, jadi jangan ragu untuk melanjutkan karena kami akan menambahkan beberapa balok jatuh.



Blok

Permainan menangkap-benda-jatuh tidak benar-benar layak dimainkan tanpa benda jatuh untuk ditangkap jadi mari kita lanjutkan dan buat balok itu jatuh!

Kita akan mulai dengan menuliskan kode untuk objek blok dan dalam waktu singkat Anda akan melihat blok merah, hijau dan biru jatuh di layar Anda.

Blok variabel tertentu

Sama seperti objek keranjang, objek blok membutuhkan array yang diisi dengan data untuk menyimpan properti spesifik blok, seperti lebar dan kecepatan gerakan vertikal.
Array data ini masuk dalam pembungkus RGBCatcher.

1
var blockData = [ 
2
	['width', 10], // Width in pixels of this block 

3
	['height', 10], // Height in pixels of this block 

4
	['ySpeed', 2], // Vertical movement speed in pixels 

5
	['color', undefined] // The color of this block 

6
];

Jadi mengapa ada, tidak seperti variabel basket, tidak ada variabel block yang akan menampung instance dari objek blok?
Karena level harus mengandung lebih dari satu blok untuk ditangkap.

Bahkan, kita akan menggunakan semacam manajer blok yang akan mengelola blok — lebih lanjut tentang itu nanti.

Constructor

Konstruktor objek blok kita persis sama dengan konstruktor objek keranjang.

1
var Block = function(data)  
2
{ 
3
	Movable.call(this, data); 
4
} 
5
Block.prototype = new Movable();

Perhatikan bagaimana kita akan memiliki beberapa baris kode duplikat jika kita tidak menggunakan definisi objek bergerak dasar?

Menyetel Ulang Block

Karena kita ingin mencegah agar semua blok jatuh pada posisi yang persis sama, kita menggunakan fungsi rand untuk meletakkan blok pada posisi horizontal acak.

Alih-alih memiliki metode resetPosition, kita akan memanggil metode yang mencapai initPosisi posisi acak karena metode ini adalah tentang menginisialisasi posisi, bukan menyetel ulang.

Inisialisasi? Bukankah konstruktor yang seharusnya melakukan itu? Ya, jadi kita hanya akan memanggil metode initPosition sekali, di konstruktor.

1
var Block = function(data)  
2
{ 
3
	Movable.call(this, data); 
4
	 
5
	this.initPosition(); 
6
} 
7
Block.prototype = new Movable(); 
8
Block.prototype.initPosition = function() 
9
{ 
10
	// Only allow to set the position of this block once 

11
	if (this.x !== undefined || this.y !== undefined) 
12
		return; 
13
 
14
	// By picking a rounded number between 0 and the canvas.width subtracted by the block's width, we have a position for this block which is still inside the block's viewport 

15
	this.x = Math.round(rand(0, canvas.width - this.width)); 
16
	 
17
	// By setting the vertical position of the block to 0 subtracted by the block's height, the block will look like it slides into the canvas's viewport 

18
	this.y = 0 - this.height; 
19
}

Sebuah blok sekarang akan secara otomatis menginisialisasi posisinya pada instantiation, tetapi belum menginisialisasi warnanya.

Pertama, kita harus mengubah konstruktor fungsi blok sehingga, di samping initPosition, panggilan akan dibuat ke initColor saat instantiasi.

1
var Block = function(data)  
2
{ 
3
	Movable.call(this, data); 
4
	 
5
	this.initPosition(); 
6
	this.initColor(); 
7
}

Untuk saat ini, ini akan membuang "Object# tidak memiliki metode 'initColor' "TypeError jadi mari kita tambahkan metode initColor ke prototipe.

1
Block.prototype.initColor = function() 
2
{ 
3
	if (this.color !== undefined) 
4
		return; 
5
 
6
	this.color = RGBCatcher.colors[Math.round(rand(0, (RGBCatcher.colors.length-1)))]; 
7
}

Memperbarui Block

Prototipe yang dapat dipindah-pindah telah menghadirkan fungsionalitas untuk metode pembaruan kolektif, jadi sekali lagi itu sesuatu yang tidak perlu kita khawatirkan.

Karena tidak diperlukan input untuk blok jatuh, metode yang membuat blok bergerak, sangat lurus ke depan: terus menambahkan kecepatan jatuh vertikal ke posisi vertikal saat ini.

1
Block.prototype.move = function() 
2
{ 
3
	// Add the vertical speed to the block's current position to move it 

4
	this.y += this.ySpeed; 
5
}

Satu-satunya yang tersisa untuk menulis untuk objek blok kita adalah metode draw, yang merupakan tugas lain untuk Canvas 2D API.

Untuk menggambar blok, pertama-tama kita harus menetapkan warna isian dan kemudian kita harus menggambar persegi panjang dengan parameter yang diambil dari properti blok.
Kedengarannya familiar? Itu harus. Kita melakukan trik yang persis sama dengan metode draw objek keranjang, ingat?

Karena objek keranjang dan blok sama-sama dapat bergerak, kita hanya akan memindahkan metode draw keranjang ke prototipe bergerak sehingga semua pewaris akan berbagi metode draw ini.

1
Movable.prototype = { 
2
	// [...] 

3
	 
4
	draw: function() 
5
	{ 
6
		context.fillStyle = this.color; 
7
		context.fillRect(this.x, this.y, this.width, this.height); 
8
	} 
9
}

Definisi fungsi akhir

Blok-blok itu tidak akan jatuh sendiri jadi mari kita lanjutkan dan menambahkan penanganan blok ke pembungkus RGBCatcher.

1
var Block = function(data)  
2
{ 
3
	Movable.call(this, data); 
4
	 
5
	this.initPosition(); 
6
	this.initColor(); 
7
} 
8
Block.prototype = new Movable(); 
9
Block.prototype.initPosition = function() 
10
{ 
11
	// Only allow to set the position of this block once 

12
	if (this.x !== undefined || this.y !== undefined) 
13
		return; 
14
 
15
	// By picking a rounded number between 0 and the canvas.width subtracted by the block's width, we have a position for this block which is still inside the block's viewport 

16
	this.x = Math.round(rand(0, canvas.width - this.width)); 
17
	 
18
	// By setting the vertical position of the block to 0 subtracted by the block's height, the block will look like it slides into the canvas's viewport 

19
	this.y = 0 - this.height; 
20
} 
21
Block.prototype.initColor = function() 
22
{ 
23
	if (this.color !== undefined) 
24
		return; 
25
 
26
	this.color = RGBCatcher.colors[Math.round(rand(0, (RGBCatcher.colors.length-1)))]; 
27
} 
28
Block.prototype.move = function() 
29
{ 
30
	// Add the vertical speed to the block's current position to move it 

31
	this.y += this.ySpeed; 
32
}

Menangani Block

Meskipun basket mampu bergerak tanpa bantuan pengendali yang dikendalikan komputer, satu blok jelas tidak.

Ini membutuhkan penyelia atau lebih tepatnya seorang manajer, yang, antara lain, dapat menghapus blok dari layar segera setelah mereka menyentuh tanah. Pengawas ini akan menemukan tempatnya di pembungkus utama RGBCatcher.


  • Initialize: Inisialisasi variabel terkait khusus ke blok, seperti variabel yang menunjukkan berapa banyak blok yang masih ada di layar.
  • Reset: Reset semua variabel initialized ke nilai default.
  • Perbarui block: Segera setelah inisialisasi selesai, loop game dimasukkan dan kita akan mulai dengan looping melalui semua blok yang tersedia saat ini dan memperbaruinya.
  • Periksa tabrakan: Untuk setiap blok kita harus memeriksa apakah sedang bertabrakan atau tidak dan melakukan tindakan spesifik pada tabrakan tertentu.
  • Tambahkan blok jika diperlukan: Ketika semua blok berhasil diperbarui dan tumbukan telah ditangani, blok baru harus muncul jika ini diperlukan.

Blok penanganan variabel tertentu

Kita membutuhkan lima variabel lagi untuk menangani blok. Semuanya masuk dalam definisi fungsi RGBCatcher sebagai properti pribadi.

1
// The amount of blocks there should be per level (example: level 3 equals has (3*4) 12 blocks to process) 

2
var blocksPerLevel = 4; 
3
// The time in seconds there should be between the fall of a new block. This value decreases when the user goes up a level 

4
var blocksSpawnSec; 
5
// The amount of blocks already spawned 

6
var blocksSpawned; 
7
// The amount of blocks currently on the canvas 

8
var blocksOnScreen; 
9
// The array which holds the blocks to process 

10
var blocks = [];

Reset

Pembungkus objek RGBCatcher sudah memiliki metode resetGame. Kita sekarang hanya perlu menambahkan nilai default untuk variabel yang ditentukan di atas, sehingga mereka direset pada awal permainan baru.

1
RGBCatcher = new function() 
2
{ 
3
	// [...] 

4
 
5
	function resetGame() 
6
	{ 
7
		basket.resetPosition(); 
8
		basket.resetColor(); 
9
		 
10
		blocksSpawnSec = 2.5; 
11
		blocksSpawned = 0; 
12
		blocksOnScreen = 0; 
13
		blocks = []; 
14
	} 
15
}

Perbarui block

Fase blok pembaruan muncul pada perulangan melalui semua blok dan memperbarui mereka. Ini juga bertanggung jawab untuk memulai fase selanjutnya dari manajemen blok yang terdiri dari memanggil metode untuk menangani deteksi tabrakan.

1
RGBCatcher = new function() 
2
	// [...] 

3
 
4
	function updateBlocks() 
5
	{ 
6
		for (var i = 0; i < blocks.length; i++) 
7
		{ 
8
			// Assign a local copy 

9
 			var block = blocks[i]; 
10
			 
11
			block.update(); 
12
			checkCollision(block); 
13
		} 
14
	} 
15
}

Sekarang kita menuju suatu tempat. Setiap blocks dalam variabel blok diperbarui dengan memanfaatkan metode update objek blok.

Ketika blok telah diperbarui, itu akan mendorong untuk metode checkCollision yang kemudian akan menangani deteksi tabrakan.

Periksa tabrakan

Deteksi tabrakan adalah salah satu bagian tersulit dalam pengembangan game. Meremehkannya, mungkin menghasilkan hasil yang tidak terduga.

Pernahkah Anda menemukan permainan di mana Anda bisa berjalan menembus tembok dan bisa menembak musuh tanpa membidik mereka dengan benar? Salahkan pengembang yang bekerja pada deteksi tabrakan!

Game-game itu mungkin 3D, tapi, beruntung bagi kita, kita hanya perlu khawatir tentang dua dimensi yang mungkin bertabrakan satu sama lain sehingga praktis ada dimensi keseluruhan yang tidak salah!

Dalam permainan RGBCatcher, hanya ada total empat tabrakan yang perlu dikhawatirkan dan kita sudah berurusan dengan dua tabrakan pertama — ingat kemungkinan tabrakan antara basket dan batas kanan atau kiri layar?

Dua tabrakan terakhir melibatkan balok jatuh dan apakah mereka mengenai basket atau tanah.

Sebelum memeriksa apakah ada tabrakan aktual dengan tanah atau basket, kita akan memeriksa apakah blok telah lewat atau saat ini berada di garis y tempat basket berada.

Seperti yang Anda lihat pada gambar di bawah ini, mendasarkan deteksi tabrakan hanya pada objek koordinat mereka adalah hal yang buruk karena representasi visual dari blok telah mengenai basket sebelum koordinat cocok dengan tabrakan.


1
RGBCatcher = new function() 
2
{ 
3
	// [...] 

4
 
5
	function checkCollision(block) 
6
	{ 
7
		// If the block is not defined or not alive, return 

8
		if (block === undefined || block.alive === false) 
9
			return; 
10
 
11
		// If the block hasn't passed the vertical line the basket resides on, there's no way we are dealing with a collision yet 

12
		if (block.y + block.height < basket.y) 
13
			return; 
14
	} 
15
}

Ketika kita yakin bahwa blok secara visual sama atau pada koordinat y yang lebih tinggi daripada keranjang, kita dapat dengan aman memeriksa apakah koordinat x dari blok berada dalam kisaran yang benar.

Selain koordinat y, tidak hanya ada satu tempat di mana sebuah blok dapat bertabrakan secara horizontal dengan keranjang. Itu sebabnya kita perlu memeriksa apakah blok berada dalam kisaran lebar total keranjang untuk menentukan apakah kita sedang berhadapan dengan tabrakan atau tidak.

Kisaran keranjang tidak dapat dijelaskan hanya dalam satu nilai statis, jadi kita akan menggunakan algoritma yang sangat dasar.

Algoritma ini akan menentukan apakah posisi x blok lebih besar atau sama dengan posisi x keranjang dan apakah posisi x blok ditambah lebarnya lebih rendah dari posisi x keranjang plus lebarnya.


1
RGBCatcher = new function() 
2
{ 
3
	// [...] 

4
	 
5
	function checkCollision(block) 
6
	{ 
7
		// If the block is not defined or not alive, return 

8
		if (block === undefined || block.alive === false) 
9
			return; 
10
 
11
		// If the block hasn't passed the vertical line the basket resides on, we're not dealing with a collision (yet) 

12
		if (block.y + block.height < basket.y) 
13
			return; 
14
		 
15
		// If the block's x coordinate is in the range of the basket's width, then we've got a collision 

16
		if (block.x >= basket.x &amp;&amp; 
17
		    block.x + block.width <= basket.x + basket.width) 
18
		{ 
19
		} 
20
		// If it's not, the block has missed the basket and will thus, eventually, collide with the ground. 

21
		else 
22
		{ 
23
		} 
24
	} 
25
}

Mengedit dan menambahkan beberapa variabel game

Sebelum kita melanjutkan, pertama-tama kita harus menambahkan dan mengedit beberapa variabel yang ditemukan dalam lingkup pembungkus RGBCatcher.

Variabel yang akan diedit adalah variabel blockData. Kita akan menambahkan properti blok yang disebut kekuatan - nilai ini akan berdampak pada kesehatan pengguna jika pengguna melewatkan blok berwarna dengan benar atau jika menangkap blok yang salah warna.

Di sisi lain, jumlah kekuatan akan ditambahkan ke skor pengguna jika pengguna menangkap blok yang memiliki warna yang sama dengan basket

Dua variabel lain yang ditambahkan disebut health dan score. Variabel-variabel ini akan menahan objek yang mewakili grafik kesehatan grafik dan penghitung skor nanti, tetapi untuk saat ini mereka hanyalah nilai integer sederhana.

1
// This is a percentage which starts at 100% 

2
var health = 100; 
3
// This is just a regular integer value 

4
var score = 0; 
5
 
6
var blockData = [ 
7
	['width', 10], 
8
	['height', 10], 
9
	['ySpeed', 2], 
10
	['color', undefined], 
11
	['strength', 30] // The strength of this block *new* 

12
];

Menangkap satu blok

Karena tujuan permainan kita mengharuskan pengguna hanya menangkap balok, yang memiliki warna yang sama dengan keranjang, pertama-tama kita akan membandingkan warna balok dengan warna keranjang.

Jika mereka sama, kita mendapat tangkapan yang benar dan skor harus meningkat dengan kekuatan blok. Di sisi lain, jika blok yang salah warna menurunkan kesehatan pengguna dengan kekuatan blok.

Dalam kedua kasus, blok tersebut harus menghilang dari layar dan variabel blockOnScreen akan berkurang dengan satu

1
RGBCatcher = new function() 
2
{ 
3
	// [...] 

4
	 
5
	// By passing a reference of the block object to the function, we can use the current very block to perform our collision detection 

6
	function checkCollision(block) 
7
	{ 
8
		// [...] 

9
		 
10
		// If the block's x-coordinate is in the range of the basket's width, then we've got a collision 

11
		if (block.x >= basket.x &amp;&amp; 
12
		    block.x + block.width <= basket.x + basket.width) 
13
		{ 
14
			// Whether it's a correctly colored block or not, the current block should disappear and the amount of blocks on the screen should decrease with one 

15
			if (block.alive == true) 
16
			{ 
17
				block.alive = false; 
18
				blocksOnScreen--; 
19
			} 
20
			 
21
			// If the block's color matches the basket's current color, we've got a correct catch 

22
			if (block.color ===  basket.color) 
23
				// So give the player some points 

24
				score += block.strength; 
25
			else 
26
				// Otherwise, inflict damage to the health of the player 

27
				health -= block.strength; 
28
		} 
29
		// If it's not, the block has missed the basket and will thus, eventually, collide with the ground 

30
		else 
31
		{ 
32
		} 
33
	} 
34
}

Hilang satu blok

Jika pengguna melewatkan blok yang berwarna dengan benar, kekuatan blok harus ditimbulkan untuk kesehatan pemain. Namun, tidak ada yang salah dengan melewatkan blok warna yang salah dan jika itu terjadi permainan harus dilanjutkan tanpa tindakan lebih lanjut.

Berlawanan dengan menangkap blok, blok seharusnya tidak segera menghilang dari layar. Pertama-tama harus jatuh ke bawah port tampilan kanvas dan kemudian dihapus dari array blocks.

1
RGBCatcher = new function  
2
{ 
3
	// [...] 

4
 
5
	function checkCollision() 
6
	{ 
7
		// [...] 

8
 
9
		// If it's not, the block has missed the basket and will thus, eventually, collide with the ground 

10
		else 
11
		{ 
12
			// The player missed a correctly colored block and no damage has been inflicted yet 

13
			if (block.color === basket.color &amp;&amp; block.strength > 0) 
14
			{ 
15
				// So lets inflict damage to the health of the player 

16
				health -= block.strength; 
17
				 
18
				// To prevent this block from inflicting damage again, we set its strength to 0 

19
				block.strength = 0; 
20
			} 
21
			 
22
			// If the block's y coordinate is greater than the canvas's height, it has disappeared from the viewport and can be removed 

23
			if (block.alive === true &amp;&amp; block.y > canvas.height) 
24
			{ 
25
				block.alive = false; 
26
				blocksOnScreen--; 
27
			} 
28
		} 
29
	} 
30
}

Tambahkan blok jika diperlukan

Jadi bagaimana cara kerja metode addBlock Anda?

Pertama-tama memeriksa apakah jumlah blok yang diperlukan untuk level saat ini (blokPerLevel * level) sama dengan jumlah blok yang telah dihasilkan (blocksSpawned).

Jika tidak, objek blok baru muncul, itu akan ditambahkan ke array blocks dan blocksSpawned dan blocksOnScreen variabel akan ditingkatkan dengan satu.

Jika ya, ekspresi dijalankan untuk menentukan apakah masih ada blok pada layar (blocksOnScreen) yang harus mendarat di suatu tempat terlebih dahulu.
Jika ini bukan masalahnya dan blockOnScreen == 0, kita dapat mengatakan bahwa level telah selesai.

Metode addBlock mengembalikan true jika masih ada blok untuk diproses dan false jika jumlah blok yang benar telah dihasilkan dan dihapus dari layar.

1
RGBCatcher = new function 
2
	// [...] 

3
 
4
	function addBlock() 
5
	{ 
6
		if (blocksSpawned != blocksPerLevel * level) 
7
		{ 
8
			// Add a new block the the blocks array	 

9
			blocks[blocks.length] = new Block(blockData); 
10
			 
11
			// Both increase the amount of blocks on the screen and the amount of spawned blocks 

12
			blocksSpawned++; 
13
			blocksOnScreen++; 
14
		} 
15
		else 
16
		{ 
17
			// Check whether all blocks have been processed 

18
			if (blocksOnScreen == 0) 
19
				return false; 
20
		} 
21
		 
22
		// Return true if there's still something to work with 

23
		return true; 
24
	} 
25
}

Layar Info

Kita dapat mengimplementasikan fungsi yang baru saja ditulis di gameloop dan mulai bermain segera, tetapi bukankah jauh lebih baik untuk memiliki layar judul yang menunggu respons pengguna alih-alih segera mulai dengan blok jatuh di layar?

Mari kita menulis fungsi yang menampilkan layar judul mewah dan menunggu input dari pengguna — bilah ruang terdengar sesuai.

Kita bisa menggunakan Canvas 2D API untuk merender layar judul, tapi mengapa tidak menggunakan tag HTML dan menggunakan JavaScript untuk mengubahnya.

Memperbarui HTML

Layar judul akan diletakkan di elemen div umum yang ditandai dengan ID info karena akan berfungsi sebagai layar info juga, tidak hanya untuk menampilkan layar judul.

Dengan dimensi 200px * 26px, kita menggunakan posisi absolut dan cukup menghitung margin 'atas' dan 'kiri' dari elemen info untuk memusatkannya dengan baik di atas elemen kanvas.

Untuk posisi teratas kita menambahkan margin atas elemen #canvas ke tinggi total, minus ketinggian elemen info dan membaginya dengan dua.
Kita melakukan hal yang sama untuk posisi kiri, kecuali bahwa alih-alih menggunakan ketinggian, kita menggunakan lebar #canvas dan elemen #info untuk menghitung posisi yang benar.

1
<!DOCTYPE html> 
2
<html> 
3
<head> 
4
	<title>RGBCatcher</title> 
5
	<script src="http://tutsplus.s3.amazonaws.com/tutspremium/web-development/172_game/js/js.js"></script> 
6
	 
7
	<style> 
8
		body, html, p { 
9
			margin: 0px; 
10
			padding: 0px; 
11
		} 
12
		 
13
		#canvas { 
14
			border: 1px solid #eee; 
15
			margin: 10px; 
16
		} 
17
		#info { 
18
			position: absolute; 
19
			top: 92px; /* (10px top margin + 200px of total height for the #canvas element - 26px of total height for the #info element) / 2 */ 
20
			left: 105px; /* (10px left margin + 400px of total width for the #canvas element - 200px of total width for the #info element / 2 */ 
21
			 
22
			text-align: center; 
23
			font: 10px sans-serif; 
24
 
25
			background-color: #fff; 
26
			 
27
			width: 200px; 
28
			height: 26px; 
29
		} 
30
		 
31
		/* Some styling for the title screen */ 
32
		span.red { 
33
			color: #f00; 
34
		} 
35
		span.green { 
36
			color: #0f0; 
37
		} 
38
		span.blue { 
39
			color: #00f; 
40
		} 
41
		span.red, span.green, span.blue { 
42
			font-weight: bold; 
43
		} 
44
	</style> 
45
</head> 
46
 
47
<body> 
48
	<canvas id="canvas" width="398" height="198"></canvas> 
49
	<div id="info"></div> 
50
</body> 
51
</html>

Sekarang kita mendapatkan HTML dan CSS kita disortir lagi, kita dapat kembali ke proses yang jauh lebih menarik dari penulisan kode yang menampilkan layar judul sebenarnya.

Fungsi titleScreen

Fungsi titleScreen adalah fungsi anak (metode) pembungkus fungsi RGBCatcher.
Sebelum menuliskannya, kita perlu menambahkan dua variabel pribadi — info dan infoScreenChange.

info memegang objek DOM yang memberi kita tool yang diperlukan untuk mengubah elemen info.
Boolean infoScreenChange, yang defaultnya benar, membantu kita untuk menentukan apakah layar info harus diperbarui atau tidak.

1
// A DOM Element of the info screen 

2
var info; 
3
// Should the info screen be changed? 

4
var infoScreenChange = true;

Ringkasnya: jika layar info belum diperbarui (infoScreenChange === true), lakukanlah.
Kemudian tunggu saja tekan pada bilah spasi (keyOn[32]) untuk menyembunyikan layar info, reset game dan masukkan loop game.

1
RGBCatcher = new function() 
2
{ 
3
	// [...] 

4
	 
5
	function titleScreen() 
6
	{ 
7
		// Should the info screen be updated? 

8
		if (infoScreenChange) 
9
		{ 
10
			// Set the HTML value of the info DOM object so it displays a fancy titlescreen 

11
			info.innerHTML = '<p><span class="red">R</span><span class="green">G</span><span class="blue">B</span>Catcher</p> <p>Press spacebar to start</p>'; 
12
			 
13
			// Only update the info screen once 

14
			infoScreenChange = false; 
15
		} 
16
		 
17
		// 32 is the key code representation of a press on the spacebar 

18
		if (keyOn[32]) 
19
		{ 
20
			// Set the infoScreenChange variable to its default value again 

21
			infoScreenChange = true; 
22
			 
23
			// Set the CSS 'display' rule of the info element to none so it disappears 

24
			info.style.display = 'none'; 
25
			 
26
			// The player wants to start playing so the current 'titleScreen loop' will be cleared 

27
			clearInterval(interval); 
28
			 
29
			// Reset the game 

30
			resetGame(); 
31
			 
32
			// And enter the game loop at 30 frames per second 

33
			interval = setInterval(gameLoop, 30/1000) 
34
		} 
35
	} 
36
}

Seharusnya ada beberapa perubahan pada metode run untuk mengatur variabel info lokal diatur dan mengaitkan setInterval ke layar judul alih-alih lingkaran permainan.

1
RGBCatcher = new function() 
2
{ 
3
	// [...] 

4
	 
5
	this.run = function() 
6
	{ 
7
		// Set the global 'canvas' object to the #canvas DOM object to be able to access its width, height and other attributes are 

8
		canvas = document.getElementById('canvas'); 
9
		 
10
		// Set the local 'info' object to the #info DOM object 

11
		info = document.getElementById('info'); 
12
		 
13
		// This is where it's all about; getting a new instance of the C2A object — pretty simple huh? 

14
		context = canvas.getContext('2d'); 
15
	 
16
		// Add an eventListener for the global keydown event 

17
		document.addEventListener('keydown', function(event) 
18
		{ 
19
			// Add the keyCode of this event to the global keyOn Array 

20
			// We can then easily check if a specific key is pressed by simply checking whether its keycode is set to true 

21
			keyOn[event.keyCode] = true; 
22
		}, false); 
23
	 
24
		// Add another eventListener for the global keyup event 

25
		document.addEventListener('keyup', function(event) 
26
		{ 
27
			// Set the keyCode of this event to false, to avoid an inifinite keydown appearance 

28
			keyOn[event.keyCode] = false; 
29
		}, false); 
30
		 
31
		// Instantiate the basket object and feed it the required basketData 

32
		basket = new Basket(basketData); 
33
		 
34
		// Go to the title screen at 30 frames per second 

35
		interval = setInterval(titleScreen, 30/1000); 
36
	} 
37
}

Jangan ragu untuk mencobanya.
Meskipun belum ada blok yang jatuh, kita sekarang memiliki layar judul yang sedang ditampilkan sebelum permainan dimulai dan menghilang segera setelah pengguna menekan spasi.



Manajemen Level

Sekarang kita punya layar judul dan berjalan, sudah waktunya untuk membiarkan blok jatuh.

Sedikit ke belakang, Anda mungkin telah memperhatikan variabel level lokal yang belum terdefinisi dalam metode addBlock pribadi RGBCatcher. Variabel ini merupakan permintaan rutin manajemen level.

Mari kita definisikan bersama-sama dengan variabel baru yang disebut levelUp. Dengan boolean ini kita dapat menentukan apakah pengguna baru saja menyelesaikan level atau tidak.

1
// The level the player is currently at 

2
var level; 
3
// Has the player recently leveled up? 

4
var levelUp = false;

Melihat kembali skema pengelolaan blok, kita melihat lingkaran pembaruan, pemeriksaan tabrakan dan penambahan blok jika ini diperlukan. Karena game kita sudah memiliki loop yang berputar, yang merupakan loop game, pola yang berulang masuk ke sana.

1
RGBCatcher = new function() 
2
{ 
3
	// [...] 

4
	 
5
	function gameLoop() 
6
	{ 
7
		context.clearRect(0, 0, canvas.width, canvas.height); 
8
		 
9
		basket.update(); 
10
		 
11
		updateBlocks(); 
12
	} 
13
}

Tapi blok apa yang akan diperbarui jika tidak ada panggilan yang dilakukan ke fungsi addBlock?

Menambahkan fungsi timer

Kapan blok baru harus jatuh, tergantung pada variabel blocksSpawnSec. Kita akan menambahkan timer sederhana untuk diperiksa agar kita dapat menentukan apakah kita harus memanggil fungsi addBlock atau tidak.

Variabel yang diperlukan untuk mencapai ini, disebut frameTime dan startTime.

1
var startTime = 0; 
2
var frameTime = 0;

frameTime akan ditetapkan pada setiap putaran loop game sedangkan startTime hanya harus ditetapkan pada awal timer baru. Timer pertama ini harus mulai berjalan pada awal permainan baru.

Kita sudah memiliki fungsi yang disebut resetGame, yang disebut pada awal permainan baru, jadi kami mendefinisikan variabel startTime di sana. Sementara kita berada di dalamnya, kita mungkin juga menambahkan reset untuk variabel level.

Perhatikan bahwa fungsi yang digunakan untuk mengambil waktu saat ini, getTime, mengembalikan nilai integer yang mewakili waktu saat ini dalam milidetik sejak tahun 1970.

1
RGBCatcher = new function() 
2
{ 
3
	// [...] 

4
	 
5
	function resetGame() 
6
	{ 
7
		basket.reset(); 
8
		 
9
		health = 100; 
10
		score = 0; 
11
		 
12
		blocksSpawnSec = 2; 
13
		blocksSpawned = 0; 
14
		blocksOnScreen = 0; 
15
		blocks = []; 
16
		 
17
		level = 1; 
18
 
19
		startTime = new Date().getTime(); 
20
	} 
21
}

Sekarang kita memiliki nilai startTime awal, kita dapat dengan mudah mengetahui berapa banyak milidetik yang telah berlalu dengan hanya mengekstraksi nilai ini dari frameTime saat ini.

Jadi setelah memanggil metode updateBlocks, kita memeriksa apakah waktu frame saat ini lebih besar dari waktu mulai ditambah detik setelah mana blok baru harus muncul. Jika ini masalahnya, kita menyetel ulang timer dan melakukan panggilan ke fungsi addBlock — tergantung pada nilai baliknya, tindakan yang sesuai akan dilakukan.

1
RGBCatcher = new function() 
2
{ 
3
	// [...] 

4
	 
5
	function gameLoop() 
6
	{ 
7
		frameTime = new Date().getTime(); 
8
		 
9
		context.clearRect(0, 0, canvas.width, canvas.height); 
10
		 
11
		basket.update(); 
12
		 
13
		updateBlocks(); 
14
		 
15
		// blocksSpawnSec * 1000 because getTime() returns a value in miliseconds 

16
		if (frameTime >= startTime + (blocksSpawnSec*1000)) 
17
		{	 
18
			// If all blocks have been processed 

19
			if (addBlock() === false) 
20
			{ 
21
			} 
22
			 
23
			// Reset the timer 

24
			startTime = frameTime; 
25
		} 
26
	} 
27
}

Naik level

Jika fungsi addBlock mengembalikan false, kita dapat mengatakan bahwa level saat ini telah selesai, jadi mengatur variabel levelUp menjadi true.

Kita juga meningkatkan kesulitan permainan dengan sedikit mengurangi waktu antara blok jatuh (blocksSpawnSec) dan sedikit meningkatkan kecepatan di mana blok jatuh (blockData['ySpeed']). In-and decrements ini adalah bagian dari permainan, jangan ragu untuk menyesuaikannya nanti jika Anda pikir ini terlalu rendah atau terlalu tinggi.

Ketika level telah selesai, beberapa variabel harus direset. Rutin ini tidak persis sama dengan prosedur reset game, jadi menulis fungsi yang pas untuk itu adalah hal berikutnya yang akan kita lakukan.

1
RGBCatcher = new function() 
2
{ 
3
	// [...] 

4
	 
5
	function gameLoop() 
6
	{ 
7
		frameTime = new Date().getTime(); 
8
		 
9
		context.clearRect(0, 0, canvas.width, canvas.height); 
10
		 
11
		basket.update(); 
12
		 
13
		updateBlocks(); 
14
		 
15
		// blocksSpawnSec * 1000 because the timer values are in miliseconds 

16
		if (frameTime >= startTime + (blocksSpawnSec*1000)) 
17
		{	 
18
			// If all blocks have been added 

19
			if (addBlock() === false) 
20
			{ 
21
				// The player should go up a level 

22
				levelUp = true; 
23
				level++; 
24
				 
25
				// Increase difficulty 

26
				blocksSpawnSec *= 0.99; 
27
				blockData['ySpeed'] *= 1.01; 
28
				basketData['xSpeed'] *= 1.02; 
29
				 
30
				// Reset level specific variables 

31
				resetLevel();	 
32
			} 
33
			 
34
			// The timer is finished, reset it 

35
			startTime = frameTime; 
36
		} 
37
	} 
38
}

Mengatur ulang level

Mengingat konsep permainan, warna dan posisi basket harus diatur ulang di awal level baru. Untuk memberi penghargaan kepada pemain karena menyelesaikan level, kita akan mengatur ulang kesehatannya.

Jelas, variabel blocksSpawned, blocksOnScreen dan blocks juga harus di-reset.

1
RGBCatcher = new function() 
2
{	 
3
	// [...] 

4
	 
5
	function resetLevel() 
6
	{ 
7
		basket.reset(); 
8
		 
9
		health = 100; 
10
 
11
		blocksSpawned = 0; 
12
		blocksOnScreen = 0; 
13
		blocks = [];		 
14
	} 
15
}

Game Over

Sekarang kita memiliki implementasi resetLevel, kita dapat melanjutkan bekerja pada loop game.

Apa yang masih hilang darinya adalah pemeriksaan kesehatan pengguna untuk menentukan apakah kesehatannya masih cukup untuk melanjutkan, dan rutin yang membuat pengguna tahu bahwa level baru sedang dimulai.

Hal pertama relatif sederhana, jadi mari kita mulai dengan itu. Pada dasarnya, kita hanya perlu memeriksa apakah kesehatan pemain lebih rendah dari 1.

1
RGBCatcher = new function() 
2
{ 
3
	// [...] 

4
	 
5
	function gameLoop() 
6
	{ 
7
		frameTime = new Date().getTime(); 
8
		 
9
		if (health < 1) 
10
		{ 
11
			basket.alive = false; 
12
			 
13
			// Game over 

14
		} 
15
		 
16
		// [...] 

17
	} 
18
}

Jadi bagaimana seharusnya prosedur game-over dilaksanakan? Pada titik di mana pengguna memiliki kurang dari 1/100 HP, gim harus berakhir dan game pesan harus ditampilkan.

Ini paling baik dilakukan dengan berhenti dari loop game saat ini dan memasukkan game baru di atas loop yang akan mem-flash pesan untuk jumlah waktu tertentu, tiga detik terdengar baik, setelah itu layar judul ditampilkan lagi.

1
RGBCatcher = new function() 
2
{ 
3
	// [...] 

4
	 
5
	function gameLoop() 
6
	{ 
7
		frameTime = new Date().getTime(); 
8
		 
9
		if (health < 1) 
10
		{ 
11
			basket.alive = false; 
12
			 
13
			// Abort the game loop and set a new loop for the game over screen 

14
			clearInterval(interval); 
15
			interval = setInterval(gameOverScreen, 30/1000); 
16
			 
17
			return; 
18
		} 
19
		 
20
		// [...] 

21
	} 
22
}

Fungsi gameOverScreen baru harus ditentukan yang pertama membersihkan seluruh kanvas, menampilkan pesan permainan-lebih-dan setelah tiga detik berlalu, kembali ke layar judul.

Game-over-message akan diletakkan di layar info. Menampilkan pesan hanya selama tiga detik mudah dicapai dengan menggunakan fungsi penghitung waktu yang harus Anda kenal sekarang.

1
RGBCatcher = new function() 
2
{ 
3
	// [...] 

4
	 
5
	function gameOverScreen() 
6
	{ 
7
		frameTime = new Date().getTime(); 
8
		 
9
		// Should the info screen be changed? 

10
		if (infoScreenChange) 
11
		{ 
12
			// First clear the canvas with the basket and blocks from the background 

13
			context.clearRect(0, 0, canvas.width, canvas.height); 
14
			 
15
			// Change the text of the info screen and show it 

16
			info.innerHTML = '<p>Game over!</p>'; 
17
			info.style.display = 'block'; 
18
			 
19
			// Do not update the info screen again 

20
			infoScreenChange = false; 
21
		} 
22
		 
23
		// If three seconds have passed 

24
		if (frameTime > startTime + (3*1000)) 
25
		{	 
26
			// A new info screen should be pushed next time 

27
			infoScreenChange = true; 
28
			 
29
			// Reset the timer 

30
			startTime = frameTime; 
31
			 
32
			// Quit this loop and set a new the loop for the title screen 

33
			clearInterval(interval); 
34
			interval = setInterval(titleScreen, 30/1000); 
35
		} 
36
	} 
37
}

Bersiaplah untuk level 2!

Apa yang hilang dari loop game kita yang diperbarui adalah rutin yang menangani flashing pesan naik level.
Kita sudah memiliki variabel yang disebut levelUp yang membantu kita untuk menentukan apakah pesan harus ditampilkan atau tidak.

1
RGBCatcher = new function()  
2
{ 
3
	// [...] 

4
 
5
	function gameLoop() 
6
	{ 
7
		frameTime = new Date().getTime(); 
8
		 
9
		if (health < 1) 
10
		{ 
11
			basket.alive = false; 
12
			 
13
			// Abort the game loop and set a new loop for the game over screen 

14
			clearInterval(interval); 
15
			interval = setInterval(gameOverScreen, 30/1000); 
16
			 
17
			return; 
18
		} 
19
		 
20
		if (levelUp) 
21
		{ 
22
			return; 
23
		} 
24
		 
25
		// [...] 

26
	} 
27
}

Jadi kita cukup menyalin kode gameOverScreen ke if-statement, ubah pesan yang akan di-flash dan tindakan dilakukan setelah tanda tiga detik tercapai.

1
RGBCatcher = new function()  
2
{ 
3
	// [...] 

4
 
5
	function gameLoop() 
6
	{ 
7
		frameTime = new Date().getTime(); 
8
		 
9
		if (health < 1) 
10
		{ 
11
			basket.alive = false; 
12
			 
13
			// Abort the game loop and set a new loop for the game over screen 

14
			clearInterval(interval); 
15
			interval = setInterval(gameOverScreen, 30/1000); 
16
			 
17
			return; 
18
		} 
19
		 
20
		if (levelUp) 
21
		{ 
22
			if (infoScreenChange) 
23
			{ 
24
				// First clear the canvas with the basket and blocks from the background 

25
				context.clearRect(0, 0, canvas.width, canvas.height); 
26
			 
27
				// Change the text of the info screen and show it 

28
				info.innerHTML = '<p>Level ' + (level-1) + ' cleared!</p><p>Get ready for level ' + level + '!</p>'; 
29
				info.style.display = 'block'; 
30
			 
31
				// Do not update the info screen again 

32
				infoScreenChange = false; 
33
			} 
34
		 
35
			// If three seconds have passed 

36
			if (frameTime > startTime + (3*1000)) 
37
			{	 
38
				// Flashing of the message has been completed 

39
				levelUp = false; 
40
				 
41
				// Hide the info screen and force an update next time 

42
				info.style.display = 'none'; 
43
				infoScreenChange = true; 
44
				 
45
				// Set a new timer 

46
				startTime = frameTime; 
47
			} 
48
			 
49
			return; 
50
		} 
51
 
52
		// [...] 

53
	} 
54
}

Jangan ragu untuk mencoba game ini lagi. Ini cukup fungsional, kecuali bahwa masih ada dua hal yang hilang: penghitung skor dan bar kesehatan. Kita membuat kemajuan, sekarang!



Fungsi Basis yang Dapat Dihitung

Penghitung skor dan bilah kesehatan memiliki satu kesamaan: menghitung nilai tertentu. Oleh karena itu adalah ide yang baik bagi mereka untuk memiliki objek yang dapat mereka warisi dari: objek dasar yang dapat dihitung — seperti yang dilakukan dengan definisi dasar bergerak untuk objek keranjang dan blok.

Dimulai dengan fakta bahwa objek kesehatan dan skor harus menyediakan kita dengan metode untuk mengubah nilai mereka, mereka juga harus memiliki metode yang mengembalikan nilai saat ini dari objek yang dipakai dan mereka berdua akan memiliki pembaruan dan gerakan rutin.

Construktor

Construktor dari basis yang dapat dihitung, seharusnya hanya mendefinisikan properti yang dibagikan oleh semua pewaris.

Ini adalah x dan y untuk penentuan posisi, properti value yang menyimpan nilai yang dapat dihitung (seperti jumlah kesehatan), properti targetValue, dan properti speed.

Properti speed dan targetValue akan digunakan untuk animasi grafis, tetapi kita akan kembali ke nanti.

1
var Countable = function() 
2
{ 
3
	this.x = 0; 
4
	this.y = 0; 
5
	 
6
	this.speed = 2; 
7
	 
8
	this.value = 0; 
9
	this.targetValue = 0; 
10
} 
11
Countable.prototype = { 
12
};

Metode reset harus dideklarasikan oleh pewaris yang akan mengatur properti objek ke nilai kustom untuk inisialisasi.

Memperbarui objek yang Dapat Dihitung

Metode pembaruan dari prototipe yang dapat dihitung identik dengan metode pembaruan dari prototipe yang dapat bergerak kecuali bahwa itu tidak memerlukan pemeriksaan 'masih hidup'.

1
Countable.prototype = { 
2
	update: function()  
3
	{ 
4
		this.move(); 
5
		this.draw(); 
6
	} 
7
};

Mengatur nilai objek yang Dapat Dihitung

Properti TargetValue dan value akan digunakan untuk melakukan animasi grafis dasar di mana bilah kesehatan atau penghitung skor meluncur dari value lamanya ke targetValue baru.

Hal ini dicapai dengan tidak secara langsung mengatur nilai properti, tapi dengan menggunakan sub-stasiun yang disebut targetValue yang dapat kita gunakan untuk membuat animasi.

1
Countable.prototype = { 
2
	// [...] 

3
	 
4
	set: function(amount) 
5
	{ 
6
		this.targetValue += amount; 
7
	} 
8
};

Dengan secara langsung menambahkan jumlah perubahan yang diinginkan ke nilai target, kita juga dapat menggunakan jumlah negatif. Ini memberi kita kemampuan untuk mengurangi, tetapi juga menambahkan jumlah tertentu ke nilai target.

Memindahkan objek yang Dapat Dihitung

Proses perpindahan objek yang dapat dihitung bukan tentang mengubah koordinatnya, seperti yang dilakukan dengan blok dan move rutin keranjang, tetapi tentang mengubah value saat ini sehingga pada akhirnya akan sama dengan targetValue.

Praktis ini akan menghasilkan proses properti value sedikit meningkat atau menurun nilainya sampai pada nilai yang sama dengan targetValue.

Karena properti value akan digunakan untuk menggambar skor atau bilah kesehatan, mengubahnya dari waktu ke waktu alih-alih menetapkan secara langsung, menghasilkan animasi.

Pertama-tama kita harus menentukan apakah nilainya sudah sama dengan nilai target. Jika ini masalahnya, tidak ada yang terjadi. Jika tidak, kecepatan harus ditambahkan (value < targetValue) dari nilai aktual (value > targetValue).

Segera setelah perbedaan antara target dan nilai aktual lebih rendah dari kecepatan animasi, nilai tersebut harus ditetapkan ke nilai target karena menambah atau mengurangi kecepatan dari nilai akan lebih dari cukup.

1
Countable.prototype = { 
2
	// [...] 

3
	 
4
	move: function() 
5
	{ 
6
		// If the difference between the target and actual value is lower than the animation speed, set the value to the target value 

7
		if (Math.abs(this.value - this.targetValue) < this.speed) 
8
			this.value = this.targetValue; 
9
		else if (this.targetValue > this.value) 
10
			this.value += this.speed; 
11
		else 
12
			this.value -= this.speed; 
13
	} 
14
};

Definisi fungsi akhir

Sekarang kita memiliki basis definisi fungsi yang dapat dihitung, pewaris hanya perlu mendefinisikan metode reset dan draw... dan siap untuk digunakan! Seberapa nyaman itu?

1
var Countable = function() 
2
{ 
3
	this.x = 0; 
4
	this.y = 0; 
5
	 
6
	this.speed = 2; 
7
	 
8
	this.value = 0; 
9
	this.targetValue = 0; 
10
} 
11
Countable.prototype = { 
12
	update: function()  
13
	{ 
14
		this.move(); 
15
		this.draw(); 
16
	}, 
17
	 
18
	change: function(amount) 
19
	{ 
20
		this.targetValue += amount; 
21
	}, 
22
	 
23
	move: function() 
24
	{ 
25
		// If the difference between the target and actual value is lower than the animation speed, set the value to the target value 

26
		if (Math.abs(this.value - this.targetValue) < this.speed) 
27
			this.value = this.targetValue; 
28
		else if (this.targetValue > this.value) 
29
			this.value += this.speed; 
30
		else 
31
			this.value -= this.speed; 
32
	} 
33
};

Objek Kesehatan

Jelas, objek kesehatan bertanggung jawab untuk melacak titik kesehatan pengguna dan menggambar bar kesehatan ke layar.

Karena fungsi terhitung dasar sudah menyediakan update, change dan get, kita hanya perlu mendefinisikan metode reset dan draw.

Constructor

Meskipun value dan targetValue properti dari objek ini harus diatur ulang dalam metode reset, konstruktor mendefinisikan statis x dan y properti.


Jika kita melihat kembali sketsa itu, kita melihat bahwa kita berencana untuk menempatkan bar kesehatan di sudut kanan atas dengan beberapa margin tambahan dari batas kanvas.

Ini dicapai dengan menyetel properti x ke lebar kanvas dikurangi dengan lebar bar kesehatan (52px) dan margin kanan yang diinginkan (10px). Kedua, kita akan mengatur properti y menjadi 10, yang tidak lebih dari margin atas.

Kita juga melakukan panggilan ke metode reset yang belum ditentukan yang akan menetapkan properti value dan targetValue saat instantiasi.

1
var Health = function()  
2
{ 
3
	Countable.call(this); 
4
	 
5
	this.x = canvas.width - 52 - 10; 
6
	this.y = 10; 
7
	 
8
	this.reset(); 
9
} 
10
Health.prototype = new Countable();

Menyetel ulang objek Kesehatan

Objek terhitung dasar sudah mendefinisikan beberapa properti, tetapi karena tidak semua pewaris berbagi nilai properti yang persis sama, pewaris metode reset akan mengatur properti tersebut ke nilai yang benar.

Karena kita ingin memulai permainan dengan animasi pengisian health bar, kita akan menetapkan nilai awal kesehatan pada 1 dan nilai target pada 100 alih-alih membiarkannya pada nilai default.

1
Health.prototype.reset = function()  
2
{ 
3
	// If we would leave it at a default of 0, the game would immediately end as it equals a loss of the game 

4
	this.value = 1; 
5
	this.targetValue = 100; 
6
}

Drawing Health object

Bilah kesehatan terdiri dari tiga gambar yang berkolaborasi; sebuah wadah, health bar aktual yang lebarnya berfluktuasi dan sepotong kecil teks untuk interface.

Container

Wadah ini cukup sederhana untuk digambar meskipun memperkenalkan metode baru Canvas 2D API: metode strokeRect; metode untuk menggambar persegi panjang dengan batas yang ditentukan oleh gaya goresan saat ini.

strokeRect membutuhkan parameter yang persis sama dengan fillRect: x, y, lebar dan tinggi. Untuk menentukan stroke warnanya, Canvas 2D API milik strokeStyle-nya digunakan. Kita akan membiarkannya pada nilai default hitam.

Sama seperti metode fillRect, warna isian dapat didefinisikan dengan menyetel properti fillStyle.

1
Health.prototype.draw = function() 
2
{ 
3
	// The container 

4
	context.fillStyle = '#fff'; 
5
	context.strokeRect(this.x, this.y, 50 + 2, 5 + 2); 
6
}

Perhatikan bahwa kita menambahkan dua piksel pada lebar dan tinggi wadah. Ini untuk benar-benar membuat wadah 50x5 piksel karena Canvas 2D API menempatkan batas di dalam lebar dan tinggi yang ditentukan.

Health bar


Animasi logikanya sudah ditangani oleh metode move yang diwarisi dari objek yang dapat dihitung. Dalam kombinasi dengan warna yang bervariasi, ini akan memberikan perubahan yang bagus untuk persentase kesehatan transisi.

Mengatur Canvas 2D API properti fillStyle sesuai dengan persentase kesehatan sangat sederhana ketika menggunakan beberapa pernyataan if.

1
Health.prototype.draw = function() 
2
{ 
3
	// The container 

4
	context.fillStyle = '#fff'; 
5
	context.strokeRect(this.x, this.y, 50 + 2, 5 + 2); 
6
	 
7
	// The bar 

8
	if (this.value >= 50) 
9
		context.fillStyle = '#00ff00'; 
10
	else if (this.value >= 25) 
11
		context.fillStyle = '#fa6600'; 
12
	else if (this.value >= 0) 
13
		context.fillStyle = '#ff0000'; 
14
}

Proses menggambar bar kesehatan tidak jauh lebih sulit untuk dilakukan; kita hanya perlu menggambar persegi panjang yang diisi (fillRect) pada posisi yang sama dari wadah, kecuali bahwa kita menambahkan satu piksel untuk lebar dan tinggi untuk menempatkannya di dalam batas-batas wadah.

Lebar bar kesehatan hanya ditemukan dengan mengalikan persentase kesehatan saat ini (value) dengan rasio skala wadah ruang kosong (50) dan jumlah kesehatan maksimum yang mungkin (100). 5 piksel harus cukup sebagai tinggi.

1
Health.prototype.draw = function() 
2
{ 
3
	// The container 

4
	context.fillStyle = '#fff'; 
5
	context.strokeRect(this.x, this.y, 50 + 2, 5 + 2); 
6
	 
7
	// The bar 

8
	if (this.value >= 50) 
9
		context.fillStyle = '#00ff00'; 
10
	else if (this.value >= 25) 
11
		context.fillStyle = '#fa6600'; 
12
	else if (this.value >= 0) 
13
		context.fillStyle = '#ff0000'; 
14
		 
15
	context.fillRect(this.x + 1, this.y + 1, this.value * (50/100), 5); 
16
}

'HP' text

Rendering teks ke kanvas dapat dicapai dengan memanfaatkan metode fillText atau strokeText dari Canvas 2D API. Definisi font dapat diatur dengan properti font (standarnya adalah '10px sans-serif').

Sama seperti persegi panjang, properti fillStyle dan strokeStyle digunakan untuk masing-masing mengatur warna fill atau border. Selain itu, ada juga properti untuk menyetel perataan (textAlign, default ke 'start') dan baseline (textBaseline, default ke 'abjad') teks.

Kita akan menggunakan metode fillText karena kita tidak ingin memiliki batas di sekitar teks. fillText membutuhkan parameter berikut: sepotong teks untuk dicetak dan posisi x dan y.

Untuk menyederhanakan proses menempatkan teks di tempat yang tepat, kita akan menetapkan teks sebagai "top". Masih ada beberapa yang dimasukkan sebelum teks dibuat dan oleh karena itu kita akan mengurangi 3 piksel dari posisi y untuk meletakkannya sejajar dengan bilah kesehatan.


1
Health.prototype.draw = function() 
2
{ 
3
	// [...] 

4
	 
5
	// The text 

6
	context.fillStyle = '#000'; 
7
	context.textBaseline = 'top'; 
8
	context.fillText('HP', this.x - 25, this.y - 3); 
9
}

Definisi fungsi akhir

Dan dengan itu, kita telah menyelesaikan objek kesehatan.
Elemen terakhir bagi kita untuk disatukan sebelum kita dapat menyebutnya tujuan adalah objek skor.

1
var Health = function()  
2
{ 
3
	Countable.call(this); 
4
	 
5
	this.x = canvas.width - 52 - 10; 
6
	this.y = 10; 
7
} 
8
Health.prototype = new Countable(); 
9
 
10
Health.prototype.reset = function() 
11
{ 
12
	// If we would leave it at a default of 0, the game would immediately end as it equals a loss of the game 

13
	this.value = 1; 
14
	this.targetValue = 100; 
15
} 
16
Health.prototype.draw = function() 
17
{ 
18
	// The container 

19
	context.fillStyle = '#fff'; 
20
	context.strokeRect(this.x, this.y, 50 + 2, 5 + 2); 
21
	 
22
	// The bar 

23
	if (this.value >= 50) 
24
		context.fillStyle = '#00ff00'; 
25
	else if (this.value >= 25) 
26
		context.fillStyle = '#fa6600'; 
27
	else if (this.value >= 0) 
28
		context.fillStyle = '#ff0000'; 
29
		 
30
	context.rect(this.x + 1, this.y + 1, this.value * (50/100), 5); 
31
 
32
	// The text 

33
	context.fillStyle = '#000'; 
34
	context.textBaseline = 'top'; 
35
	context.fillText('HP', this.x - 25, this.y - 3); 
36
}

Obyek Skor

Objek skor mewarisi dari objek dasar yang dapat dihitung yang, sekali lagi, memberi kita keuntungan memiliki sedikit metode untuk menulis. Ini berarti kita hanya perlu menulis dua metode.

Satu untuk menggambar teks 'PT' (tangan pendek untuk poin) dan value saat ini ke kanvas dan satu untuk mengatur ulang properti objek sehingga pemain akan mulai segar di awal level baru.

Constructor

Karena posisi penghitung skor statis dan tidak perlu diatur ulang pada awal gim baru, tidak masalah untuk menentukannya di konstruktor.

Sedangkan posisi x harus sama dengan bilah kesehatan untuk menyelaraskan interface dengan baik, posisi y harus sedikit lebih rendah. Ini turun menjadi 10 piksel untuk margin atas, 7 piksel tambahan untuk bar kesehatan tingginya dan 5 piksel lainnya untuk margin antara bar kesehatan dan penghitung skor.

1
var Score = function()  
2
{ 
3
	Countable.call(this); 
4
	 
5
	this.x = canvas.width - 50 - 10; 
6
	this.y = 10 + 7 + 5; 
7
} 
8
Score.prototype = new Countable();

Menyetel ulang objek Skor

Metode reset dari objek Score hanya boleh dipanggil pada awal permainan baru karena skor harus diambil bersamaan dengan level yang pemain selesaikan - pengaturan value dan properti targetValue ke nol sudah cukup.

1
Score.prototype.reset = function()  
2
{ 
3
	this.value = this.targetValue = 0; 
4
}

Menggambar objek Skor

Seluruh fase gambar objek skor terdiri dari tidak lebih dari meletakkan dua buah teks di kanvas; 'PT' dan skor saat ini.

Teks 'PT' dapat diposisikan seperti yang dilakukan dengan teks 'HP'; mengubah properti x yang sudah didefinisikan sedikit untuk memposisikannya dengan spasi putih dari penghitung skor yang sebenarnya.

1
Score.prototype.draw = function() 
2
{ 
3
	context.textBaseline = 'top'; 
4
	context.fillStyle = '#000'; 
5
	context.fillText(this.value, this.x, this.y); 
6
	context.fillText('PT', this.x - 25, this .y); 
7
}

Definisi fungsi akhir

Itu untuk objek skor.
Mari kita lanjutkan ke tahap akhir dan benar-benar menerapkan objek kesehatan dan skor ke dalam game.

1
var Score = function()  
2
{ 
3
	Countable.call(this); 
4
	 
5
	this.x = canvas.width - 52 - 10; 
6
	this.y = 10 + 7 + 5; 
7
} 
8
Score.prototype = new Countable(); 
9
 
10
Score.prototype.reset = function()  
11
{ 
12
	this.value = this.targetValue = 0; 
13
} 
14
Score.prototype.draw = function() 
15
{ 
16
	context.textBaseline = 'top'; 
17
	context.fillStyle = '#000'; 
18
	context.fillText(this.value, this.x, this.y); 
19
	context.fillText('PT', this.x - 25, this .y); 
20
}

Menyatukan Semuanya

Untuk benar-benar mengintegrasikan penghitung kesehatan dan skor ke dalam game, kita harus memperbarui pembungkus fungsi master RGBCatcher.

Dimulai dengan metode run, kita harus menambahkan dua baris untuk itu untuk instantiate objek penghitung skor dan kesehatan baru.

1
RGBCatcher = new function() 
2
{ 
3
	// [...] 

4
	 
5
	this.run = function() 
6
	{ 
7
		// [...] 

8
		 
9
		basket = new Basket(basketData); 
10
		health = new Health(); 
11
		score = new Score(); 
12
		 
13
		// [...] 

14
	} 
15
}

Dalam metode resetGame dan resetLevel, baris yang mengatur variabel kesehatan dan skor menjadi nol, ketika nilai integer masih sederhana, harus diganti dengan panggilan ke kesehatan dan mencetak objek metode reset mereka.

1
score.reset(); // old definition: score = 0; 

2
health.reset(); // old definition: health = 100;

Metode gameLoop rutin untuk menentukan apakah pengguna masih memiliki cukup kesehatan untuk terus bermain, masih bekerja dengan variabel health seolah-olah itu adalah bilangan bulat (health).

1
RGBCatcher = new function() 
2
{ 
3
	// [...] 

4
	 
5
	function gameLoop() 
6
	{ 
7
		// [...] 

8
 
9
		if (health.value < 1) 
10
		{ 
11
			clearInterval(interval); 
12
			interval = setInterval(gameOver, 1000/targetFPS); 
13
		} 
14
		 
15
		// [...] 

16
	} 
17
}

Untuk metode gameLoop juga harus ditambahkan panggilan ke health dan skor objek metode update mereka. Ini cocok di bawah panggilan untuk metode update objek basket.

1
RGBCatcher = new function() 
2
{ 
3
	// [...] 

4
	 
5
	function gameLoop() 
6
	{ 
7
		// [...] 

8
 
9
		basket.update(); 
10
		health.update(); 
11
		score.update(); 
12
		 
13
		// [...] 

14
	} 
15
}

Hal terakhir yang harus kita ubah adalah kenaikan dan penurunan panggilan ke nilai integer kesehatan dan skor lama dalam fungsi checkCollision di bungkus RGBCatcher.

Kedua variabel itu tidak lagi bilangan bulat dan karena itu kita harus menggunakan kesehatan dan objek skor metode change mereka untuk masing-masing menimbulkan kerusakan atau menambah poin ke skor.

1
score += block.strength;; 
2
// becomes 

3
score.change(block.strength); 
4
 
5
// and 

6
health -= block.strength; 
7
// becomes 

8
health.change(- block.strength);

Dan kita sudah selesai!


Kesimpulan

Dengan menggunakan akal sehat dan membagi elemen permainan menjadi tugas yang terpisah, tidak terlalu sulit untuk meretas game dasar menggunakan teknologi HTML5.

Jangan ragu untuk terus melanjutkan menjelajahi dunia luar biasa dari Canvas 2D API. Misalnya, Anda dapat mencoba mengubah fase gambar keranjang sehingga sebenarnya ada celah.

Anda juga bisa memikirkan untuk meningkatkan permainan dengan menambahkan blok, yang akan meningkatkan kesehatan pengguna atau menambah skor teratas dengan memanfaatkan fungsionalitas penyimpanan lokal HTML5.

Apa pun yang Anda lakukan, bersenang-senanglah dengan itu!

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 Web Design 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.