HTML5-Canvas: Viết game Mario – Part 4

Html5-canvas-mario-part4Trong các phần trước (1, 2, 3), ta đã tạo đã được một giao diện game tương đối hoàn chỉnh và một vài tính năng cơ bản. Trong phần này tôi sẽ bổ sung thêm một số tính năng và tập trung vào việc thiết kế các level cho game.

Các cập nhật

– Thay đổi nền bản đồ.
– Giảm chiều cao của map và GRAVITY để giảm bớt tốc độ rơi.
– Thêm khả năng bắn đạn cho mario.
– Thiết kế bản đồ với 3 level khác nhau.
– Sửa chức năng tính điểm.
– Thêm lâu đài vào bản đồ để làm đích.

Xem Demo.

Html5-canvas-mario-part4

Chức năng bắn đạn

Cùng với chức năng, ta cũng cần tạo ra các “invisible flower” trong các viên gạch. Phương pháp thực hiện tương tự như các phần trước, chỉ cần vẽ 1 bông hoa lên phía trên viên gạch bị phá.

// Map.collide() method:
// ...
else if(b.type==FLOWER_BRICK) // show the hidden flower
{
	data[b.col+(b.row-1)*COLS] = FLOWER;
	itemsSprite.sprites.flower.draw(context,b.left,b.top-CELL_SIZE,CELL_SIZE,CELL_SIZE);

	data[b.col+b.row*COLS] = HARD_BRICK;
	itemsSprite.sprites.hard_brick.draw(context,b.left,b.top,CELL_SIZE,CELL_SIZE);
}
// ...

Việc tiếp theo cần làm là tạo ra các viên đạn. Bạn cũng thấy nó cũng hoạt động như những con “flying turtle”. Trong lớp Bullet bên dưới, ta cần có phương thức collide() để kiểm tra va chạm với các monster và tiêu diệt chúng.

function Bullet(map,left,top){
// call the super-constructor
Character.call(this,map,{
	height:10,
	width:10,
	left: left,
	top: top,
	speed: 2,
	isAutoMoving: true,
	canJump: true,
	dieOnCollide: true
});
this.speedY = -this.speed;
}

Bullet.prototype = new Character();
Bullet.prototype.update = function(){
	Character.prototype.update.call(this);
	this.collide(this.map.monsters);
	if(this.isOutsideView()){
		this.die();
	}
}
Bullet.prototype.draw = function(context){
	// 	just simply draw a black circle
	// ...
}
Bullet.prototype.collide = function(monsters){

	for(var i=0;i<monsters.length;i++)
	{
		var m = monsters[i];

		if(!(this.left > m.right ||
			this.right < m.left ||
			this.top > m.bottom ||
			this.bottom < m.top))
			{
				this.die();
				m.die();
			return;
			}

	}
}

Trong Character, ta định nghĩa thêm thuộc tính canShoot và gán giá trị cho nó là true trong lớp Player. Để người chơi không “đại khai sát giới”, tốt nhất ta nên giới hạn số lượng và thời gian bắn đạn:

var BULLETS_LIMIT = 3;
var SHOOT_DELAY = 500; // 2 shoots each second

// ...

Player.prototype.shoot = function(){
	if(this.canShoot && this.bullets.length<BULLETS_LIMIT)
	{
		var time = (new Date()).getTime();
		var d =  time - this.lastShoot;
		if(d>SHOOT_DELAY)
		{
			this.lastShoot = time;

			var left = this.isFaceRight ? this.right : this.left-10;
			var b = new Bullet(this.map, left, this.top + this.height / 2);
			b.speedX = this.isFaceRight ? b.speed : -b.speed;
			this.bullets.push(b);
		}
	}
}

Score và Level

Phiên bản hiện tại chỉ cho phép thay đổi cấu trúc của bản đồ, chưa cho phép thay đổi các đặc điểm khác như kích thước, loại, vị trí của monster, lâu đài,… Mỗi level là một phần tử của mảng MAPS, khi đến level nào, ta chỉ cần lấy phần tử tại vị trí đó trong đối tượng MAPS.

Khi chuyển qua level mới, ta sẽ lưu số điểm người chơi đạt được từ level trước để phục hồi lại mỗi khi người chơi mất mạng:

// Map:
// return false if player has reached the last level; otherwise, return true
this.nextLevel = function(){
	startingScores = this.scores; // store the scores from previous level
	this.level++;
	if(this.level==MAPS.length)
		this.level = 0;

	return this.level!=0;

};

Thêm lâu đài

Trong game này ta sẽ để người chơi điều khiển mario đi vào cửa lâu đài để chuyển qua level tiếp theo. Phiên bản này cố định vị trí của lâu đài và điểm kết thúc (endPoint) sẽ tự động tính dựa vào vị trí của lâu đài:

// Map:
var castlePos = {
	x: this.width - 300,
	y: this.height - _images.castle.height - CELL_SIZE
};
this.endPoint = {
	x: castlePos.x + _images.castle.width/2,
	y: castlePos.y + _images.castle.height
};

Và bổ sung trong phương thức Player.update():

Player.prototype.update = function(){
	var p = this.map.endPoint;
	if(this.left<=p.x && this.top<=p.y && this.right>=p.x && this.bottom>=p.y){
		// win
		return 2;
	}
	// ...
}

YinYangIt’s Blog

Gửi phản hồi

Mời bạn điền thông tin vào ô dưới đây hoặc kích vào một biểu tượng để đăng nhập:

WordPress.com Logo

Bạn đang bình luận bằng tài khoản WordPress.com Log Out / Thay đổi )

Twitter picture

Bạn đang bình luận bằng tài khoản Twitter Log Out / Thay đổi )

Facebook photo

Bạn đang bình luận bằng tài khoản Facebook Log Out / Thay đổi )

Google+ photo

Bạn đang bình luận bằng tài khoản Google+ Log Out / Thay đổi )

Connecting to %s