一个简单的俄罗斯方块游戏
基本思路:
用一个二维数组来表示地图,把方块的移动看成在二维数组上的移动,0表示空位,1表示已经落定的方块,2表示正在下落的方块,3表示边界
游戏主要用HTML5的canvas绘图来完成,按照一定的时间间隔在数组值为1或2的地方绘制
游戏在chrom下开发测试,暂时存在一些缺陷,效果见 http://snailanger.free3v.net/russian.html
代码如下
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>russian</title>
<meta name="description" content="">
<meta name="author" content="Thinkpad">
<link rel="stylesheet" type="text/css" href="russian.css" />
</head>
<body>
<div class="backdiv">
<canvas id="mcns" width="200" height="400" class="canvas"></canvas>
<canvas id="icns" width="100" height="100" class="info_canvas"></canvas>
<span id="score" class="info_span">0</span>
</div>
</body>
<script type="text/javascript" src="russian.js"></script>
</html>
css
.backdiv{
width: 350px;
height: 410px;
margin: 0 auto;
}
.canvas{
border: 1px solid gray;
float: right;
}
.info_canvas{
border: 1px solid gray;
float: left;
margin-bottom:70px;
}
.info_span{
float: left;
margin-left:30px;
font-size: 33px
}
js
var x = 3; //全局移动坐标
var y = 4;
var start_x = 3;//起始行
var start_y = 4;
var old_x; //上一步坐标
var old_y;
var map; //0空,1已经落下的块,2正在下落的块,3边界
var row = 24; // 全局数组大小
var column = 12;
var size = 18; //方块大小
var fix_xy = 20; // xy轴偏移量
var fix_x = 19; //xy方向修正
var fix_y = 59;
var canvas_width = 200; //画布大小
var canvas_height = 400;
var canvas; //画布对象
var ctx = null; //画笔
var ctxInfo = null;
var paint_color = "red"; //画笔颜色
var diamonds = null; //当前方块
var nextDiamonds = null; //下一个方块
var count = 0; //统计消行
var clear_row = null; //保存要消的行
var score = 100; //单位得分
var ratio = 1; //分数系数
var total_score = 0; //当前消行总分
var html_score = 0; //总分
var downIndex = 0; //开始消行序号
var run = null; //游戏控制
var speed = 600; //速度
var total_count = 0; //消行总次数
//基本图形分为坐标,颜色,变形指向,最大列,第一个坐标为最下方最右边的块
var baseDiamonds = [{
diamonds:[[0,3],[0,2],[0,1],[0,0],"red",1,3] // ——
},
{
diamonds:[[0,0],[-1,0],[-2,0],[-3,0],"red",0,0] // |
},
{
diamonds:[[0,1],[0,0],[-1,1],[-1,0],"black",2,1] // 口
},
{
diamonds:[[0,2],[0,1],[0,0],[-1,1],"yellow",4,2] // ⊥
},
{
diamonds:[[0,0],[-1,1],[-1,0],[-2,0],"yellow",5,1] // |-
},
{
diamonds:[[0,1],[-1,2],[-1,1],[-1,0],"yellow",6,2] // T
},
{
diamonds:[[0,1],[-1,1],[-1,0],[-2,1],"yellow",3,1] // -|
},
{
diamonds:[[0,0],[-1,0],[-2,0],[-2,1],"green",8,1] // 『
},
{
diamonds:[[0,2],[-1,2],[-1,1],[-1,0],"green",9,2] // 『右转1
},
{
diamonds:[[0,1],[0,0],[-1,1],[-2,1],"green",10,1] // 『右转2
},
{
diamonds:[[0,2],[0,1],[0,0],[-1,0],"green",7,2] // 『右转3
},
{
diamonds:[[0,1],[-1,1],[-2,1],[-2,0],"blue",12,1] // 『反向那个
},
{
diamonds:[[0,2],[0,1],[0,0],[-1,2],"blue",13,2] // 『反向那个右转1
},
{
diamonds:[[0,1],[0,0],[-1,0],[-2,0],"blue",14,1] // 『反向那个右转2
},
{
diamonds:[[0,0],[-1,2],[-1,1],[-1,0],"blue",11,2] // 『反向那个右转3
},
{
diamonds:[[0,2],[0,1],[-1,1],[-1,0],"orange",16,2] // Z
},
{
diamonds:[[0,0],[-1,0],[-1,1],[-2,1],"orange",15,1] // Z右转1
},
{
diamonds:[[0,1],[0,0],[-1,1],[-1,2],"gray",18,2] // Z反向那个
},
{
diamonds:[[0,1],[-1,1],[-1,0],[-2,0],"gray",17,1] // Z反向那个右转1
}
];
//初始化游戏参数
function init(){
canvas = document.getElementById("mcns");
ctx = canvas.getContext("2d");
ctx.fillStyle = paint_color;
canvasInfo = document.getElementById("icns");
ctxInfo = canvasInfo.getContext("2d");
ctxInfo.fillStyle = "blue";
map = new Array(); //初始化地图数组
for(var i=0;i<row;i++){
map.push(new Array());
for(var j=0;j<column;j++){
if(i==row-1 || j==0 || j==column-1){
map[i].push(3);
}else{
map[i].push(0);
}
}
}
x = start_x;
y = start_y;
html_score = document.getElementById("score");
}
//绘制当前方块
function paint(){
ctx.clearRect(0,0,canvas_width,canvas_height); //清空画布
// paintGrid();
resetMap(y,x,2);
for(var i=start_x;i<row;i++){ //在当前位置为1或2的地方绘制
for(var j=0;j<column;j++){
if(map[i][j] == 1 || map[i][j] == 2){
ctx.fillRect(fix_xy*j-fix_x,fix_xy*i-fix_y,size,size);
}
}
}
}
//绘制下一个方块
function paintInfo(){
ctxInfo.clearRect(0,0,100,100);
for(var i=0;i<4;i++){
ctxInfo.fillRect(fix_xy*nextDiamonds[i][1]+21,fix_xy*nextDiamonds[i][0]+61,size,size);
}
}
//绘制背景表格
function paintGrid(){
for(var i=1;i<20;i++){
ctx.moveTo(0,i*fix_xy);
ctx.lineTo(canvas_width,i*fix_xy);
ctx.stroke();
}
for(var i=1;i<10;i++){
ctx.moveTo(i*fix_xy,0);
ctx.lineTo(i*fix_xy,canvas_height);
ctx.stroke();
}
}
//给map赋值
function resetMap(r,c,val){
for(var i=0;i<4;i++){
map[c+diamonds[i][0]][r+diamonds[i][1]] = val;
}
}
//方块下落
function down(){
old_x = x; //保存当前位置
old_y = y;
if(!isMove(1,0)){ //如果下一个位置有块,则当前下落结束
resetMap(y,x,1);
if(isGameOver()){ //判断游戏是否结束
gameOver(); //游戏结束
}else{
clearRow(); //消行
x = start_x; //将 下落方块坐标还原
y = start_y;
if(createDemonds()){ //生成新的方块
paint();
paintInfo();
}
}
}else{ //方块下落一个位置
resetMap(old_y,old_x,0);
++x;
}
paint();
}
//判断下个位置是否可以移动
function isMove(mx,my){
for(var i=0;i<4;i++){ //如果下个位置为1或3,即已经落下的方块和边界,则不能移动
if(map[x+diamonds[i][0]+mx][y+diamonds[i][1]+my] == 1 || map[x+diamonds[i][0]+mx][y+diamonds[i][1]+my] == 3){
return false;
}
}
return true;
}
//改变方块方向
function changeDirect(val){
resetMap(y,x,0);
y += val;
ctx.clearRect(0,0,canvas_width,canvas_height);
paint();
}
//消行
function clearRow(){
clear_row = new Array();
for(var i=x,len=x-4;i>len;i--){ //从当前位置开始扫描,最多向上扫描三行
count = 0;
for(var j=1,len2=column-1;j<len2;j++){
count += map[i][j];
}
if(count == 10){
clear_row.push(i);
}
}
if(clear_row.length > 0){ //如果有要消的行
for (var i=0,len=clear_row.length;i<len;i++) { //消行
for (var j=0,len2=column-1; j<len2; j++) {
map[clear_row[i]][j] = 0;
}
}
switch(clear_row.length){ //计分系数
case 1:ratio = 1; break;
case 2:ratio = 3; break;
case 3:ratio = 5; break;
case 4:ratio = 7; break;
}
total_score += score * ratio;
html_score.innerHTML = total_score;
total_count += clear_row.length;
diamondsDown();//消完后下落
if(total_count%7==0){ //根据消行次数,改变速度
speed -= 70;
begin();
}
}
}
//消行后块下落
function diamondsDown(){
clear_row.push(start_x);
for(var k=0,len=clear_row.length-1;k<len;k++){
downIndex = clear_row[k]; //获取下落起始行
for(var i=downIndex;i>clear_row[k+1];i--){ //逐行扫描,按所消的行数下落
for(var j=1,len2=column-1;j<len2;j++){
map[i+k][j] = map[i-1][j]; //第一次下移一行,第二次两行,以此类推
map[i-1][j] = 0;
}
}
}
}
//生成方块
function createDemonds(){
if(!nextDiamonds){ //如果下一个不存在
if(!diamonds){
diamonds = baseDiamonds[parseInt(Math.random()*19)].diamonds;
}
nextDiamonds = baseDiamonds[parseInt(Math.random()*19)].diamonds;
}else{ //若存在,将当前方块换成上次生成的下一个,继续生成下一个
diamonds = nextDiamonds;
nextDiamonds = baseDiamonds[parseInt(Math.random()*19)].diamonds;
}
return true;
}
//变形
function change(){
resetMap(y,x,0); //将当前位置清空
diamonds = baseDiamonds[diamonds[5]].diamonds; //获取当前方块右转后的方块
if(y+diamonds[6]>10){ //靠右变形修正
y = 10-diamonds[6];
}
paint();
}
//判断游戏是否结束
function isGameOver(){
for(var j=1;j<column;j++){
if(map[start_x][j] == 1){
return true;
}
}
return false;
}
//暂停
function stop(){
if(run){ //如果当前正在运动,则清掉
window.clearInterval(run);
run = null;
}else{
run = setInterval("down()",speed);
}
}
function begin(){
if(run){ //如果当前正在运动,则清掉
window.clearInterval(run);
run = setInterval("down()",speed);
}
}
//游戏结束
function gameOver(){
if (confirm('Game Over,是否重新开始?')) {
window.clearInterval(run);
location.reload(true);
}
}
//键盘事件
function keyDown(e){
var keycode;
if(e){
keycode = e.keyCode;
}else{
keycode = window.event.keyCode;
}
switch(keycode){
case 37: //左
if(isMove(0,-1)){
changeDirect(-1);
}
break;
case 38: //上
change();
break;
case 39: //右
if(isMove(0,1)){
changeDirect(1);
}
break;
case 40: //下
down(); //加速下落
break;
case 83: //暂停
stop();
break;
}
}
document.onkeydown = keyDown; //绑定事件
//开始游戏
init();
if(createDemonds()){
paint();
paintInfo();
run = setInterval("down()",speed);
}