html畫布製作貪吃蛇小遊戲

我有顆小粒的痣發表於2017-10-29

最終效果:


做貪吃蛇遊戲需要Html5,部分Css美化,重要的是JavaScript的應用,因為我們主要是運用Html5的Canvas標籤來打造遊戲的,所以還是在JavaScript上的筆墨較多

首先搭建好框架:


<!DOCTYPE html>
<html>
<head>
	<title>snake</title>

	<style type="text/css">
	#canvas_frame{
		height:500px;
		width:500px;
		margin:auto;
		background:#444;
		border-radius:8px;
		box-shadow:0px 0px 15px #000;
	}
	canvas{
		background:#21F;
		margin:25px 25px;
	}
	#start_button{
		height:40px;
		width:150px;
		border:0px;
		background:#000;
		color:white;
		line-height:40px;
		font-size:15px;
	}
	</style>
</head>
<body>
	<div id="canvas_frame">
		<canvas id="mycanvas" width="450" height="450"></canvas>
	</div>
	<section style="height:40px;width:100%;text-align:center;">
		<button id="start_button" onclick="moveSnake();">start</button>
	</section>

	<script type="text/javascript">
/*javascript程式碼區*/
	</script>
</body>
</html>

然後在javascript程式碼區建立畫布Dom物件:

	var canvas = document.getElementById('mycanvas');
	var ctx = canvas.getContext('2d');

建立蛇物件:

	var snake = {
		head : { x : 15 * 3, y : 0 },
		body : [{ x : 15 * 2, y : 0 },{ x : 15 * 1, y : 0 },{ x : 0, y : 0 }],
		speed : 100,
		direction: "right",
	};

注意點:蛇物件中的body是一個陣列,陣列內的元素是代表蛇每個節在畫布上的X座標和Y座標,snake.body[0]為最靠近head的節,依此類推。。


描繪蛇:

	function drawSnake(){
		ctx.clearRect( 0, 0, 450, 450 );
		var headImg = new Image();
		var headSrc = "./snake_head_min_" + snake.direction +".png";/*存貯蛇圖片的位置,用於動態改變head的圖片位置*/
		headImg.src= headSrc;
		ctx.drawImage(headImg, snake.head.x, snake.head.y );
		for( var i = 0; i <= snake.body.length - 1; i++){
			var cell = new Image();
			cell.src = "./snake_body_min.png";
			ctx.drawImage(cell, snake.body[i].x, snake.body[i].y );
		}
	}

注意點:

1.每次描繪蛇之前都需要將原有的圖畫清空------ ctx.clearRect( 0, 0, 450, 450);  clearRect()用法詳見:HTML5 canvas clearRect() 方法

2.這裡我採用的不是填充矩形的方法來實現畫蛇,而是採用描繪影象的方法: 用法詳見:HTML 5 <canvas> 標籤

1)建立影象:var Img = new Image();

2)影象位置:Img.src="./img.png";

3)開始描繪:ctx.drawImage( Img, 0, 0);後兩個參數列示的是描繪圖片的x座標的y座標;

在這裡我採用了自己製作的圖片,注意要調整圖片的大小(為展示,此處顯示大圖):

             

body head

因為蛇頭有方向:所以對於head我們有四張圖片:



建立食物物件:

	var food = {
		x : 300,
		y : 150,
	};


x , y 代表食物在畫布的x座標和y位置。


描繪食物:

	function drawFood(){
		if(IsEat()){
			food.x = parseInt(Math.random() * 28) * 15;
			food.y = parseInt(Math.random() * 28) * 15;
		}
		var foodImg = new Image();
		foodImg.src="./apple_min.png";
		ctx.drawImage(foodImg, food.x, food.y );
	}

注意點:

1.關於獲取隨機數:Math.random();該函式返回一個區間為(0, 1 )的隨機數;

2.此處我們將隨機數乘以28以放大隨機數的區間為(0, 28 ),總共的格子數為30 * 30 個 即450 / 15 = 30;

3.我們將其隨機數的區間轉化為整數:即1,2,3,4,。。。,27,

4.當food 被蛇吃的時候(snake.head的座標與食物的座標相同的時候),我們要隨機在生成新的食物座標,此處不考慮食物生成的座標與蛇的身體有相同(食物生成在了蛇的body上)


判斷食物是否被吃:

	function IsEat(){
		if( snake.head.x == food.x && snake.head.y == food.y){
			snake.body[snake.body.length] = { x : 0, y : 0};
			return 1;
		}
		return 0;
	}

注意點:

1.此處用了if判斷語句,且判斷的表示式需滿足 蛇的頭的座標與食物的座標相同(x座標和y座標);

2.為了在描繪食物的方法(drawFood();)裡更好的判斷,我們設定返回值 1 代表被吃,0代表未被吃;

3.食物被吃之後應該生成一個新的節(此處預設生成的節懂得x座標和y座標都為0(這個不要緊)在蛇移動的時候會自動改變該值)

snake.body[snake.body.length] = { x : 0, y : 0};


開始重頭大戲----移動蛇:

首先移動蛇我們需要的是:不斷地描繪影象,並且不斷的更新蛇的頭的身體各節的座標:

程式碼上:

	function moveSnake(){
		for( var i = snake.body.length - 1; i > 0 ; i-- ){
				snake.body[i].x = snake.body[i - 1].x;
				snake.body[i].y = snake.body[i - 1].y;
			}
			snake.body[0].x = snake.head.x;
			snake.body[0].y = snake.head.y;
		switch( snake.direction ){
			case "right":{
				snake.head.x = snake.head.x + 15;
				break;
			} 
			case "left":{
				snake.head.x = snake.head.x - 15;
				break;
			}
			case "up":{
				snake.head.y = snake.head.y - 15;
				break;
			}
			case "down":{
				snake.head.y = snake.head.y + 15;
				break;
			}
		}
		drawSnake();
		drawFood();
		IsGameover();
		setTimeout( 'moveSnake()', snake.speed );
	}

注意點:

1.為了達到更新蛇的頭和蛇的身體各節座標的目標,我們的想法應該是:較後的節繼承較前的節的座標;所以就有for迴圈:

			for( var i = snake.body.length - 1; i > 0 ; i-- ){
				snake.body[i].x = snake.body[i - 1].x;
				snake.body[i].y = snake.body[i - 1].y;
			}
			snake.body[0].x = snake.head.x;
			snake.body[0].y = snake.head.y;
最靠近頭的節將繼承頭的座標。

2.針對頭的座標更新我們根據蛇的方向(snake.direction)來進行判斷:

switch( snake.direction ){}

3.在移動蛇的時候,我們需要不斷的畫蛇,不斷的畫食物,不斷的判斷是否結束遊戲,並且我們需要不斷的呼叫自己這個函式;

更新時間我們用setTimeout();用法詳見:HTML DOM setTimeout() 方法


判斷GameOver:

判斷遊戲結束有兩個標準:

1.碰壁,遊戲結束

2.吃著自己,遊戲結束


程式碼上:

	function IsGameover(){
		if(snake.head.x == game.width && snake.direction == 'right'){
			alert("GameOver!");
		}
		if(snake.head.x == -15 && snake.direction == 'left'){
			alert("GameOver!");
		}
		if(snake.head.y == game.height  && snake.direction == 'down'){
			alert("GameOver!");
		}
		if(snake.head.y == -15 && snake.direction == 'up'){
			alert("GameOver!");
		}
		for( var i = 0; i < snake.body.length; i++ ){
			if( snake.head.x == snake.body[i].x && snake.head.y == snake.body[i].y){
				alert("GameOver!");
			}
		}
	}

注意點:

1.四個if判斷語句用於判斷碰壁:!!滿足條件為 座標 和 蛇的移動方向(例如 蛇的head的x座標為0,方向向上  =》 碰壁)

2.for迴圈語句:對每個body的節判斷其座標是否與head的座標重合(重合即代表 蛇頭吃到自己的身體了)


監聽鍵盤輸入:

	document.onkeydown = function(e){
		var code = e.keyCode;
		switch(code){
			case 38:{
				if( snake.direction != 'down'){
					snake.direction='up';
				}
				break;
			}
			case 39:{
				if( snake.direction != 'left'){
					snake.direction='right';
				}
				break;
		}
			case 40:{
				if( snake.direction != 'up'){
					snake.direction='down';
				}
				break;
			}
			case 37:{
				if( snake.direction != 'right'){
					snake.direction='left';
				}
				break;
			}
			default:break;
		}
	}

注意點:

1.html Dom document物件的onkeydown事件後的函式需要引入引數(e)

2.對該事件(event)的鍵盤值(keyCode)進行判斷然後改變蛇的方向--snake.direction;


該遊戲的製作就完成了,當然我們也可以把它可以做的更好。


最後上完整程式碼:

<!DOCTYPE html>
<html>
<head>
	<title>snake</title>

	<style type="text/css">
	#canvas_frame{
		height:500px;
		width:500px;
		margin:auto;
		background:#444;
		border-radius:8px;
		box-shadow:0px 0px 15px #000;
	}
	canvas{
		background:#21F;
		margin:25px 25px;
	}
	#start_button{
		height:40px;
		width:150px;
		border:0px;
		background:#000;
		color:white;
		line-height:40px;
		font-size:15px;
	}
	</style>
</head>
<body>
	<div id="canvas_frame">
		<canvas id="mycanvas" width="450" height="450"></canvas>
	</div>
	<section style="height:40px;width:100%;text-align:center;">
		<button id="start_button" onclick = "moveSnake();">start</button>
	</section>

	<script type="text/javascript">
	var canvas = document.getElementById('mycanvas');
	var ctx = canvas.getContext('2d');
	/*
	//畫格子
	 ctx.strokeStyle = 'white';

	for( var i = 1; i <= 30; i++ ){
		ctx.beginPath();
		ctx.moveTo( 15 * i, 0 );
		ctx.lineTo( 15 * i, 450 );
		ctx.closePath();
		ctx.stroke();
	}
	for( var i = 1; i <= 30; i++ ){
		ctx.beginPath();
		ctx.moveTo( 0, 15 * i );
		ctx.lineTo( 450, 15 * i );
		ctx.closePath();
		ctx.stroke();
	}
*/
	//建立遊戲物件
	var game = {
		width : 450,
		height : 450,
	};
	//建立一條蛇
	var snake = {
		head : { x : 15 * 3, y : 0 },
		body : [{ x : 15 * 2, y : 0 },{ x : 15 * 1, y : 0 },{ x : 0, y : 0 }],
		length : 4,
		speed : 100,
		direction: "right",
	};
	//建立食物
	var food = {
		x : 300,
		y : 150,
		color : "green",
	};

	//描繪蛇
	function drawSnake(){
		ctx.clearRect( 0, 0, 450, 450 );
		var headImg = new Image();
		var headSrc = "./snake_head_min_" + snake.direction +".png";
		headImg.src= headSrc;
		ctx.drawImage(headImg, snake.head.x, snake.head.y );
		for( var i = 0; i <= snake.body.length - 1; i++){
			var cell = new Image();
			cell.src = "./snake_body_min.png";
			ctx.drawImage(cell, snake.body[i].x, snake.body[i].y );
		}
	}
	//描繪食物
	function drawFood(){
		if(IsEat()){
			food.x = parseInt(Math.random() * 28) * 15;
			food.y = parseInt(Math.random() * 28) * 15;
		}
		var foodImg = new Image();
		foodImg.src="./apple_min.png";
		ctx.drawImage(foodImg, food.x, food.y );
	}
	//移動蛇
	function moveSnake(){
		for( var i = snake.body.length - 1; i > 0 ; i-- ){
				snake.body[i].x = snake.body[i - 1].x;
				snake.body[i].y = snake.body[i - 1].y;
			}
			snake.body[0].x = snake.head.x;
			snake.body[0].y = snake.head.y;
		switch( snake.direction ){
			case "right":{
				snake.head.x = snake.head.x + 15;
				break;
			} 
			case "left":{
				snake.head.x = snake.head.x - 15;
				break;
			}
			case "up":{
				snake.head.y = snake.head.y - 15;
				break;
			}
			case "down":{
				snake.head.y = snake.head.y + 15;
				break;
			}
		}
		drawSnake();
		drawFood();
		IsGameover();
		setTimeout( 'moveSnake()', snake.speed );
	}
	//判斷遊戲結束
	function IsGameover(){
		if(snake.head.x == game.width && snake.direction == 'right'){
			alert("GameOver!");
		}
		if(snake.head.x == -15 && snake.direction == 'left'){
			alert("GameOver!");
		}
		if(snake.head.y == game.height  && snake.direction == 'down'){
			alert("GameOver!");
		}
		if(snake.head.y == -15 && snake.direction == 'up'){
			alert("GameOver!");
		}
		for( var i = 0; i < snake.body.length; i++ ){
			if( snake.head.x == snake.body[i].x && snake.head.y == snake.body[i].y){
				alert("GameOver!");
			}
		}
	}
	//監聽鍵盤輸入
	document.onkeydown = function(e){
		var code = e.keyCode;
		switch(code){
			case 38:{
				if( snake.direction != 'down'){
					snake.direction='up';
				}
				break;
			}
			case 39:{
				if( snake.direction != 'left'){
					snake.direction='right';
				}
				break;
		}
			case 40:{
				if( snake.direction != 'up'){
					snake.direction='down';
				}
				break;
			}
			case 37:{
				if( snake.direction != 'right'){
					snake.direction='left';
				}
				break;
			}
			default:break;
		}
	}
//判斷是否吃了食物
	function IsEat(){
		if( snake.head.x == food.x && snake.head.y == food.y){
			snake.body[snake.body.length] = { x : 0, y : 0};
			return 1;
		}
		return 0;
	}
	</script>
</body>
</html>


圖片資源:

     

本文中的超連結指向W3school



相關文章