游戏过程:点击开始游戏,游戏界面中有一个活动方块,准备界面有一个准备方块,都是随机生成的。所有方块的形状固定,有7种,随机生成。活动方块每次移动后都将原来位置的方块清除,记录下当前活动方块的位置再渲染颜色。活动方框每次移动都会出发边界检查。一旦碰触边界时,移动判断为合法就禁止移动了。碰触底部边界时,则落定方块,记录下当前方块的位置,在游戏界面的相同位置标记表示已经被覆盖。将当前活动方块设置为准备方块,并在游戏界面初始位置显示,准备界面清空,设置新的准备方块。当下一次移动会到游戏界面已经被覆盖的方块(值为1表示被覆盖,0为空白),为非法移动。消行就是判断游戏界面若有一行方块值全部为1,就清空该行方块。然后将该行之上的所有行逐一位置下移,每下移一行,清空上一行。游戏分数随着每个方块最后一次下移时消行的个数加倍增加。游戏状态有4种游戏,分别是未开始(0),已开始游戏(1),暂停游戏(2),游戏结束(2)。开始游戏后状态改变,定时器开始,暂停游戏后状态改变,定时器停止;继续游戏恢复开始游戏状态,定时器启动;游戏结束,状态改变,定时器为空,并恢复到位开始游戏状态,初始化游戏界面。
俄罗斯方块主要部分:
1、判断边界:判断方块移动时的横纵坐标是否大于等于游戏界面的大小,超出范围就不允许继续向该方向移动。
判断下一次移动的位置是否有方块覆盖,即游戏界面方块值为1。游戏界面所有方块初始值为0,表示空白。当每次落定一个方块,记录位置并赋值1,表示被覆盖。
2、旋转算法: 旋转以逆时针方向旋转90度,在数学上,俄罗斯方块是按照方块的中心点进行旋转。(x,y)是每个小方块的坐标,(x0,y0)整个方块的中心点坐标。中心点坐标是每个小方块的平均横纵坐标。方块旋转后的坐标是(x0 +y0 -y,y0-x0+x)。
3、新的活动方块和准备方块:在判断碰触底部边界后,停止定时器并更新新的活动方块和准备方块,再在页面上渲染,这就再初始位置上有新的方块了,再随其进行定时器绑定就行了
我使用的是谷歌浏览器,其他的可能有问题。关于音乐没有的看看这个,https://www.cnblogs.com/wsg25/p/9640316.html
代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
div {
display: inline-block;
}
.choose {
display: inline-block;
position: absolute;
left: 30%;
}
.game {
position: absolute;
width: 222px;
height: 354px;
left: 40%;
display: inline-block;
}
.nextSquere {
position: absolute;
right: 33.7%;
}
table {
border-color: #eee;
}
td {
width: 20px;
height: 20px;
background-color: bisque;
}
#again {
display: none;
}
</style>
</head>
<body>
<audio id="sounds" loop= "loop" volume = "60">
<source src="music.wav" id="A" type="audio/wav">
</audio>
<!-- 分数和游戏控制 -->
<div class="choose">
score:<span class="score">0</span>
<br>
<br>
<button id="begin">开始游戏</button>
<br>
<br>
<button id="again">重新开始</button>
</div>
<!-- 游戏界面 -->
<div class="game">
<table class="game_content" border="1" cellpadding = 0 cellspacing = 0>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</table>
</div>
<!-- 准备界面 -->
<div class="nextSquere">
<table class="next_content" border="1" cellpadding = 0 cellspacing = 0>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</table>
</div>
<div>
<button id="music">音乐</button>
</div>
<br>
<script>
//游戏界面
var game = document.getElementsByClassName("game_content")[0];
//准备方块界面
var nextSquere = document.getElementsByClassName("next_content")[0];
//分数
var score = document.getElementsByClassName("score")[0];
//定时器,方块的下移动作moveDown()
var timer = null;
//开始游戏按钮会分为三种状态,未开始游戏(0),已经开始游戏(1),暂停游戏(2),结束游戏为(3)
var status = 0;
//重新开始按钮
//again
//当前正在活动的方块
var activeBlock;
//游戏界面的所有方块
var table = new Array(16);
//游戏界面中已经被方快覆盖的位置,空白为0,覆盖为1
for(var i = 0; i < table.length; i++){
table[i] = new Array(10);
}
for(var i = 0; i < table.length; i++){
for(var j = 0; j < table[i].length; j++){
table[i][j] = 0; //游戏界面方块状态,初始值为0;
}
}
//下一个活动的方块
var nextBlock;
//方块的形状,方块最宽为4,最高为4
function shapes(){
var block = new Array(4);
//随机生成形状,一共有七种形状,0~6
var r = Math.ceil(Math.random()*7) - 1;
switch(r){
case 0:
block[0] = {x:0, y:0};
block[1] = {x:0, y:1};
block[2] = {x:0, y:2};
block[3] = {x:0, y:3};
break;
case 1:
block[0] = {x:0, y:1};
block[1] = {x:1, y:0};
block[2] = {x:1, y:1};
block[3] = {x:1, y:2};
break;
case 2:
block[0] = {x:0, y:0};
block[1] = {x:1, y:0};
block[2] = {x:1, y:1};
block[3] = {x:1, y:2};
break;
case 3:
block[0] = {x:0, y:2};
block[1] = {x:1, y:0};
block[2] = {x:1, y:1};
block[3] = {x:1, y:2};
break;
case 4:
block[0] = {x:0, y:0};
block[1] = {x:0, y:1};
block[2] = {x:1, y:0};
block[3] = {x:1, y:1};
break;
case 5:
block[0] = {x:0, y:1};
block[1] = {x:0, y:2};
block[2] = {x:1, y:0};
block[3] = {x:1, y:1};
break;
case 6:
block[0] = {x:0, y:0};
block[1] = {x:0, y:1};
block[2] = {x:1, y:1};
block[3] = {x:1, y:2};
break;
}
return block;
}
//渲染当前活动方块的位置
function activeLoaction(){
for(var i = 0; i < activeBlock.length; i++){
game.rows[activeBlock[i].x].cells[activeBlock[i].y].style.backgroundColor="yellow";
}
}
//渲染准备界面方块的位置
function nextShape(){
for(var i = 0; i < nextBlock.length; i++){
nextSquere.rows[nextBlock[i].x].cells[nextBlock[i].y].style.backgroundColor="yellow";
}
}
//检查左边界
function checkLeft(){
for(var i = 0; i < activeBlock.length; i++){
if(activeBlock[i].y === 0){
return false;
}
if(!checkGameBlock(activeBlock[i].x,activeBlock[i].y-1)){
return false;
}
}
return true;
}
//检查右边界
function checkRight(){
for(var i = 0; i < activeBlock.length; i++){
if(activeBlock[i].y === 9){
return false;
}
if(!checkGameBlock(activeBlock[i].x,activeBlock[i].y+1)){
return false;
}
}
return true;
}
//检查下边界
function checkBottom(){
for(var i = 0; i < activeBlock.length; i++){
if(activeBlock[i].x === 15){
return false;
}
if(!checkGameBlock(activeBlock[i].x+1,activeBlock[i].y)){
return false;
}
}
return true;
}
//移动时,检查游戏界面中的方块是否被覆盖
function checkGameBlock(x,y){
if(table[x][y] == 1){
// console.log("a");
return false;
}
if(x>15||x<0||y<0||y>9){
return false;
}
return true;
}
//判断最初位置能否放方块
function initLocation(){
for(var i = 0; i < 4; i++){
if(table[nextBlock[i].x][nextBlock[i].y] == 1){
gameOver();
}
}
}
//向左移动
function moveLeft(){
if(checkLeft()){
clearActive();
//移动方块位置
for(var i = 0 ; i < activeBlock.length; i++){
activeBlock[i].y -= 1;
}
activeLoaction();
}
}
//向右移动
function moveRight(){
if(checkRight()){
clearActive();
//移动方块位置
for(var i = 0 ; i < activeBlock.length; i++){
activeBlock[i].y += 1;
}
activeLoaction();
}
}
//向下移动
function moveDown(){
//console.log(checkBottom());
//没碰到底部边界,继续运动
if(checkBottom()){
clearActive();
//移动方块位置
for(var i = 0 ; i < activeBlock.length; i++){
activeBlock[i].x += 1;
}
activeLoaction();
}
//碰到底部边界就停止所有可能的动作
else {
//停止向下移动的定时器
clearInterval(timer);
//记住当前游戏界面的方块已落下的方块位置
remember(activeBlock);
//判断最初位置能否放方块
initLocation();
if(status == 1){
//消行
var rows = clearRow();
//更新分数
if(rows != 0){
//一次消多行则分数加倍
switch(rows) {
case 1:
score.innerHTML = parseInt(score.innerHTML) + rows*10;
break;
case 2:
score.innerHTML = parseInt(score.innerHTML) + rows*10*2;
break;
case 3:
score.innerHTML = parseInt(score.innerHTML) + rows*10*3;
break;
case 4:
score.innerHTML = parseInt(score.innerHTML) + rows*10*4;
break;
}
}
//更新当前活动方块为准备方块
activeBlock = nextBlock;
activeLoaction();
//消除准备界面方块
clearNext();
//更新准备方块
nextBlock = shapes();
//更新准备方块界面
nextShape();
//定时器, 每隔一秒执行一次moveDown
timer = setInterval(moveDown,1000)
}else if(status == 2){
// stopGame();
}else if(status == 3){
return;
}
}
}
//旋转方块
function rotate(){
var rotateBlock = copyBlock(activeBlock);
var centerX = 0;
var centerY = 0;
//算法思想:坐标(x,y)绕中心点(x0,y0)逆时针旋转90度后的坐标为(x0+y0-y,y0-x0+x)
for(var i = 0; i < rotateBlock.length; i++){
centerX += rotateBlock[i].x;
centerY += rotateBlock[i].y;
//求中心点坐标
if(i == 3){
centerX = Math.round(centerX / 4);
centerY = Math.round(centerY / 4);
}
}
//旋转后坐标,这里注意,是当前活动方块的中心点,不是拷贝的旋转方块的中心点
for(var i = 0; i < 4; i++){
rotateBlock[i].x = centerX + centerY - activeBlock[i].y;
rotateBlock[i].y = centerY - centerX + activeBlock[i].x;
}
//先判断是否合法
for(var i = 0; i < 4; i++){
if(!checkGameBlock(rotateBlock[i].x,rotateBlock[i].y)){
return;
}
}
//合法就清除变换前方块
clearActive();
//更新当前活动方块形状
for(var i=0; i<4; i++){
activeBlock[i].x = rotateBlock[i].x;
activeBlock[i].y = rotateBlock[i].y;
}
activeLoaction();
}
//记住游戏界面每次确定落完的方块位置,不能被覆盖和碰触
function remember(fallBlock){
// console.log(fallBlock);
for(var k = 0; k < 4; k++){
table[fallBlock[k].x][fallBlock[k].y] = 1;
}
// for(var i = 0; i < table.length; i++){
// for(var j = 0; j < table[i].length; j++){
// console.log(table[i][j]); //检查游戏界面方块状态,初始值为0;
// }
// }
}
//初始化界面
function init(){
clearInterval(timer);
score.innerHTML = 0;
begin.innerHTML = '开始游戏';
clearGame();
clearNext();
for(var i = 0; i < table.length; i++){
for(var j = 0; j < table[i].length; j++){
table[i][j] = 0;
}
}
timer = null;
status = 0;
}
//消除游戏界面每次移动前的方块
function clearActive(){
for(var i=0; i<4; i++){
game.rows[activeBlock[i].x].cells[activeBlock[i].y].style.backgroundColor="bisque";
}
}
//消除准备方块界面的方块
function clearNext(){
for(var i = 0; i < 4; i++){
nextSquere.rows[nextBlock[i].x].cells[nextBlock[i].y].style.backgroundColor="bisque";
}
}
//消除游戏界面所有方块
function clearGame(){
for(var i = 0; i< table.length; i++){
for(var j = 0; j < table[i].length; j++){
game.rows[i].cells[j].style.backgroundColor = "bisque";
}
}
}
//消行
function clearRow(){
var row = 0;
for(var i = 0; i < table.length; i++){
var flag = true;
for(var j = 0; j < table[i].length; j++){
if(table[i][j] == 0){
flag = false;
break;
}
}
if(flag){
row++;
for(var k = table[i].length - 1; k >= 0; k--){
table[i][k] = 0;
game.rows[i].cells[k].style.backgroundColor = "bisque";
}
//更新消行后的游戏界面方块位置
for(var q = i; q >= 1; q--){
for(var p = 0; p < table[q].length; p++){
table[q][p] = table[q - 1][p];
table[q-1][p] = 0;
game.rows[q-1].cells[p].style.backgroundColor = "bisque";
}
}
}
}
//更新后给方块加颜色
for(var i = 0; i < table.length; i++){
for(var j = 0; j < table[i].length; j++){
if(table[i][j] == 1){
game.rows[i].cells[j].style.backgroundColor = "yellow";
}
}
}
// console.log(row);
return row;
}
//更新分数
function updateScore(i){
score = 10*i + parseInt(score.innerHTML);
score.innerHTML = score;
}
//键盘上下左右代表旋转,向下,向左,向右移动
document.onkeydown = function(){
if(status == 1) {
if(event.keyCode == 37){
moveLeft();
}else if(event.keyCode == 38){
rotate();
}else if(event.keyCode == 39){
moveRight();
}else if(event.keyCode == 40){
moveDown();
}
}
}
//复制一个活动方块
function copyBlock(b) {
var o = new Array(4);
for(var i = 0; i < o.length; i++){
o[i] = {x:0, y:0};
}
for(var i=0; i<4; i++){
o[i].x = b[i].x;
o[i].y = b[i].y;
}
return o;
}
//点击游戏开始,开始游戏按钮会分为三种状态,开始游戏,暂停游戏,继续游戏
begin.onclick = function(){
again.style.display = 'inline';
if(begin.innerHTML == '开始游戏') {
start();
begin.innerHTML = '暂停游戏';
}else if(begin.innerHTML == '暂停游戏'){
stopGame();
begin.innerHTML ='继续游戏';
}else if(begin.innerHTML == '继续游戏'){
continueGame();
begin.innerHTML = '暂停游戏';
}
}
//重新开始游戏事件
again.onclick = function(){
sounds.load();
init();
start();
begin.innerHTML = '暂停游戏';
}
//开始游戏
function start(){
sounds.play();
status = 1;
activeBlock = shapes();
activeLoaction();
nextBlock = shapes();
nextShape();
timer = setInterval(moveDown,1000);
}
//暂停游戏
function stopGame(){
status = 2;
sounds.pause();
clearInterval(timer);
}
//继续游戏
function continueGame(){
status = 1;
sounds.play();
timer = setInterval(moveDown,1000);
}
//游戏结束
function gameOver(){
sounds.pause();
status = 3;
alert("Game Over,你的分数是"+score.innerHTML+"分,请继续努力");
init();
}
</script>
</body>
</html>