100字范文,内容丰富有趣,生活中的好帮手!
100字范文 > 用html写游戏 Html5写一个简单的俄罗斯方块小游戏

用html写游戏 Html5写一个简单的俄罗斯方块小游戏

时间:2020-10-30 08:22:59

相关推荐

用html写游戏 Html5写一个简单的俄罗斯方块小游戏

导言

在一个风和日丽的一天,看完了疯狂HTML 5+CSS 3+JavaScript讲义,跟着做了书里最后一章的俄罗斯方块小游戏,并做了一些改进,作为自己前端学习的第一站。

游戏效果:

制作思路

因为书里的俄罗斯方块比较普通,太常规了,不是很好看,所以我在网上找了上面那张图片,打算照着它来做。(请无视成品和原图的差距)

然后便是游戏界面和常规的俄罗斯方块游戏逻辑。

接着便是游戏结束界面了。

原本想做个弹出层,但觉得找图片有点麻烦,所以就在网上找了文字特效,套用了一下。

代码实现:

首先是html文件和css文件,主要涉及了布局方面。作为新手,在上面真的是翻来覆去的踩坑。o(╥﹏╥)o

index.html

俄罗斯方块

/*导入外部的字体文件*/

@font-face{

font-family:tmb;/*为字体命名为tmb*/

src:url("DS-DIGIB.TTF") format("TrueType");/*format为字体文件格式,TrueType为ttf*/

}

div>span{

font-family:tmb;

font-size:18pt;

color:green;

}

速度:1

当前分数:0

最高分数:0

teris.css

*{

margin:0;

padding:0;

}

html, body{

width:100%;

height:100%;

}

.bg{

font-size:13pt;

background-color:rgb(239, 239, 227);

/*好看的渐变色*/

background-image:radial-gradient(rgb(239, 239, 227), rgb(230, 220, 212));

/*阴影*/

box-shadow:#cdc8c1 -1px -1px 7px 0px;

padding-bottom:4px;

}

.ui_bg{

border-bottom:1px #a69e9ea3 solid;

padding-bottom:2px;

overflow:hidden;/*没有这句的话因为子div都设置了float,所以是浮在网页上的,所以父div就没有高度,这句清除了浮动,让父div有了子div的高度*/

}

然后是重头戏,teris.js

游戏变量

//游戏设定

var TETRIS_ROWS = 20;

var TETRIS_COLS = 14;

var CELL_SIZE = 24;

var NO_BLOCK=0;

var HAVE_BLOCK=1;

// 定义几种可能出现的方块组合

var blockArr = [

// Z

[

{x: TETRIS_COLS / 2 - 1 , y:0},

{x: TETRIS_COLS / 2 , y:0},

{x: TETRIS_COLS / 2 , y:1},

{x: TETRIS_COLS / 2 + 1 , y:1}

],

// 反Z

[

{x: TETRIS_COLS / 2 + 1 , y:0},

{x: TETRIS_COLS / 2 , y:0},

{x: TETRIS_COLS / 2 , y:1},

{x: TETRIS_COLS / 2 - 1 , y:1}

],

// 田

[

{x: TETRIS_COLS / 2 - 1 , y:0},

{x: TETRIS_COLS / 2 , y:0},

{x: TETRIS_COLS / 2 - 1 , y:1},

{x: TETRIS_COLS / 2 , y:1}

],

// L

[

{x: TETRIS_COLS / 2 - 1 , y:0},

{x: TETRIS_COLS / 2 - 1, y:1},

{x: TETRIS_COLS / 2 - 1 , y:2},

{x: TETRIS_COLS / 2 , y:2}

],

// J

[

{x: TETRIS_COLS / 2 , y:0},

{x: TETRIS_COLS / 2 , y:1},

{x: TETRIS_COLS / 2 , y:2},

{x: TETRIS_COLS / 2 - 1, y:2}

],

// □□□□

[

{x: TETRIS_COLS / 2 , y:0},

{x: TETRIS_COLS / 2 , y:1},

{x: TETRIS_COLS / 2 , y:2},

{x: TETRIS_COLS / 2 , y:3}

],

// ┴

[

{x: TETRIS_COLS / 2 , y:0},

{x: TETRIS_COLS / 2 - 1 , y:1},

{x: TETRIS_COLS / 2 , y:1},

{x: TETRIS_COLS / 2 + 1, y:1}

]

];

// 记录当前积分

var curScore=0;

// 记录曾经的最高积分

var maxScore=1;

var curSpeed=1;

//ui元素

var curSpeedEle=document.getElementById("cur_speed");

var curScoreEle=document.getElementById("cur_points");

var maxScoreEle=document.getElementById("max_points");

var timer;//方块下落控制

var myCanvas;

var canvasCtx;

var tetris_status;//地图数据

var currentFall;//当前下落的block

游戏界面的完善

//create canvas

function createCanvas(){

myCanvas=document.createElement("canvas");

myCanvas.width=TETRIS_COLS*CELL_SIZE;

myCanvas.height=TETRIS_ROWS*CELL_SIZE;

//绘制背景

canvasCtx=myCanvas.getContext("2d");

canvasCtx.beginPath();

//TETRIS_COS

for(let i=1; i

canvasCtx.moveTo(i*CELL_SIZE, 0);

canvasCtx.lineTo(i*CELL_SIZE, myCanvas.height);

}

for(let i=1; i

canvasCtx.moveTo(0, i*CELL_SIZE);

canvasCtx.lineTo(myCanvas.width, i*CELL_SIZE);

}

canvasCtx.closePath();

canvasCtx.strokeStyle="#b4a79d";

canvasCtx.lineWidth=0.6;

canvasCtx.stroke();

//第一行,最后一行,第一列,最后一列粗一点。

canvasCtx.beginPath();

canvasCtx.moveTo(0, 0);

canvasCtx.lineTo(myCanvas.width, 0);

canvasCtx.moveTo(0, myCanvas.height);

canvasCtx.lineTo(myCanvas.width, myCanvas.height);

canvasCtx.moveTo(0, 0);

canvasCtx.lineTo(0, myCanvas.height);

canvasCtx.moveTo(myCanvas.width, 0);

canvasCtx.lineTo(myCanvas.width, myCanvas.height);

canvasCtx.closePath();

canvasCtx.strokeStyle="#b4a79d";

canvasCtx.lineWidth=4;

canvasCtx.stroke();

//设置绘制block时的style

canvasCtx.fillStyle="#201a14";

}

draw canvas

function changeWidthAndHeight(w, h){

//通过jquery设置css

h+=$("ui_bg").css("height")+$("ui_bg").css("margin-rop")+$("ui_bg").css("margin-bottom")+$("ui_bg").css("padding-top")+$("ui_bg").css("padding-bottom");

$(".bg").css({

"width":w,

"height":h,

"top":0, "bottom":0, "right":0, "left":0,

"margin":"auto"

});

}

change width and height

//draw blocks

function drawBlocks(){

//清空地图

for(let i=0; i

for(let j=0;j

canvasCtx.clearRect(j*CELL_SIZE+1, i*CELL_SIZE+1, CELL_SIZE-2, CELL_SIZE-2);

}

//绘制地图

for(let i=0; i

for(let j=0;j

if(tetris_status[i][j]!=NO_BLOCK)

canvasCtx.fillRect(j*CELL_SIZE+1, i*CELL_SIZE+1, CELL_SIZE-2, CELL_SIZE-2);//中间留点缝隙

}

}

//绘制currentFall

for(let i=0;i

canvasCtx.fillRect(currentFall[i].x*CELL_SIZE+1, currentFall[i].y*CELL_SIZE+1, CELL_SIZE-2,CELL_SIZE-2);

}

draw block

游戏逻辑

function rotate(){

// 定义记录能否旋转的旗标

var canRotate = true;

for (var i = 0 ; i < currentFall.length ; i++)

{

var preX = currentFall[i].x;

var preY = currentFall[i].y;

// 始终以第三个方块作为旋转的中心,

// i == 2时,说明是旋转的中心

if(i != 2)

{

// 计算方块旋转后的x、y坐标

var afterRotateX = currentFall[2].x + preY - currentFall[2].y;

var afterRotateY = currentFall[2].y + currentFall[2].x - preX;

// 如果旋转后所在位置已有方块,表明不能旋转

if(tetris_status[afterRotateY][afterRotateX + 1] != NO_BLOCK)

{

canRotate = false;

break;

}

// 如果旋转后的坐标已经超出了最左边边界

if(afterRotateX < 0 || tetris_status[afterRotateY - 1][afterRotateX] != NO_BLOCK)

{

moveRight();

afterRotateX = currentFall[2].x + preY - currentFall[2].y;

afterRotateY = currentFall[2].y + currentFall[2].x - preX;

break;

}

if(afterRotateX < 0 || tetris_status[afterRotateY-1][afterRotateX] != NO_BLOCK)

{

moveRight();

break;

}

// 如果旋转后的坐标已经超出了最右边边界

if(afterRotateX >= TETRIS_COLS - 1 ||

tetris_status[afterRotateY][afterRotateX+1] != NO_BLOCK)

{

moveLeft();

afterRotateX = currentFall[2].x + preY - currentFall[2].y;

afterRotateY = currentFall[2].y + currentFall[2].x - preX;

break;

}

if(afterRotateX >= TETRIS_COLS - 1 ||

tetris_status[afterRotateY][afterRotateX+1] != NO_BLOCK)

{

moveLeft();

break;

}

}

}

if(canRotate){

for (var i = 0 ; i < currentFall.length ; i++){

var preX = currentFall[i].x;

var preY = currentFall[i].y;

if(i != 2){

currentFall[i].x = currentFall[2].x +

preY - currentFall[2].y;

currentFall[i].y = currentFall[2].y +

currentFall[2].x - preX;

}

}

localStorage.setItem("currentFall", JSON.stringify(currentFall));

}

}

旋转

//按下 下 或 interval到了

function next(){

if(moveDown()){

//记录block

for(let i=0;i

tetris_status[currentFall[i].y][currentFall[i].x]=HAVE_BLOCK;

//判断有没有满行的

for(let j=0;j

for(let i=0;i

if(tetris_status[currentFall[j].y][i]==NO_BLOCK)

break;

//最后一行满了

if(i==TETRIS_COLS-1){

//消除最后一行

for(let i=currentFall[j].y; i>0;i--){

for(let j=0;j

tetris_status[i][j]=tetris_status[i-1][j];

}

//分数增加

curScore+=5;

localStorage.setItem("curScore", curScore);

if(curScore>maxScore){

//超越最高分

maxScore=curScore;

localStorage.setItem("maxScore", maxScore);

}

//加速

curSpeed+=0.1;

localStorage.setItem("curSpeed", curSpeed);

//ui输出

curScoreEle.innerHTML=""+curScore;

maxScoreEle.innerHTML=""+maxScore;

curSpeedEle.innerHTML=curSpeed.toFixed(1);//保留两位小数

clearInterval(timer);

timer=setInterval(function(){

next();

}, 500/curSpeed);

}

}

}

//判断是否触顶

for(let i=0;i

if(currentFall[i].y==0){

gameEnd();

return;

}

}

localStorage.setItem("tetris_status", JSON.stringify(tetris_status));

//新的block

createBlock();

localStorage.setItem("currentFall", JSON.stringify(currentFall));

}

drawBlocks();

}

//右移

function moveRight(){

for(let i=0;i

if(currentFall[i].x+1>=TETRIS_ROWS || tetris_status[currentFall[i].y][currentFall[i].x+1]!=NO_BLOCK)

return;

}

for(let i=0;i

currentFall[i].x++;

localStorage.setItem("currentFall", JSON.stringify(currentFall));

return;

}

//左移

function moveLeft(){

for(let i=0;i

if(currentFall[i].x-1<0 || tetris_status[currentFall[i].y][currentFall[i].x-1]!=NO_BLOCK)

return;

}

for(let i=0;i

currentFall[i].x--;

localStorage.setItem("currentFall", JSON.stringify(currentFall));

return;

}

//judge can move down and if arrive at end return 1, if touch other blocks return 2, else, return 0

function moveDown(){

for(let i=0;i

if(currentFall[i].y>=TETRIS_ROWS-1 || tetris_status[currentFall[i].y+1][currentFall[i].x]!=NO_BLOCK)

return true;

}

for(let i=0;i

currentFall[i].y+=1;

return false;

}

上下左右移动

function gameKeyEvent(evt){

switch(evt.keyCode){

//向下

case 40://↓

case 83://S

next();

drawBlocks();

break;

//向左

case 37://←

case 65://A

moveLeft();

drawBlocks();

break;

//向右

case 39://→

case 68://D

moveRight();

drawBlocks();

break;

//旋转

case 38://↑

case 87://W

rotate();

drawBlocks();

break;

}

}

keydown事件监听

keydown事件监听

其他的详细情况可以看源代码,我就不整理了。

接下来我们看游戏结束时的特效。因为我也不是很懂,所以在这里整理的会比较详细。当做学习。

//game end

function gameEnd(){

clearInterval(timer);

//键盘输入监听结束

window.οnkeydοwn=function(){

//按任意键重新开始游戏

window.οnkeydοwn=gameKeyEvent;

//初始化游戏数据

initData();

createBlock();

localStorage.setItem("currentFall", JSON.stringify(currentFall));

localStorage.setItem("tetris_status", JSON.stringify(tetris_status));

localStorage.setItem("curScore", curScore);

localStorage.setItem("curSpeed", curSpeed);

//绘制

curScoreEle.innerHTML=""+curScore;

curSpeedEle.innerHTML=curSpeed.toFixed(1);//保留两位小数

drawBlocks();

timer=setInterval(function(){

next();

}, 500/curSpeed);

//清除特效

this.stage.removeAllChildren();

this.textStage.removeAllChildren();

};

//特效,游戏结束

setTimeout(function(){

initAnim();

//擦除黑色方块

for(let i=0; i

for(let j=0;j

canvasCtx.clearRect(j*CELL_SIZE+1, i*CELL_SIZE+1, CELL_SIZE-2, CELL_SIZE-2);

}

}, 200);

//推迟显示Failed

setTimeout(function(){

if(textFormed) {

explode();

setTimeout(function() {

createText("FAILED");

}, 810);

} else {

createText("FAILED");

}

}, 800);

}

上面代码里的localstorage是html5的本地数据存储。因为不是运用很难,所以具体看代码。

整个特效是运用了createjs插件。要引入几个文件。

easeljs-0.7.1.min.js,EasePacj.min.js,requestAnimationFrame.js和TweenLite.min.js 游戏重新开始就要清除特效。我看api里我第一眼望过去最明显的就是removeAllChildren(),所以就选了这个。其他的改进日后再说。

//清除特效

this.stage.removeAllChildren();

this.textStage.removeAllChildren();

function initAnim() {

initStages();

initText();

initCircles();

//在stage下方添加文字——按任意键重新开始游戏.

tmp = new createjs.Text("t", "12px 'Source Sans Pro'", "#54555C");

tmp.textAlign = 'center';

tmp.x = 180;

tmp.y=350;

tmp.text = "按任意键重新开始游戏";

stage.addChild(tmp);

animate();

}

initAnim

上面初始化了一个stage,用于存放特效,一个textstage,用于形成“FAILED”的像素图片。还有一个按任意键重新游戏的提示。同时开始每隔一段时间就刷新stage。

根据block的位置来初始化小圆点。

function initCircles() {

circles = [];

var p=[];

var count=0;

for(let i=0; i

for(let j=0;j

if(tetris_status[i][j]!=NO_BLOCK)

p.push({'x':j*CELL_SIZE+2, 'y':i*CELL_SIZE+2, 'w':CELL_SIZE-3, 'h':CELL_SIZE-4});

for(var i=0; i<250; i++) {

var circle = new createjs.Shape();

var r = 7;

//x和y范围限定在黑色block内

var x = p[count]['x']+p[count]['w']*Math.random();

var y = p[count]['y']+p[count]['h']*Math.random();

count++;

if(count>=p.length)

count=0;

var color = colors[Math.floor(i%colors.length)];

var alpha = 0.2 + Math.random()*0.5;

circle.alpha = alpha;

circle.radius = r;

circle.graphics.beginFill(color).drawCircle(0, 0, r);

circle.x = x;

circle.y = y;

circles.push(circle);

stage.addChild(circle);

circle.movement = 'float';

tweenCircle(circle);

}

}

initCircles

然后再讲显示特效Failed的createText()。先将FAILED的text显示在textstage里,然后ctx.getImageData.data获取像素数据,并以此来为每个小圆点定义位置。

function createText(t) {

curText=t;

var fontSize = 500/(t.length);

if (fontSize > 80) fontSize = 80;

text.text = t;

text.font = "900 "+fontSize+"px 'Source Sans Pro'";

text.textAlign = 'center';

text.x = TETRIS_COLS*CELL_SIZE/2;

text.y = 0;

textStage.addChild(text);

textStage.update();

var ctx = document.getElementById('text').getContext('2d');

var pix = ctx.getImageData(0,0,600,200).data;

textPixels = [];

for (var i = pix.length; i >= 0; i -= 4) {

if (pix[i] != 0) {

var x = (i / 4) % 600;

var y = Math.floor(Math.floor(i/600)/4);

if((x && x%8 == 0) && (y && y%8 == 0)) textPixels.push({x: x, y: y});

}

}

formText();

textStage.clear();//清楚text的显示

}

CreateText

跟着代码的节奏走,我们现在来到了formtext.

function formText() {

for(var i= 0, l=textPixels.length; i

circles[i].originX = offsetX + textPixels[i].x;

circles[i].originY = offsetY + textPixels[i].y;

tweenCircle(circles[i], 'in');

}

textFormed = true;

if(textPixels.length < circles.length) {

for(var j = textPixels.length; j

circles[j].tween = TweenLite.to(circles[j], 0.4, {alpha: 0.1});

}

}

}

formtext

explode()就是讲已组成字的小圆点给重新遣散。

动画实现是使用了tweenlite.

function tweenCircle(c, dir) {

if(c.tween) c.tween.kill();

if(dir == 'in') {

/*TweenLite.to 改变c实例的x坐标,y坐标,使用easeInOut弹性函数,透明度提到1,改变大小,radius,总用时0.4s*/

c.tween = TweenLite.to(c, 0.4, {x: c.originX, y: c.originY, ease:Quad.easeInOut, alpha: 1, radius: 5, scaleX: 0.4, scaleY: 0.4, onComplete: function() {

c.movement = 'jiggle';/*轻摇*/

tweenCircle(c);

}});

} else if(dir == 'out') {

c.tween = TweenLite.to(c, 0.8, {x: window.innerWidth*Math.random(), y: window.innerHeight*Math.random(), ease:Quad.easeInOut, alpha: 0.2 + Math.random()*0.5, scaleX: 1, scaleY: 1, onComplete: function() {

c.movement = 'float';

tweenCircle(c);

}});

} else {

if(c.movement == 'float') {

c.tween = TweenLite.to(c, 5 + Math.random()*3.5, {x: c.x + -100+Math.random()*200, y: c.y + -100+Math.random()*200, ease:Quad.easeInOut, alpha: 0.2 + Math.random()*0.5,

onComplete: function() {

tweenCircle(c);

}});

} else {

c.tween = TweenLite.to(c, 0.05, {x: c.originX + Math.random()*3, y: c.originY + Math.random()*3, ease:Quad.easeInOut,

onComplete: function() {

tweenCircle(c);

}});

}

}

}

TweenLite.to函数第一个参数,要做动画的实例,第二个参数,事件,第三个参数,动画改变参数。

Quad.easeInOut()意思是在动画开始和结束时缓动。onComplete动画完成时调用的函数。易得,在我们的应用中,我们将开始下一次动画。

个人感言

其实刚开始没想做这么复杂,所以文件排的比较随意,然后就导致了后期项目完成时那副杂乱无章的样子。^_^,以后改。等我等看懂动画效果时在说,现在用的有点半懵半懂。

这篇博客写得有点乱。新手之作,就先这样吧。同上,以后改。因为不知道这个项目会不会拿来直接当我们计算机职业实践的作业。要是的话,我就彻改,连同博客。

以下是源代码地址。

还在审核。明天再说。相信我,我在小番茄里做了记录。

总结

以上所述是小编给大家介绍的Html5写一个简单的俄罗斯方块小游戏,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对脚本之家网站的支持!如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。