您的位置:首页 > Web前端 > HTML5

HTML5 Canvas实现简单的俄罗斯方块

2012-07-18 14:11 393 查看
HTML5 Canvas实现简单的俄罗斯方块

最近学习了下HTML5,抽时间利用Canvas写了个俄罗斯方块的小游戏,写的比较简单也比较粗糙,还有点bug,算法方面也没深入考虑,只是为了学习使用Canvas。

主要实现思路:

一、绘制画布

画布宽200px,高400px,小方块宽和高为10px。

二、绘制7种形状的方块



在画布上,每个形状的4个小方块,用相对于画布左上角的坐标来绘制,HTML5的Canvas的左上角坐标为x=0,y=0.

以第一个形状举例,四个方块的坐标分别为:



4个坐标位置存储在数组中[0,0,1,0,1,1,1,2]

每个形状的变形后的相对坐标也存储在数组中,这样做是为了简单实现形状变形的算法。

在画布上绘制的时候,需要以画布上的实际坐标绘制四个正方形,正方形的宽度和高度为10px;

fillRect(0,0,10,10)

fillRect(10,0,10,10)

fillRect(10,10,10,10)

fillRect(10,20,10,10)

三、向下移动

每向下移动一次,Y坐标+1,例如上图的坐标变为:0,1,1,1,1,2,1,3

四、左右移动

移动一次,X坐标+1或-1,例如如果上图向右移动1个坐标后的坐标为:1,0,2,0,2,1,2,2。

需要判断当前坐标是否到了画布的左边距或右边距,即每个形状或形状的变形中的最右边的方块到了画布的右边缘,则不能再向右移动,相反,最左边的方块到了左边缘,不能再向左移动。

五、变形

读取当前形状的变形后的初始坐标,加上未变形前的偏移坐标。

六、消除满格行

按【绘制画布】中的描述,画布被分为40*20个区域,即40行、20列,每个区域可以放置一个方块(10*10),画布中的40*20个区域对应一个二维数组,存储每个区域是否有方块,有为1,没有为0

当每个形状下落到最下方的时候,判断该形状所在的行的每个区域是否都有方块,即对应的数组中的值是否都为1,都为1,则表示该行满格,可以消除。

七、操作

  添加按键事件

  上箭头:变形

  下箭头:加速下移

  左箭头:向左移动

  右箭头:向右移动

八、 代码

<!DOCTYPE html>
<!--
Author:kinglau(liushouqian)
Date:2012-07-17
Description:目前有些内容是固定的,例如画布大小。也存在bug
   Blog:http://www.cnblogs.com/kinglau/
         http://weibo.com/u/1712849017

-->
<html>
<head>
<title></title>
<meta charset="UTF-8" />
<style>
#canvas{
border: black 1px solid;
}
</style>
<script type="text/javascript">
//7个基本形状,及变形后的坐标
var shap1=[[0,0,1,0,1,1,1,2],[0,1,1,1,2,1,2,0],[0,0,0,1,0,2,1,2],[0,0,1,0,2,0,0,1]];
var shap2=[[0,0,0,1,0,2,1,0],[0,0,1,0,2,0,2,1],[0,2,1,0,1,1,1,2],[0,0,0,1,1,1,2,1]];
var shap3=[[0,0,1,0,1,1,2,1],[1,0,1,1,0,1,0,2],[0,0,1,0,1,1,2,1],[1,0,1,1,0,1,0,2]]
var shap4=[[0,1,1,0,1,1,2,0],[0,0,0,1,1,1,1,2],[0,1,1,0,1,1,2,0],[0,0,0,1,1,1,1,2]];
var shap5=[[0,0,1,0,0,1,1,1],[0,0,1,0,0,1,1,1],[0,0,1,0,0,1,1,1],[0,0,1,0,0,1,1,1]];
var shap6=[[0,1,1,1,2,1,1,0],[0,0,0,1,0,2,1,1],[0,0,1,0,2,0,1,1],[1,0,1,1,0,1,1,2]];
var shap7=[[0,0,1,0,2,0,3,0],[0,0,0,1,0,2,0,3],[0,0,1,0,2,0,3,0],[0,0,0,1,0,2,0,3]];
var shaps=[shap1,shap2,shap3,shap4,shap5,shap6,shap7];

//单位长度
var unitLen=10;
//画布上下文
var ctx;
//定时
var tid;
//画布区域对应的数组
var resultArray=new Array(40);
function init()
{
//获取画布上下文
ctx=document.getElementById("canvas").getContext('2d');
//增加按键事件
document.addEventListener('keydown',moveShape,false);

//初始化画布区域数组
for(var i=0;i<40;i++)
{
var row=new Array();
for(var j=0;j<20;j++)
{
row[j]=0;
}
resultArray[i]=row;
}
//
startRun=true;
DrawLine();
topTrue=true;
tid=setInterval("DrawTetris();",200);

}

//每个形状在画布上相对于初始位置的坐标偏移量
var rectX=0;
var rectY=0;

//形状变形
var rotate=0;
//当前形状或变形的初始坐标
var t;
//当前形状
var shape;

var startRun=true;

var shapeHeight=0;//当前形状的高度,四个方块的Y坐标的最大差
//用于产生随机形状
var randmShape=1;

//当前形状每个方块的坐标
var shapeXY=new Array(4);

//根据坐标绘制形状
function Draw(){
var i=0;
var tempY=0;
shapeXY=new Array(4);
for(i=0;i<4;i++){
DrawRect((t[i*2]+rectX) *unitLen,(t[i*2+1]+rectY)*unitLen);
var row=new Array(2);
row[0]=(t[i*2+1]+rectY);
row[1]=t[i*2]+rectX;
shapeXY[i]=row;
//shapeXY[i,0]=(t[i*2+1]+rectY);
//shapeXY[i,1]=t[i*2];
if(topTrue==true)
{
if(tempY<(t[i*2+1]+rectY))
{
tempY=t[i*2+1]+rectY;
shapeHeight=tempY+1;
}

}
else
{
//tempY=rectY;
}
}
//rectY=tempY;
rectY+=1;

}

//根据捕获的按键,判断具体的按键
function getDirection(event){
var keyCode = event.which || event.keyCode;
switch(keyCode){
case 1:
case 38:
case 269: //up
return 'up';
break;
case 2:
case 40:
case 270:
return 'down';
break;
case 3:
case 37:
case 271:
return 'left';
break;
case 4:
case 39:
case 272:
return 'right';
break;
case 339: //exit
case 240: //back
return 'back';
break;
}
}
//根据按键确定执行的操作
function moveShape(event){
if(getDirection(event)=='right')
{
for(var i=0;i<4;i++){
if(t[2*i]+rectX+1>=20 || resultArray[2*i+rectX+1]==1 ){
return;
}
}
rectX+=1;
}
if(getDirection(event)=='left')
{
for(var i=0;i<4;i++)
{
if(t[2*i]+rectX-1<0 || resultArray[2*i+rectX-1]==1){
return;
}
}
rectX-=1;

}
if(getDirection(event)=='up'){
var mleft=0;
for(var i=0;i<4;i++){
if(t[i*2]+rectX>mleft){
mleft=rectX;
}
}
if(rotate==3){
rotate=0;
}
else{
rotate+=1;
}
t=shape[rotate];
for(var i=0;i<4;i++){

//t[2*i]=t[2*i]+mleft;
rectX=mleft;
}

}

if(getDirection(event)=='down'){
clearInterval(tid);
tid=setInterval("DrawTetris();",50);
}
}
//定时执行的方法
function DrawTetris(){
if(CheckBottom()==true) return;
//t=shape[rotate];
if(startRun==false){
ctx.clearRect(shapeXY[0][1]*unitLen-1,shapeXY[0][0]*unitLen-1,unitLen+2,unitLen+2);
ctx.clearRect(shapeXY[1][1]*unitLen-1,shapeXY[1][0]*unitLen-1,unitLen+2,unitLen+2);
ctx.clearRect(shapeXY[2][1]*unitLen-1,shapeXY[2][0]*unitLen-1,unitLen+2,unitLen+2);
ctx.clearRect(shapeXY[3][1]*unitLen-1,shapeXY[3][0]*unitLen-1,unitLen+2,unitLen+2);
}
startRun=false;
//ctx.clearRect(0,0,200,400-(shapeHeight)*10);
DrawLine();
var sp=randmShape;
shape=shaps[sp-1];
t=shape[rotate];
Draw();

}

var topTrue=false;
//检查当前形状是否到了画布底部
function CheckBottom()
{

if(topTrue==true){
startRun=true;
topTrue=false;
rectX=9;
rectY=0;
randmShape=Math.floor(Math.random()*7+1);

return true;
}

if(rectY+shapeHeight-1>=40 || rectY==0)
{
//clearInterval(tid);
//ctx.clearRect(0,0,200,300);

if(rectY==0)
{
return false
}

CurrentShapeOnBottom();
return true;
}
else
{
//形状中的四个方块有一个到了底部,就不能再向下移动
if(shapeXY[0][0]==39 || shapeXY[1][0]==39 || shapeXY[2][0]==39 || shapeXY[3][0]==39)
{
CurrentShapeOnBottom();
return true;
}
//形状中的每个方块所在行的下一行,如果已经存在方块,不能再向下移动
if((resultArray[shapeXY[0][0]+1][shapeXY[0][1]]+resultArray[shapeXY[1][0]+1][shapeXY[1][1]]
+resultArray[shapeXY[2][0]+1][shapeXY[2][1]]+resultArray[shapeXY[3][0]+1][shapeXY[3][1]]
>=1) )
{
CurrentShapeOnBottom();
return true;
}
topTrue=false;
return false;
}
}

//当前形状到达画布底部后进行的操作
function CurrentShapeOnBottom(){

resultArray[shapeXY[0][0]][shapeXY[0][1]]=1;
resultArray[shapeXY[1][0]][shapeXY[1][1]]=1;
resultArray[shapeXY[2][0]][shapeXY[2][1]]=1;
resultArray[shapeXY[3][0]][shapeXY[3][1]]=1;

if(ClearRow()==false){
return;
}
rectY=0;
rectX=9;
randmShape=Math.floor(Math.random()*7+1)
startRun=true;
topTrue=true;
clearInterval(tid);
tid=setInterval("DrawTetris();",200);
}
//计分
var vpoint=0;
//清除满格行,并计分
function ClearRow(){
var row=new Array();
var spaceRow=new Array();
var spaceRows=new Array();
for(var i=0;i<20;i++){
spaceRow[i]=0;
}
row[0]=shapeXY[0][0];
for(var i=1;i<4;i++){
for(var j=0;j<row.length;j++){
if(row[j]!=shapeXY[i][0]){
if(row[j]<shapeXY[i][0]){
row.push(shapeXY[i][0]);

}
else {
row.unshift(shapeXY[i][0]);
}
break;

}
}
}

var isNeedRedraw=false;
for(var i=0;i<row.length;i++){

var rowState=0;
for(var j=0;j<20;j++){
rowState+=resultArray[row[i]][j];
}

if(rowState==20){
resultArray.splice(row[i],1);
resultArray.unshift(spaceRow);
vpoint+=10;
document.getElementById("txtPoint").value=vpoint;
isNeedRedraw=true;
}
}

if(isNeedRedraw==true){
ctx.clearRect(0,0,200,400);
RedrawCanvas();

}
else
{
if(shapeXY[0][0]==0 || shapeXY[1][0]==0 || shapeXY[2][0]==0 || shapeXY[3][0]==0){
document.getElementById("idState").innerText="Game Over";
clearInterval(tid);
return false;
}
}
return true;
}

//清除满格行后,重新绘制画布
function RedrawCanvas()
{
for(var i=0;i<40;i++){
for(var j=0;j<20;j++){
if(resultArray[i][j]==1){
DrawRect(j*unitLen,i*unitLen);
}
}
}
}
//绘制形状中的小方格
function DrawRect(x,y){
ctx.fillStyle="red";
ctx.strokeStyle="black";
ctx.lineWidth=1;
ctx.fillRect(x,y,10,10);
ctx.strokeRect(x,y,10,10);
}

function DrawLine()
{
/*
var i=1;
for(i=1;i<40;i++)
{
ctx.beginPath();
ctx.strokeStyle="black";
ctx.lineWidth=1;
ctx.moveTo(0,i*10);
ctx.lineTo(200,i*10);
ctx.stroke();
}
*/
}

</script>

</head>
<body onload="init()">

<canvas id="canvas" width="200" height="400">
浏览器不支持HTML5  Canvas
</canvas>
<br>
<form id="f" name="f" onsubmit="init()">
<br>
<input id="btnStart" type="submit" value="开始" />
<label id="idState"></label>

</div>
<br>

<label>分数:</label>
<input  id="txtPoint" readonly="true"  value="0" />
</form>

</body>
</html>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: