第20回【テトリス制作.完成】


まずは動画を見てくださいね!
ラインをチェックして消す処理を入れたら完成です。

ラインをチェックして消す関数を作る

チェック関数を作る前に(後でも構いませんが)テトロミノを固定した後でcheckLineという関数を呼び出すこととします。
		...
		fixTetro();
		checkLine();
		...


//ラインが揃ったかチェックして消す
function checkLine()
{
	let linec=0;
	for(let y=0; y<FIELD_ROW ; y++ )
	{
		let flag=true;
		
		for(let x=0; x<FIELD_COL ; x++ )
		{
			if( !field[y][x] )
			{
				flag=false;
				break;
			}
		}
		
		if(flag)
		{
			linec++;
			
			for(let ny = y; ny>0 ;ny-- )
			{
				for(let nx=0;nx<FIELD_COL ; nx++)
				{
					field[ny][nx] = field[ny-1][nx];
				}
			}
		}
	}
}
チェックする部分(上半分)と消す部分(下半分)どちらも単純なので、よく理解しましょう。
これでゲーム部分はほぼ完成です!


ゲームオーバーの処理を付けて完成

フィールドに固定して新しいテトロを出した時、そこに出現できない状態の時はゲームオーバーとします。
ゲームオーバーのフラグは改めて作っておきます。
//ゲームオーバーフラグ
let over=false;
		...
		...
		tetro_x = START_X;
		tetro_y = START_Y;
		
		if( !checkMove(0,0) )
		{
			over=true;
		}

キーボードの処理、落ちる処理は先頭でoverフラグを見てゲームオーバーの時はすぐにreturnしてしまいましょう。
	if(over)return;


ゲームオーバーの表示を入れたら完成!

drawAll()関数の最後にとりあえず入れておきます。
	...
	if(over)
	{
		let s="GAME OVER";
		con.font = "40px 'MS ゴシック'";
		let w = con.measureText(s).width;
		let x = SCREEN_W/2 - w/2;
		let y = SCREEN_H/2 - 20;
		con.lineWidth = 4;
		con.strokeText(s,x,y);
		con.fillStyle="white";
		con.fillText(s,x,y);
	}
	...
これで完成です!
どうでしょうか、難しかったですかね・・・
ここまで全部ちゃんと理解できるようになれば、もうプログラマ初心者卒業でもいいかもしれません。
後は自分で考えて作れるかどうかですが・・・。

それでは、次はシューティングゲーム作成で!


最終形の全ソースです!

<!DOCTYPE html>
<html lang="ja">
<head><meta charset="utf-8">;</head>
<body>
<canvas id="can"></canvas>
<script>

//落ちるスピード
const GAME_SPEED = 300;

//フィールドサイズ
const FIELD_COL = 10;
const FIELD_ROW = 20;

//ブロック一つのサイズ(ピクセル)
const BLOCK_SIZE = 30;

//スクリーンサイズ
const SCREEN_W = BLOCK_SIZE * FIELD_COL;
const SCREEN_H = BLOCK_SIZE * FIELD_ROW;

//テトロミノのサイズ
const TETRO_SIZE =4;

const TETRO_COLORS =[
	"#000",			//0空
	"#6CF",			//1水色
	"#F92",			//2オレンジ
	"#66F",			//3青
	"#C5C",			//4紫
	"#FD2",			//5黄色
	"#F44",			//6赤
	"#5B5"			//7緑
];

const TETRO_TYPES = [
	[],	// 0.空っぽ
	
	[					// 1.I
		[ 0, 0, 0, 0 ],
		[ 1, 1, 1, 1 ],
		[ 0, 0, 0, 0 ],
		[ 0, 0, 0, 0 ]
	],
	[					// 2.L
		[ 0, 1, 0, 0 ],
		[ 0, 1, 0, 0 ],
		[ 0, 1, 1, 0 ],
		[ 0, 0, 0, 0 ]
	],
	[					// 3.J
		[ 0, 0, 1, 0 ],
		[ 0, 0, 1, 0 ],
		[ 0, 1, 1, 0 ],
		[ 0, 0, 0, 0 ]
	],
	[					// 4.T
		[ 0, 1, 0, 0 ],
		[ 0, 1, 1, 0 ],
		[ 0, 1, 0, 0 ],
		[ 0, 0, 0, 0 ]
	],
	[					// 5.O
		[ 0, 0, 0, 0 ],
		[ 0, 1, 1, 0 ],
		[ 0, 1, 1, 0 ],
		[ 0, 0, 0, 0 ]
	],
	[					// 6.Z
		[ 0, 0, 0, 0 ],
		[ 1, 1, 0, 0 ],
		[ 0, 1, 1, 0 ],
		[ 0, 0, 0, 0 ]
	],
	[					// 7.S
		[ 0, 0, 0, 0 ],
		[ 0, 1, 1, 0 ],
		[ 1, 1, 0, 0 ],
		[ 0, 0, 0, 0 ]
	]
];

//初期位置
const START_X = FIELD_COL/2 - TETRO_SIZE/2;
const START_Y = 0;

//テトロミノ本体
let tetro;
//テトロミノの形
let tetro_t;

//テトロミノの座標
let tetro_x = START_X;
let tetro_y = START_Y;

//フィールド本体
let field = [];

//ゲームオーバーフラグ
let over = false;

let can = document.getElementById("can");
let con = can.getContext("2d");

can.width  = SCREEN_W;
can.height = SCREEN_H;
can.style.border = "4px solid #555";

tetro_t = Math.floor( Math.random()*(TETRO_TYPES.length-1) )+1;
tetro = TETRO_TYPES[ tetro_t ];

init();
drawAll();

setInterval( dropTetro, GAME_SPEED );

//初期化
function init()
{
	//フィールドのクリア
	for(let y=0; y<FIELD_ROW ; y++ )
	{
		field[y] = [];
		for(let x=0; x<FIELD_COL ; x++ )
		{
			field[y][x] =0;
		}
	}
	/*
	field[5][8]  = 1;
	field[19][9] = 1;
	field[19][0] = 1;
	*/
}

//ブロック一つを描画する
function drawBlock(x,y,c)
{
	let px = x * BLOCK_SIZE;
	let py = y * BLOCK_SIZE;
	con.fillStyle=TETRO_COLORS[c];
	con.fillRect(px,py,BLOCK_SIZE,BLOCK_SIZE);
	con.strokeStyle="black";
	con.strokeRect(px,py,BLOCK_SIZE,BLOCK_SIZE);
}

//全部描画する
function drawAll()
{
	con.clearRect(0,0,SCREEN_W,SCREEN_H);
	
	for(let y=0; y<FIELD_ROW ; y++ )
	{
		for(let x=0; x<FIELD_COL ; x++ )
		{
			if( field[y][x] )
			{
				drawBlock(x,y,field[y][x]);
			}
		}
	}
	for(let y=0; y<TETRO_SIZE ; y++ )
	{
		for(let x=0; x<TETRO_SIZE ; x++ )
		{
			if( tetro[y][x] )
			{
				drawBlock(tetro_x+x, tetro_y+y,tetro_t);
			}
		}
	}
	
	if(over)
	{
		let s="GAME OVER";
		con.font = "40px 'MS ゴシック'";
		let w = con.measureText(s).width;
		let x = SCREEN_W/2 - w/2;
		let y = SCREEN_H/2 - 20;
		con.lineWidth = 4;
		con.strokeText(s,x,y);
		con.fillStyle="white";
		con.fillText(s,x,y);
	}
}

// ブロックの衝突判定
function checkMove( mx,my ,ntetro)
{
	if( ntetro == undefined ) ntetro = tetro;
	
	for(let y=0; y<TETRO_SIZE ; y++ )
	{
		for(let x=0; x<TETRO_SIZE ; x++ )
		{
			if(  ntetro[y][x]  )
			{
				let nx = tetro_x + mx + x;
				let ny = tetro_y + my + y;
				
				if( ny < 0 ||
					nx < 0 ||
					ny >= FIELD_ROW ||
					nx >= FIELD_COL ||
					field[ny][nx] )
				{
					return false;
				}
			}
		}
	}
	
	return true;
}

// テトロの回転
function rotate()
{
	let ntetro = [];
	
	for(let y=0; y<TETRO_SIZE ; y++ )
	{
		ntetro[y]=[];
		for(let x=0; x<TETRO_SIZE ; x++ )
		{
			ntetro[y][x] = tetro[TETRO_SIZE-x-1][y];
		}
	}
	
	return ntetro;
}

//テトロを固定する
function fixTetro()
{
	for(let y=0; y<TETRO_SIZE ; y++ )
	{
		for(let x=0; x<TETRO_SIZE ; x++ )
		{
			if( tetro[y][x] )
			{
				field[tetro_y + y][tetro_x + x] = tetro_t;
			}
		}
	}
}

//ラインが揃ったかチェックして消す
function checkLine()
{
	let linec=0;
	for(let y=0; y<FIELD_ROW ; y++ )
	{
		let flag=true;
		
		for(let x=0; x<FIELD_COL ; x++ )
		{
			if( !field[y][x] )
			{
				flag=false;
				break;
			}
		}
		
		if(flag)
		{
			linec++;
			
			for(let ny = y; ny>0 ;ny-- )
			{
				for(let nx=0;nx<FIELD_COL ; nx++)
				{
					field[ny][nx] = field[ny-1][nx];
				}
			}
		}
	}
}

// ブロックの落ちる処理
function dropTetro()
{
	if(over)return;
	
	if( checkMove(  0, 1 ) )tetro_y++;
	else
	{
		fixTetro();
		checkLine();
		tetro_t = Math.floor( Math.random()*(TETRO_TYPES.length-1) )+1;
		tetro = TETRO_TYPES[ tetro_t ];
		tetro_x = START_X;
		tetro_y = START_Y;
		
		if( !checkMove(0,0) )
		{
			over=true;
		}
	}
	drawAll();
}

//キーボードが押された時の処理
document.onkeydown = function(e)
{
	if(over)return;
	
	switch( e.keyCode )
	{
		case 37:// 左
			if( checkMove( -1, 0 ) )tetro_x--;
			break;
		case 38:// 上
			//if( checkMove(  0,-1 ) )tetro_y--;
			break;
		case 39:// 右
			if( checkMove(  1, 0 ) )tetro_x++;
			break;
		case 40:// 下
			if( checkMove(  0, 1 ) )tetro_y++;
			break;
		case 32:// スペース
			let ntetro = rotate();
			if( checkMove( 0, 0, ntetro) ) tetro = ntetro;
			break;
	}
	
	drawAll();
}

</script>
</body>
</html>



akichon/あきちょん
Youtubeでプログラミング講座やってるゲーム好きの(元?)プログラマ...
>>open profile...
■Twitter始めました
https://twitter.com/ak1chon
■Youtubeチャンネルはこちら
akichon(Youtube)