Html5 – Tạo menu và chuyển đổi giữa các màn hình Game

Mỗi game được làm ra đều cần có màn hình chào mừng và các menu để giúp người chơi chuyển đổi giữa các màn hình khác nhau như: giới thiệu, chơi game, hướng dẫn, tùy chọn. Từ yêu cầu của một bạn, sẽ hướng dẫn cách thực hiện phần này một cách đơn giản và nhanh chóng.

Lớp MenuItem

Mỗi đối tượng MenuItem tương ứng với một mục menu trên màn hình. Một đối tượng thế này có thể chỉ cần 4 giá trị xác định tọa độ và kích thước. Tuy nhiên để dễ thay đổi và phát triển hơn. Tôi tạo thêm một số thuộc tính và cung cấp một sự kiện onclick cho nó.

Dựa vào thuộc tính isMouseOver, ta sẽ thay đổi màu sắc hiển thị của MenuItem mỗi khi chuột được hover. Phần này khá đơn giản, chỉ cần coi đây là một cấu trúc dữ liệu để lưu các giá trị của một hình chữ nhật cùng với phương thức vẽ để hiển thị nó.

function MenuItem(data){
	this.left = data.left || 0;
	this.top = data.top || 0;
	this.width = data.width || 100;
	this.height = data.height || 30;
	this.text = data.text || "Menu Item";
	this.onclick = data.onclick;

	this.right = this.left+this.width;
	this.bottom = this.top+this.height;
	this.centerX = this.left+this.width/2;
	this.centerY = this.top+this.height/2;

	this.isMouseOver = false;

	this.update = function(){

	};
	this.draw = function(context){
		context.font = "16px Arial";
		context.textAlign = "center";
		if(this.isMouseOver)
			context.fillStyle = "rgba(255,255,255,0.7)";
		else
			context.fillStyle = "rgba(255,255,255,0.2)";

		context.fillRect(this.left,this.top,this.width,this.height);

		context.fillStyle = "white";
		context.fillText(this.text,this.centerX,this.centerY);
		context.strokeRect(this.left,this.top,this.width,this.height);
	};
	this.contain = function(x,y){
		return !(x<this.left || x>this.right || y<this.top || y>this.bottom);
	};
}

Lớp Screen

Lớp này là phần chính của tạo nên một game. Một game có thể được tạo ra từ một hoặc nhiều Screen và người chơi có thể chuyển qua lại giữa các Screen bằng một vài nút bấm nào đó (trong ví dụ này là MenuItem).

Lớp Screen này là nơi quản lý chính và có thể coi là một game thu nhỏ trong ứng dụng. Giống như mọi ví dụ và mọi đối tượng game tôi từng thực hiện, lớp này sẽ gồm hai phương thức chính là update() và draw(). Bên cạnh đó ta có thể bổ sung các phương thức để bắt đầu và kết thúc game. Ví dụ như start() và stop().
Vì đây là ví dụ tập trung vào việc thiết kế menu và chuyển đổi giữa các màn hình, các đối tượng Screen tôi tạo chỉ chứa các MenuItem.

Chú ý: Vì mỗi lớp Screen sẽ đăng kí các event onmousemove, onclick cho canvas, nên khi start() một Screen, bạn cần phải đăng kí các event này lại các event này. Nếu không nó sẽ bị overwrite bởi event từ các Screen khác.

var CELL_SIZE = 10;
var FPS = 10	;
var WIDTH = 400;
var HEIGHT = 400;

function Screen(canvas){
	var timer;
	var width = canvas.width;
	var height = canvas.height;
	var context = canvas.getContext("2d");
	this.items = [];
	// this method/event is actived in the end of draw() method
	this.afterDraw = null;

	this.update = function(){
		for(var i=0;i<this.items.length;i++){
			this.items[i].update();
		}
	};
	this.draw = function(){

		context.fillStyle = "black";
		context.fillRect(0,0,width,height);
		for(var i=0;i<this.items.length;i++){
			this.items[i].draw(context);
		}

		if(this.afterDraw)
			this.afterDraw(context);
	};
	this.start = function(){
		this.stop();
		var self = this;
		// register events
		canvas.onclick = function(e){
			// raise the onclick event of each MenuItem when it is clicked
			var x = e.pageX - this.offsetLeft;
			var y = e.pageY - this.offsetTop;
			for(var i=0;i<self.items.length;i++){
				if(self.items[i].onclick && self.items[i].contain(x,y))
					self.items[i].onclick(x,y);
			}
		};
		canvas.onmousemove = function(e){
			// change the isMouseOver property of each MenuItem
			var x = e.pageX - this.offsetLeft;
			var y = e.pageY - this.offsetTop;
			canvas.style.cursor = 'default';
			for(var i=0;i<self.items.length;i++){
				self.items[i].isMouseOver = self.items[i].contain(x,y);
				// change the cursor type to hand
				if(self.items[i].isMouseOver)
					canvas.style.cursor = 'pointer';
			}
		};
		timer = setInterval(function(){
			self.update();
			self.draw();
		},1000/FPS);
	};
	this.stop = function(){
		if(timer)
			clearInterval(timer);
		timer = null;
	};
	this.addItem = function(item){
		this.items.push(item);
	};
}

Kiểm tra kết quả

Vậy là xong hai lớp chính của ví dụ, bạn tạo một file html với tên bất kì để test với nội dung bên dưới.

Html5-Canvas-Screen-Menu-Game

<html>
  <head>
  <title></title>

	<script src="Screen.js"></script>
	<script src="MenuItem.js"></script>
    <script>

	window.onload = function()
	{
		var width = 400;
		var height = 400;

		var canvas = document.getElementById("canvas");
		canvas.width = 400;
		canvas.height = 400;

		// create the help screen
		var helpScreen,welcomeScreen;
		helpScreen = new Screen(canvas);
		helpScreen.afterDraw = function(context){
			context.fillStyle = "white";
			context.fillText("HELP",200,30);
		};
		helpScreen.addItem(new MenuItem({
				left: 100,
				top: 180,
				width: 200,
				height: 40,
				text: "Back",
				onclick: function(){
					// back to welcome screen
					helpScreen.stop();
					welcomeScreen.start();
				}
			}));

		// create the welcome screen
		welcomeScreen = new Screen(canvas);
		var titles = ["Play","Help","Visit My Blogs"];

		for(var i=0;i<titles.length;i++){
			welcomeScreen.addItem(new MenuItem({
				left: 100,
				top: 100+50*i,
				width: 200,
				height: 40,
				text: titles[i]
			}));
		}
		welcomeScreen.items[0].onclick = function(){
			alert("You clicked the Play item.");
		};
		welcomeScreen.items[1].onclick = function(){
			helpScreen.start();
			welcomeScreen.stop();
		};
		welcomeScreen.items[2].onclick = function(){
			window.open('http://yinyangit.wordpress.com', '_blank');
		};
		welcomeScreen.start();
	}
    </script>
  </head>

  <body>
	<canvas id="canvas" tabindex="0" style="margin:0px; border: 1px solid"> </canvas>
  </body>
</html>