共计 7306 个字符,预计需要花费 19 分钟才能阅读完成。
开始自己手写一个好玩的俄罗斯方块吧,上变形,左右移动,下加速,空格瞬移等功能,无聊的时候学习下 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;
};
}
3、判断掉落是否到底或者碰撞到上一个底部的方块
// 掉落完成后重新调用
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].ys==arr[i].ys+20&&finallys[t][j].xs==arr[i].xs){/*console.log(arr);*/
return 1;
}
}
}
}
return 0;
}
4、下落到底部后的方块要保存起来,定义重新渲染的方法
// 用来组成下标已经堆积的格子
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;
}
}
}
}
5、当横向一行满了后就可以清楚并得到对应的分数
// 判断是否组满一行达到清除的位置
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;
}
}
}
}
6、碰撞检测
// 判断不能掉出围起来的范围
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;}
}
7、按钮操作
$(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);// 判断是否可以加到右边
}
8、定义绘制预知框的方法,如上面绘制的方法一样,只是对应的 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;
};
}
9、开始运行的方法
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()// 开始绘制
正文完
发表至: javascript
2020-05-28