JavaScript实现俄罗斯方块游戏过程分析及源码分享_javascript技巧

观摩一下《编程之美》:“程序虽然很难写,却很美妙。要想把程序写好,需要写好一定的基础知识,包括编程语言、数据结构与算法。程序写得好,需要缜密的逻辑思维能力和良好的梳理基础,而且熟悉编程环境和编程工具。”

学了几年的计算机,你有没有爱上编程。话说,没有尝试自己写过一个游戏,算不上热爱编程。

俄罗斯方块曾经造成的轰动与造成的经济价值可以说是游戏史上的一件大事,它看似简单但却变化无穷,令人上瘾。相信大多数同学,曾经为它痴迷得茶不思饭不想。

游戏规则

1、一个用于摆放小型正方形的平面虚拟场地,其标准大小:行宽为10,列高为20,以每个小正方形为单位。

2、一组由4个小型正方形组成的规则图形,英文称为Tetromino,中文通称为方块共有7种,分别以S、Z、L、J、I、O、T这7个字母的形状来命名。

I:一次最多消除四层

J(左右):最多消除三层,或消除二层

L:最多消除三层,或消除二层

O:消除一至二层

S(左右):最多二层,容易造成孔洞

Z (左右):最多二层,容易造成孔洞

T:最多二层

方块会从区域上方开始缓慢继续落下。玩家可以以90度为单位旋转方块,以格子为单位左右移动方块,让方块加速落下。方块移到区域最下方或是着地到其他方块上无法移动时,就会固定在该处,而新的方块出现在区域上方开始落下。当区域中某一列横向格子全部由方块填满,则该列会消失并成为玩家的得分。同时删除的列数越多,得分指数上升。

分析与解法

每块方块落下的过程中,我们可以做:

1)旋转到合适的方向

2)水平移动到某一列

3)垂直下落到底部

首先,需要用一个二维数组,area[18][10]表示18*10的游戏区域。其中,数组中值为0表示空,1表示有方块。

方块一共7种,每种有4种方向。定义activeBlock[4],在编译之前这个数组的值预定算好,在程序中直接使用。

难点

 1)边界检查。

 //检查左边界,尝试着朝左边移动一个,看是否合法。
function checkLeftBorder(){
  for(var i=0; i<activeBlock.length; i++){
    if(activeBlock[i].y==0){
      return false;
    }
    if(!isCellValid(activeBlock[i].x, activeBlock[i].y-1)){
      return false;
    }
  }
  return true;
} //同理,需要检测右边界和底边界

 2)旋转, 需要数理逻辑, 一个点相对另外一个点旋转90度的问题。
 3)定时和监听键盘事件机制让游戏自动运行下去。

//开始
function begin(e){
  e.disabled = true;
  status = 1;
  tbl = document.getElementById("area");
  if(!generateBlock()){
    alert("Game over!");
    status = 2;
    return;
  }
  paint();
  timer = setInterval(moveDown,1000);
}
document.onkeydown=keyControl;

程序过程

1)用户点开始->构造一个活动图形, 设置定时器。

//当前活动的方块, 它可以左右下移动, 变型。当它触底后, 将会更新area;
var activeBlock;
//生产方块形状, 有7种基本形状。
function generateBlock(){
  activeBlock = null;
  activeBlock = new Array(4);
  //随机产生0-6数组,代表7种形态。
  var t = (Math.floor(Math.random()*20)+1)%7;
  switch(t){
    case 0:{
      activeBlock[0] = {x:0, y:4};
      activeBlock[1] = {x:1, y:4};
      activeBlock[2] = {x:0, y:5};
      activeBlock[3] = {x:1, y:5}; 

      break;
    }
    //省略部分代码..............................
    case 6:{
      activeBlock[0] = {x:0, y:5};
      activeBlock[1] = {x:1, y:4};
      activeBlock[2] = {x:1, y:5};
      activeBlock[3] = {x:1, y:6};
      break;
    }
  }
  //检查刚生产的四个小方格是否可以放在初始化的位置.
  for(var i=0; i<4; i++){
    if(!isCellValid(activeBlock[i].x, activeBlock[i].y)){
        return false;
      }
    }
  return true;
}

2)每次向下移动后, 都检查是否触底, 如果触底了, 则尝试消行。

//消行
function deleteLine(){
  var lines = 0;
  for(var i=0; i<18; i++){
    var j=0;
    for(; j<10; j++){
      if(area[i][j]==0){
        break;
    }
  }
  if(j==10){
    lines++;
    if(i!=0){
      for(var k=i-1; k>=0; k--){
        area[k+1] = area[k];
      }
    }
    area[0] = generateBlankLine();
    }
  }
  return lines;
}

3)完了之后再构造一个活动图形, 再设置定时器。

效果图

有待优化

1)设置不同形状方块的颜色。

  思路:在创建方块函数内,设定activeBlockColor颜色,七种不同形态方块颜色各异(除了修改generateBlock方法之外,还需要修改paintarea方法。因为一开始考虑不周全,消除一行后,重绘方块的同时将颜色统一,因此可以考虑移除表格n行,然后在顶部增添n行,以保证没消除方块的完整性)。

2)当当前方块下落时,可以提前查看下一个方块。

  思路:将generateBlock方法拆分成两部分,一部分用于随机尝试下一个方块,一部分用于缓存当前所要描绘的方块。当当前方块碰到底部被固定后,下一方块开始描绘,同时又再次随机产生新方块。如此反复。

完整HTML源码:

<!DOCTYPE>
<html>
<head>
<title>Tetris</title>
<meta charset="UTF-8">
<style>
*{
	font-family: "微软雅黑";
}
.tetrisContainer{
	width: 230px;
	height: 400px;
	position: relative;
	left: 50%;
	margin-left: -115px;
	top: 40%;
	margin-top: -200px;
}
#area tr td{
	width: 20px;
	height: 20px;
	border:1px solid #ccc;
}
</style>
</head>
	<body>
		<div class = "tetrisContainer">
			<input type="button" value="开始游戏" onclick="begin(this);"/> 得分: <span id="score"> 0</span>
			<table id="area" cellspacing="0" cellpadding="0" border="1" style="border-collapse:collapse"><tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr></table>
		</div>
	</body>
	<script type="text/javascript" src="script/tetris.js"></script>
</html>

完整tetris.js源码:

/**
* JS俄罗斯方块游戏 v 1.0
*/
//表示页面中的table, 这个table就是将要显示游戏的主面板
var tbl;
//游戏状态 0: 未开始;1 运行; 2 中止;
var status = 0;
//定时器, 定时器内将做moveDown操作
var timer;
//分数
var score = 0; 

//area是一个18*10的数组,也和页面的table对应。初始时都为0, 如果被占据则为1 

var area = new Array(18);
for(var i=0;i<18;i++){
	area[i] = new Array(10);
}
for(var i=0;i<18;i++){
	for(var j=0; j<10; j++){
		area[i][j] = 0;
	}
} 

//当前活动的方块, 它可以左右下移动, 变型。当它触底后, 将会更新area;
var activeBlock;
//生产方块形状, 有7种基本形状。
function generateBlock(){
	activeBlock = null;
	activeBlock = new Array(4);
	//随机产生0-6数组,代表7种形态。
	var t = (Math.floor(Math.random()*20)+1)%7;
	switch(t){
		case 0:{
			activeBlock[0] = {x:0, y:4};
			activeBlock[1] = {x:1, y:4};
			activeBlock[2] = {x:0, y:5};
			activeBlock[3] = {x:1, y:5}; 

			break;
		}
		case 1:{
			activeBlock[0] = {x:0, y:3};
			activeBlock[1] = {x:0, y:4};
			activeBlock[2] = {x:0, y:5};
			activeBlock[3] = {x:0, y:6};
			break;
		}
		case 2:{
			activeBlock[0] = {x:0, y:5};
			activeBlock[1] = {x:1, y:4};
			activeBlock[2] = {x:1, y:5};
			activeBlock[3] = {x:2, y:4};
			break;
		}
		case 3:{
			activeBlock[0] = {x:0, y:4};
			activeBlock[1] = {x:1, y:4};
			activeBlock[2] = {x:1, y:5};
			activeBlock[3] = {x:2, y:5};
			break;
		}
		case 4:{
			activeBlock[0] = {x:0, y:4};
			activeBlock[1] = {x:1, y:4};
			activeBlock[2] = {x:1, y:5};
			activeBlock[3] = {x:1, y:6};
			break;
		}
		case 5:{
			activeBlock[0] = {x:0, y:4};
			activeBlock[1] = {x:1, y:4};
			activeBlock[2] = {x:2, y:4};
			activeBlock[3] = {x:2, y:5};
			break;
		}
		case 6:{
			activeBlock[0] = {x:0, y:5};
			activeBlock[1] = {x:1, y:4};
			activeBlock[2] = {x:1, y:5};
			activeBlock[3] = {x:1, y:6};
			break;
		}
	}
	//检查刚生产的四个小方格是否可以放在初始化的位置.
	for(var i=0; i<4; i++){
		if(!isCellValid(activeBlock[i].x, activeBlock[i].y)){
				return false;
			}
		}
	return true;
}
//向下移动
function moveDown(){
	//检查底边界.
	if(checkBottomBorder()){
		//没有触底, 则擦除当前图形,
		erase();
		//更新当前图形坐标
		for(var i=0; i<4; i++){
			activeBlock[i].x = activeBlock[i].x + 1;
		}
		//重画当前图形
		paint();
	}
	//触底,
	else{
		//停止当前的定时器, 也就是停止自动向下移动.
		clearInterval(timer);
		//更新area数组.
		updatearea();
		//消行
		var lines = deleteLine();
		//如果有消行, 则
		if(lines!=0){
			//更新分数
			score = score + lines*10;
			updateScore();
			//擦除整个面板
			erasearea();
			//重绘面板
			paintarea();
		}
		//产生一个新图形并判断是否可以放在最初的位置.
		if(!generateBlock()){
			alert("Game over!");
			status = 2;
			return;
		}
		paint();
		//定时器, 每隔一秒执行一次moveDown
		timer = setInterval(moveDown,1000)
	}
}
//左移动
function moveLeft(){
	if(checkLeftBorder()){
		erase();
		for(var i=0; i<4; i++){
			activeBlock[i].y = activeBlock[i].y - 1;
		}
		paint();
		}
	}
	//右移动
function moveRight(){
	if(checkRightBorder()){
		erase();
		for(var i=0; i<4; i++){
			activeBlock[i].y = activeBlock[i].y + 1;
		}
		paint();
	}
}
//旋转, 因为旋转之后可能会有方格覆盖已有的方格.
//先用一个tmpBlock,把activeBlock的内容都拷贝到tmpBlock,
//对tmpBlock尝试旋转, 如果旋转后检测发现没有方格产生冲突,则
//把旋转后的tmpBlock的值给activeBlock.
function rotate(){
	var tmpBlock = new Array(4);
	for(var i=0; i<4; i++){
		tmpBlock[i] = {x:0, y:0};
	}
	for(var i=0; i<4; i++){
		tmpBlock[i].x = activeBlock[i].x;
		tmpBlock[i].y = activeBlock[i].y;
	}
	//先算四个点的中心点,则这四个点围绕中心旋转90度。
	var cx = Math.round((tmpBlock[0].x + tmpBlock[1].x + tmpBlock[2].x + tmpBlock[3].x)/4);
	var cy = Math.round((tmpBlock[0].y + tmpBlock[1].y + tmpBlock[2].y + tmpBlock[3].y)/4);
	//旋转的主要算法. 可以这样分解来理解。
	//先假设围绕源点旋转。然后再加上中心点的坐标。 

	for(var i=0; i<4; i++){
		tmpBlock[i].x = cx+cy-activeBlock[i].y;
		tmpBlock[i].y = cy-cx+activeBlock[i].x;
	}
	//检查旋转后方格是否合法.
	for(var i=0; i<4; i++){
	if(!isCellValid(tmpBlock[i].x,tmpBlock[i].y)){
		return;
	}
}
//如果合法, 擦除
erase();
//对activeBlock重新赋值.
for(var i=0; i<4; i++){
	activeBlock[i].x = tmpBlock[i].x;
	activeBlock[i].y = tmpBlock[i].y;
}
//重画.
paint();
}
//检查左边界,尝试着朝左边移动一个,看是否合法。
function checkLeftBorder(){
	for(var i=0; i<activeBlock.length; i++){
		if(activeBlock[i].y==0){
			return false;
		}
		if(!isCellValid(activeBlock[i].x, activeBlock[i].y-1)){
			return false;
		}
	}
	return true;
}
//检查右边界,尝试着朝右边移动一个,看是否合法。
function checkRightBorder(){
	for(var i=0; i<activeBlock.length; i++){
		if(activeBlock[i].y==9){
			return false;
		}
		if(!isCellValid(activeBlock[i].x, activeBlock[i].y+1)){
			return false;
		}
	}
	return true;
}
//检查底边界,尝试着朝下边移动一个,看是否合法。
function checkBottomBorder(){
	for(var i=0; i<activeBlock.length; i++){
		if(activeBlock[i].x==17){
			return false;
		}
		if(!isCellValid(activeBlock[i].x+1, activeBlock[i].y)){
			return false;
		}
	}
	return true;
}
//检查坐标为(x,y)的是否在area种已经存在, 存在说明这个方格不合法。
function isCellValid(x, y){
	if(x>17||x<0||y>9||y<0){
		return false;
	}
	if(area[x][y]==1){
		return false;
	}
	return true;
}
//擦除
function erase(){
	for(var i=0; i<4; i++){
		tbl.rows[activeBlock[i].x].cells[activeBlock[i].y].style.backgroundColor="white";
	}
}
//绘活动图形
function paint(){
	for(var i=0; i<4; i++){
		tbl.rows[activeBlock[i].x].cells[activeBlock[i].y].style.backgroundColor="#CC3333";
	}
}
//更新area数组
function updatearea(){
	for(var i=0; i<4; i++){
		area[activeBlock[i].x][activeBlock[i].y]=1;
	}
}
//消行
function deleteLine(){
	var lines = 0;
	for(var i=0; i<18; i++){
		var j=0;
		for(; j<10; j++){
			if(area[i][j]==0){
				break;
		}
	}
	if(j==10){
		lines++;
		if(i!=0){
			for(var k=i-1; k>=0; k--){
				area[k+1] = area[k];
			}
		}
		area[0] = generateBlankLine();
		}
	}
	return lines;
}
//擦除整个面板
function erasearea(){
	for(var i=0; i<18; i++){
		for(var j=0; j<10; j++){
			tbl.rows[i].cells[j].style.backgroundColor = "white";
		}
	}
}
//重绘整个面板
function paintarea(){
	for(var i=0;i<18;i++){
		for(var j=0; j<10; j++){
			if(area[i][j]==1){
				tbl.rows[i].cells[j].style.backgroundColor = "#CC3333";
			}
		}
	}
}
//产生一个空白行.
function generateBlankLine(){
	var line = new Array(10);
	for(var i=0; i<10; i++){
		line[i] = 0;
	}
	return line;
}
//更新分数
function updateScore(){
	document.getElementById("score").innerText=" " + score;
}
//键盘控制
function keyControl(){
	if(status!=1){
		return;
	}
	var code = event.keyCode;
	switch(code){
		case 37:{
			moveLeft();
			break;
		}
		case 38:{
			rotate();
			break;
		}
		case 39:{
			moveRight();
			break;
		}
		case 40:{
			moveDown();
			break;
		}
	}
}
//开始
function begin(e){
	e.disabled = true;
	status = 1;
	tbl = document.getElementById("area");
	if(!generateBlock()){
		alert("Game over!");
		status = 2;
		return;
	}
	paint();
	timer = setInterval(moveDown,1000);
}
document.onkeydown=keyControl; 

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索javascript
俄罗斯方块游戏
javascript俄罗斯方块、俄罗斯方块java源代码、俄罗斯方块源代码、c 俄罗斯方块源码、安卓俄罗斯方块源码,以便于您获取更多的相关知识。

时间: 2024-12-03 00:12:23

JavaScript实现俄罗斯方块游戏过程分析及源码分享_javascript技巧的相关文章

JavaScript头像上传插件源码分享_javascript技巧

本文实例为大家分享了JavaScript头像上传插件源码,供大家参考,具体内容如下 效果图: 源码: cxc.js       /* cxc.js 频繁操作公共接口 */ var $ = function (id) { return document.getElementById(id); }; //通过id获取dom对象 var A = function (msg) { alert(msg); }; //alert的简写 var EmptyFun = function () { }; // 空

JavaScript实现简单图片滚动附源码下载_javascript技巧

昨晚德国和葡萄牙的焦点之战你看了吗?北京时间凌晨的比赛中,C罗领衔的葡萄牙0-4德国被完灭--他是金球奖得主.欧洲金靴.欧冠冠军核心,在葡萄牙队--9张图 C罗告诉你什么叫欲哭无泪 复制代码 代码如下: <span style="font-size:14px;"><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtm

JavaScript 封装一个tab效果源码分享_javascript技巧

效果图如下: 查看演示  源码下载 html代码 <!DOCTYPE html> <html> <head> <title>tab测试</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-" /> <link href="css/tab.css" rel="styles

本人自用的global.js库源码分享_javascript技巧

var GLOBAL = {}; GLOBAL.namespace = function(str) { var arr = str.split("."), o = GLOBAL,i; for (i = (arr[0] = "GLOBAL") ? 1 : 0; i < arr.length; i++) { o[arr[i]] = o[arr[i]] || {}; o = o[arr[i]]; } }; //Dom相关 GLOBAL.namespace("

JavaScript实现的石头剪刀布游戏源码分享_javascript技巧

这个游戏主要设计到两点: 首先是胜负运算 由于石头剪刀布是循环性的 石头 杀 剪子 剪子 杀 布 布   杀  石头 石头  杀  剪子 ... 根据以上特点找出规律,写出算法即可. 让电脑随机 这点比较容易,前面我有写过文章介绍,不明白的童鞋可以去看看. 随机刷屏 其实这个效果不是游戏的关键性,但为了看起来更加互动,好玩,我就给加上了.这里用到了一个取模算法,根据余数去循环显示即可达到效果. 界面截图 最后上代码 <!DOCTYPE html> <html> <head&g

JS简单编号生成器实现方法(附demo源码下载)_javascript技巧

本文实例讲述了JS简单编号生成器实现方法.分享给大家供大家参考,具体如下: 运行效果截图如下: 具体代码如下: <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>编号生成器</title> </head> <body&g

javascript 模拟坦克大战游戏(html5版)附源码下载_javascript技巧

一.总结关键点和遇到的问题 1.javascript中的继承,最好父类只提供方法共享,属性写到各自子类中,避免父类和子类的构造函数混杂. 2.prototype模拟继承的代码,应写在所有方法定义之前,否则原型对象被改变,方法就变成了未定义,如: 复制代码 代码如下: Hero.prototype = new Tank (0, 0, 0); Hero.prototype.constructor = Hero; Hero.prototype.addLife = function(){ this.li

HTML5游戏引擎LTweenLite实现的超帅动画效果(附demo源码下载)_javascript技巧

本文实例讲述了HTML5游戏引擎LTweenLite实现的超帅动画效果.分享给大家供大家参考,具体如下: lufylegend.js是一个开源的HTML5游戏引擎,在游戏中往往会有各种的动画,这些动画有些是flash文件,有些是视频文件,本次就来利用lufylegend制作一个帅气的游戏动画,如下图. 测试连接如下: http://lufylegend.com/demo/effects01/ 一.准备工作 准备工作当然就是引擎的下载了. lufylegend.js引擎官网 http://lufy

cocos2d-x开发的《派对小游戏》-github源码分享

这是博主很久以前写的一个cocos2d-x跨平台小游戏,我称它为<派对小游戏>,现在分享给大家,希望对大家有所帮助的话. 项目源码地址:https://github.com/xiebaochun/partygame-cocos2d-x 游戏截图如下: 主页: 打地鼠 视觉空间 拖拖拉拉