介绍
手写俄罗斯方块小游戏
# 简单手写俄罗斯方块
# 开始自己手写一个好玩的俄罗斯方块吧,上变形,左右移动,下加速,空格瞬移等功能,无聊的时候学习下canvas,f12 修改分数,体验金手指的快乐吧
# 1、定义界面,和按钮
<div id="by">
<div id="title">
<!--//定义游戏界面-->
<canvas id="myCanvas" height="600" width="400" style="border: 2px solid #3c763d"></canvas>
<!--//定义下一个方块的预知框-->
<div id="title2">
<canvas id="Canvas" height="150" width="150" style="border: 2px solid #3c763d"></canvas>
</div>
</div>
<!--背景图-->
<div id="">
<img src="http://cnd.yinglingxuan.cn/75Z58PICq67_1024.jpg" id="img" width="405px" height="602px" />
</div>
</div>
<!--按钮-->
<div class="aj">
<span onclick="tops()">上</span>
<span onclick="under()">下</span>
<span onclick="lefts()">左</span>
<span onclick="rights()">右</span>
</div>
# 2、js部分
# 1、先定义每个图形的形状和变化的形状,这里是使用多维数组的方式去保存它图形每个方块的位置(当然这里也可以用循环的方式)
var data=[[[[1,0,0,0],[1,0,0,0],[1,1,0,0],[0,0,0,0]],[[1,1,1,0],[1,0,0,0],[0,0,0,0],[0,0,0,0]],[[1,1,0,0],[0,1,0,0],[0,1,0,0],[0,0,0,0]],[[0,0,1,0],[1,1,1,0],[0,0,0,0],[0,0,0,0]]],
[[[1,0,0,0],[1,1,0,0],[1,0,0,0],[0,0,0,0]],[[1,1,1,0],[0,1,0,0],[0,0,0,0],[0,0,0,0]],[[0,0,1,0],[0,1,1,0],[0,0,1,0],[0,0,0,0]],[[0,0,0,0],[0,1,0,0],[1,1,1,0],[0,0,0,0]]],
[[[0,1,0,0],[0,1,1,0],[0,0,1,0],[0,0,0,0]],[[0,1,1,0],[1,1,0,0],[0,0,0,0],[0,0,0,0]],[[0,1,0,0],[0,1,1,0],[0,0,1,0],[0,0,0,0]],[[0,0,0,0],[0,1,1,0],[1,1,0,0],[0,0,0,0]]],
[[[0,1,1,0],[0,1,1,0],[0,0,0,0],[0,0,0,0]],[[0,1,1,0],[0,1,1,0],[0,0,0,0],[0,0,0,0]],[[0,1,1,0],[0,1,1,0],[0,0,0,0],[0,0,0,0]],[[0,1,1,0],[0,1,1,0],[0,0,0,0],[0,0,0,0]]],
[[[0,0,1,0],[0,1,1,0],[0,1,0,0],[0,0,0,0]],[[1,1,0,0],[0,1,1,0],[0,0,0,0],[0,0,0,0]],[[0,0,1,0],[0,1,1,0],[0,1,0,0],[0,0,0,0]],[[1,1,0,0],[0,1,1,0],[0,0,0,0],[0,0,0,0]]],
[[[0,1,0,0],[0,1,0,0],[0,1,0,0],[0,1,0,0]],[[0,0,0,0],[1,1,1,1],[0,0,0,0],[0,0,0,0]],[[0,1,0,0],[0,1,0,0],[0,1,0,0],[0,1,0,0]],[[0,0,0,0],[1,1,1,1],[0,0,0,0],[0,0,0,0]]],
[[[1,1,0,0],[1,0,0,0],[1,0,0,0],[0,0,0,0]],[[1,1,1,0],[0,0,1,0],[0,0,0,0],[0,0,0,0]],[[0,0,0,0],[0,0,1,0],[0,0,1,0],[0,1,1,0]],[[0,0,0,0],[0,0,0,0],[1,0,0,0],[1,1,1,0]]]
];
# 2、定义图形的位置和每帧下落的速度
var newX=120;//记录这个图形的左右位置
var count=0; //记录下落的速度
var shape=0; //记录形状
var finallys=new Array(); //记录落下后停止的位置
var re=6; //获取随机数方块
var fat=1; //每帧的速度
var xyg=0; //下一个方块
var dfen=0;//分数
//定义游戏界面
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
//下个方格的画布
var c2=document.getElementById("Canvas");
var ctx2=c2.getContext("2d");
# 3、定义组成图形的方法
//组成图形的方法
function constitute(){
var Arr=new Array(); // 存储当前下来的方块位置
var y=0; //记录每个小方块组成图形的小方块y下标--相加
for (var i=0;i<data[re][shape].length;i++) {
var x=0; //记录每个小方块组成图形的小方块x下标
for (var j=0;j<data[re][shape][i].length;j++) {
x+=20;
if(data[re][shape][i][j]==1&&Arr.length<4){ //每个图形都是四个小方块组成
ctx.strokeStyle = "#000";
ctx.strokeRect(x+newX,y+count,20,20);
ctx.fillStyle="#000000"; //定义颜色
ctx.fillRect(x+newX,y+count,19,19); //绘图
var a={
xs:x+newX,
ys:y+count
};
Arr.push(a); //记录图形位置
if(Arr.length==4){ //图形形成后调用掉落完成后重新调用
anew(Arr);
return Arr;
}
}
}
y+=20;
};
}
# 4、判断掉落是否到底或者碰撞到上一个底部的方块
//掉落完成后重新调用
function anew (arr){
var da=bottom(arr); //调用判断是否到底
if(da==1){ //如果返回1 则已经是到底部,
finallys.push(arr); // 保存在底部的方块,方便碰撞判断
count=0; //恢复成原来的上下位置
newX=120; //恢复原来的左右位置
re=xyg; //当前的方块变成上一个的下一个
xyg=Math.floor(Math.random()*7); //重新随机获取当前的下一个
ctx2.clearRect(0, 0, c.width,c.height); //清除画布
xygs();//重新绘制预知框的
}
}
function bottom(arr){ //判断是否掉落底部//返回1表示可以保存起来
for (var i =0;i<arr.length;i++) {
if(arr[i].ys>=c.height-20){//首先判断是否到界面的底部
/console.log(arr);/
return 1;
}
for (var t=0;t<finallys.length;t++) {//再到保存的底部图形里面遍历出来判断是否与其他图形发生了碰撞
for (var j=0;j<finallys[t].length;j++) {
if(finallys[t][j].ysarr[i].ys+20&&finallys[t][j].xsarr[i].xs){
/console.log(arr);/
return 1;
}
}
}
}
return 0;
}
# 5、下落到底部后的方块要保存起来,定义重新渲染的方法
//用来组成下标已经堆积的格子
function ground(finallys){
deletes();//判断是否到达顶部了, 顶部了就直接清除重新开始
for (var i=0;i<finallys.length;i++) { //新图形开始的时候重新渲染已经到底部保存的图形
for (var j=0;j<finallys[i].length;j++) {
ctx.strokeStyle = "#000";
ctx.strokeRect(finallys[i][j].xs,finallys[i][j].ys,20,20);
ctx.fillStyle="#000000"; //定义颜色
ctx.fillRect(finallys[i][j].xs,finallys[i][j].ys,19,19); //画图
if(finallys[i][j].ys<=20){
this.finallys=new Array();
count=0;
constitute(); //调用组成画布的方法
/*ctx.clearRect(0, 0, c.width,c.height); //清除画布*/
return;
}
}
}
}
# 6、当横向一行满了后就可以清楚并得到对应的分数
//判断是否组满一行达到清除的位置
function deletes(){
var c=600;//当前横向的宽度
var add=new Array;//记录宽度的每个方块
for (var i=0;i<c/20;i++) {//除以当前方块的宽度获得每个横度的方块位置
c=c-20;
add.push(c);
}
for (var a=0;a<add.length;a++) {//判断每个横向的方块是否堆积满了
var cou=0;
for (var t=0;t<finallys.length;t++) {
for (var j=0;j<finallys[t].length;j++) {
if(add[a]==finallys[t][j].ys){
cou++;
}
}
}
if(cou>=20){ //如果横向满了调用清除的方法
qc(add[a]);//
cou=0;
}
}
}
//一行满了后清除和上面的向下增加移动
function qc(add){
for (var t=0;t<finallys.length;t++) {
for (var j=0;j<finallys[t].length;j++) {
if(add==finallys[t][j].ys){
finallys[t].splice(j,1);
dfen=dfen+10;
j--;
}
}
}
for (var t=0;t<finallys.length;t++) {
for (var j=0;j<finallys[t].length;j++) {
if(finallys[t][j].ys<add){
finallys[t][j].ys=finallys[t][j].ys+20;
}
}
}
}
# 7、碰撞检测
//判断不能掉出围起来的范围
function crash(count,e){ //count 下落的位置, e 表示当前是什么按钮操作
var newCount=0; //判断是否有下降的数据有就下降
if(count!=0){
newCount=count;
}
var y=0; //记录组成形状的位置--相加
var r=0;
var make=-1;
for (var i=0;i<data[re][shape].length;i++) {
var x=0;
for (var j=0;j<data[re][shape][i].length;j++) {
x+=20;
if(data[re][shape][i][j]==1){
//防止变形的时候溢出
if(e==38&&x+newX<0||x+newX>c.width-20){
if(x+newX<0){
newX=newX+20;
r=1;
}else{
newX=newX-20;
r=2;
}
}
if(e==38){
if(y+count>c.height-20){
if(shape!=0){
shape=shape-1;
}else{
shape=3;
}
}
}
////防止突出右边
if(e==39&&c.width-20<x+newX+20){
make=1;
}
//防止突出左边
if(e==37&&x+newX-20<0){
make=1;
}
if(y+newCount>c.height-20){
count=count-20;
}
var ys=(y+count)%20; //下落的时候可能会一直按着 速度快会直接插到下面,所以这里要求余,当到这个方块的位置的时候取整判断
var es=20-ys;
var newCount=y+count+es;
for (var t=0;t<finallys.length;t++) {
for (var j1=0;j1<finallys[t].length;j1++) {
if(finallys[t][j1].ys==newCount&&finallys[t][j1].xs==x+newX+20&&e==39){
newX=newX-20;
}
if(finallys[t][j1].ys==newCount&&finallys[t][j1].xs==x+newX-20&&e==37){
newX=newX+20;
}
if(finallys[t][j1].ys+20==newCount&&finallys[t][j1].xs==x+newX&&e==38){
if(shape!=0){
shape=shape-1;
}else{
shape=3;
}
if(r==1){
newX=newX-20;
}
if(r==2){
newX=newX+20;
}
}
}
}
}
}
y+=20;
};
if(e==39&&make==-1){
newX=newX+20;
}
if(e==37&&make==-1){
newX=newX-20;
}
}
# 8、按钮操作
$(document).keydown(function(e){ //电脑键盘的
if(e.keyCode==39){ //右
crash(count,e.keyCode);//判断是否可以加到右边
}else if(e.keyCode==37){ //左
//判断是否可以加到左边
crash(count,e.keyCode);
}else if(e.keyCode==40){ //下
var i=count%10;
count=count-i;
fat=10;
}else if(e.keyCode==38){ //上
shape++;
if(shape>3){
shape=0;
}
crash(count,e.keyCode); //防止变形溢出
}else if(e.keyCode==32){
var i=count%20;
count=count-i;
for (var t=0;t<400;t++) {
count=count+10;
var arr=constitute();
var e=bottom(arr);
if(e==1){
break;
}
}
}
});
//////////////////////////// 手机端的按钮
function under(){
var i=count%10;
count=count-i;
fat=10;
setTimeout(function(){
fat=1;
},100)
}
document.onkeyup=function(){
fat=1;
}
function tops(){
shape++;
if(shape>3){
shape=0;
}
crash(count,38); //防止变形溢出
}
function lefts(){
//判断是否可以加到左边
crash(count,37);
}
function rights(){
crash(count,39);//判断是否可以加到右边
}
# 9、定义绘制预知框的方法,如上面绘制的方法一样,只是对应的canvas不同
//预知框里面的绘制
function xygs(){
var y=0; //记录组成形状的位置--相加
for (var i=0;i<data[xyg][shape].length;i++) {
var x=0;
for (var j=0;j<data[xyg][shape][i].length;j++) {
x+=20;
if(data[xyg][shape][i][j]==1){
ctx2.strokeStyle = "#000";
ctx2.strokeRect(x+30,y+50,20,20);
ctx2.fillStyle="#000000"; //定义颜色
ctx2.fillRect(x+30,y+50,19,19); //画图
}
}
y+=20;
};
}
# 10、开始运行的方法
function dy(){
ctx.clearRect(0, 0, c.width,c.height); //清除画布
constitute(); //调用组成画布的方法
count=count+fat; // 下落的速度
ground(finallys); //调用已经在底部的方块的方法
ctx.font="20px 微软雅黑";
ctx.fillText("得分:"+dfen+"",20,30);
requestAnimationFrame(dy); //调用循环运行
}
requestAnimationFrame(dy); //开始运行每一帧
if(finallys.length==0){ //获取第一个预知框
xyg=Math.floor(Math.random()*7);//随机获取
xygs();//调用预知框的渲染
}
constitute()//开始绘制