index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Russian Block</title>
<link rel="stylesheet" href="./css/index.css">
</head>
<body>
<!--游戏活动区域-->
<article class="container" id="container">
<!--开始游戏按钮-->
<button id="start">开始游戏</button>
</article>
<script src="./js/index.js"></script>
<script src="./js/jquery-3.5.1.min.js"></script>
</body>
</html>
index.css
/* 初始化页面样式 */
* {
margin: 0;
padding: 0;
list-style: none;
}
/* 设置活动区域container样式 */
.container {
width: 540px;
height: 600px;
background-color: black;
margin: 30px auto 0;
position: relative;
}
/*设置游戏活动区域创建块元素 actvity_model 样式*/
.actvity_model{
background-color: cadetblue;
border: 1px solid #eee;
box-sizing: border-box;
position: absolute;
}
/*按钮样式*/
#start{
top: -27px;
right: 1px;
position: absolute;
color: #fff;
width: 100px;
border: 1px solid cadetblue;
background-color: cadetblue;
height: 26px;
text-align: center;
}
/*滑到底部固定游戏快*/
.fixed_model {
background-color: #FFF;
border: 1px solid #333;
box-sizing: border-box;
position: absolute;
}
/* 提示框 */
#model{
margin: auto;
width: 200px;
border: 3px solid cadetblue;
padding: 30px 10px;
position: fixed;
margin-left: -122px;
margin-top: -262px;
text-align: center;
left: 50%;
top: 50%;
background: #fff;
display: none;
}
index.js
function CreateRandomModel(){
// 创建所有可能的方块的数组
var MODELS = [
{//第一个 L 字型
'name': 'L 字型',
'model': [[0,0],[1,0],[2,0],[2,1]]
},
{//第二个 J 字型
'name': 'J 字型',
'model': [[0,1],[1,1],[2,0],[2,1]]
},
{//第三个凸字型
'name': '凸字型',
'model': [[1,1],[0,0],[1,0],[2,0]]
},
{//第四个田字型
'name': '田字型',
'model': [[0,0],[0,1],[1,0],[1,1]]
},
{//第五个一字型
'name': '一字型',
'model': [[0,0],[0,1],[0,2],[0,3]]
},
{//第六个 Z 字型
'name': 'Z 字型',
'model': [[0,0],[0,1],[1,1],[1,2]]
}
];
// 随机方块模型下标
var ramdomNum = Math.floor(Math.random()*MODELS.length);
//方块模型名称
this.name = MODELS[ramdomNum]['name'];
//方块模型
this.blockModel = MODELS[ramdomNum]['model'];
}
CreateRandomModel.prototype.getBlockModle = function(){
return this.blockModel;
}
CreateRandomModel.prototype.getName = function(){
return this.name;
}
// 显示屏的高度
var screenWidth = window.screen.width;
var screenHeight = window.screen.height;
console.log(screenWidth);
console.log(screenHeight);
// 根据屏幕分辨率设置步长
var STEP;
if(screenWidth >= 1200){
STEP = 30;
}else if(screenWidth >= 992){
STEP = 20;
}else if(screenWidth >= 768){
STEP = 10;
}else{
STEP = 5;
}
// 根据步长得到方块的宽高
var boxWidth = STEP;
var boxHeight = STEP;
// 根据游戏界面方块的行列数得到游戏界面的宽高
var gameHeight = STEP*20;
var gameWidth = STEP*18;
console.log(gameWidth);
console.log(gameHeight);
console.log(boxWidth);
console.log(boxHeight);
// 分割容器为多少行多少列(即多少个单个方块的距离)
var W_COUNT=gameWidth/boxWidth;
var H_COUNT=gameHeight/boxHeight;
//定义变量设置当前使用的模型
var currentModel = [];
//标记初始位置
var currentX = 0;
var currentY = 0;
//根据模型的数据创建对应的块元素
function createModel() {
//判断游戏是否结束
if(isGameOver()){
// console.log("游戏结束!")
gameOver();
return;
}
//游戏界面宽高样式设置
document.getElementById('container').style.width = gameWidth + 'px';
document.getElementById('container').style.height = gameHeight + 'px';
//调用构造函数,实例化一个随机方块对象
var objectBlocks = new CreateRandomModel();
//获取随机方块对象的模型
currentModel = objectBlocks.getBlockModle();
//重新初始化 16 宫格位置
currentX = 0;
currentY = 0;
for (var key in currentModel) {
//创建 div 子节点
var divEle = document.createElement('div');
//给 div 子节点设置 class 属性值
divEle.className = 'actvity_model';
//追加方块到游戏界面中
document.getElementById('container').appendChild(divEle);
//方块模型中单个方块宽高样式设置
document.getElementsByClassName('actvity_model')[key].style.width = boxWidth + 'px';
document.getElementsByClassName('actvity_model')[key].style.height = boxHeight + 'px';
}
}
//根据数据源定义移动块元素的位置
function locationBlocks(){
//方块移动时需要判断活动边界,在函数顶部调用
checkBound();
//获取所有的块元素
var eles = document.getElementsByClassName('actvity_model');
for (var i=0; i<eles.length; i++){
//单个块元素
var actityModelEle = eles[i];
//找到每个块元素对应的数据(行,列)
var blockModel = currentModel[i];
//根据每个块元素对应的数据来指定块元素的位置
//每个块元素的位置由两个值来决定 (16 宫格所在的位置/块元素在 16 宫格的位置)(每次移动的距离,为单个方块的宽高度)
actityModelEle.style.top = (currentY + blockModel[0]) * boxWidth + 'px';
actityModelEle.style.left = (currentX + blockModel[1]) * boxHeight + "px";
}
}
//点击页面中“开始游戏”按钮,触发点击事件后游戏开始
document.getElementById('start').onclick=function(){
//清除已有模态框元素
$("#modal").remove();
//获取根据模型的数据创建对应的块元素
createModel();
//根据数据源定义移动块元素的位置
locationBlocks();
//监听用户的键盘事件()
onKeyDown();
}
//创建 onKeyDown()函数监听用户的键盘事件
function onKeyDown(){
document.onkeydown = function(event){
console.log(event.keyCode)
}
}
//监听用户的键盘事件
function onKeyDown() {
//获取按下的键盘按键 Unicode 值
document.onkeydown = function (event) {
switch (event.keyCode) {
case 38: //(上方向键)
console.log("上键")
rotate();
break;
case 39: //(右方向键)
console.log("右键")
move(1,0);
break;
case 40: //(下方向建)
console.log("下键")
move(0,1);
break;
case 37: //(左方向建)
console.log("左键")
move(-1,0);
break;
}
}
}
//移动
function move(x, y) {
if (isMent(currentX + x, currentY + y, currentModel)) {
if (y !== 0) {
// console.log("发生了触碰");
//模型之间底部发生了触碰
fixedBottonModel();
}
}
//16 宫格移动
currentX += x;
currentY += y;
//根据 16 宫格的位置重新定位块元素
locationBlocks();
}
// 旋转模型
function rotate() {
var cloneModels = deepClone(currentModel);
//测试代码
console.log(cloneModels);
//遍历模型数据源
for (var key in currentModel) {
//块元素数据源
var blockModel = currentModel[key];
var temp = blockModel[0]
//旋转前的行=旋转后的列
blockModel[0] = blockModel[1];
//旋转后的行=3-旋转前的行
blockModel[1] = 3 - temp;
}
if(isMent(currentX, currentY, cloneModels)) {
return;
}
//重新定位块元素
locationBlocks();
}
function checkBound() {
//定义模型可以活动边界
var leftBound = 0;
var rightBound = W_COUNT;
var boottonBound = H_COUNT;
//当块元素超出了边界后,让 16 宫格后退
for (var key in currentModel) {
var blockModel = currentModel[key];
//左侧越界
if ((blockModel[1] + currentX) < leftBound ) {
currentX++;
}
//右侧越界
if ((blockModel[1] + currentX) >= rightBound) {
currentX--;
}
// 底部越界
if ((blockModel[0] + currentY) >= boottonBound) {
currentY--;
//把模型固定在底部
fixedBottonModel();
}
}
}
// 记录所有块元素的位置
var fixedBlocks = [];
function reStore(){
//遍历行
for(var i=0;i<=H_COUNT;i++){
fixedBlocks[i] = [];
//遍历列
for(var j=0;j<=W_COUNT;j++){
fixedBlocks[i][j] = null;
}
}
}
function fixedBottonModel() {
var activityModelEles = $(".actvity_model");
//遍历获取的类元素
for(var i = activityModelEles.length - 1; i >= 0; i--) {
//获取当前的模型
var activityModelEle = activityModelEles[i];
//更改块元素的类名
$(activityModelEle).attr("class","fixed_model");
var blockModel = currentModel[i];
//把该元素放入变量中
fixedBlocks[currentY + blockModel[0]][currentX + blockModel[1]] =activityModelEle;
}
//判断是否清理
isRemoveLine();
//创建新的模型
createModel();
}
//判断是否碰撞
function isMent(x, y, model) {
for (var k in model) {
var blockModel = model[k];
//先判断一维数组是否存在元素
if(!fixedBlocks[y + blockModel[0]]) {
return true;
}
//再判断二维数组里面是否存在元素
if (fixedBlocks[y +blockModel[0]][x + blockModel[1]]) {
return true;
}
}
return false;
}
function deepClone(obj) {
var _obj = JSON.stringify(obj),
cloneObj = JSON.parse(_obj);
return cloneObj;
}
function isRemoveLine() {
// 遍历所有行中的列
for (var i = 0; i < H_COUNT; i++) {
// 标记符,假设当前行被铺满
var flag = true;
// 遍历当前行中的所有列
for (var j = 0; j < W_COUNT; j++) {
// 如果当前行中有一列没有数据那就说明没有被铺满
if (!fixedBlocks[i] || !fixedBlocks[i][j]) {
flag = false;
break;
}
}
if (flag) {
// 该行已经被铺满
// console.log("该行已经被铺满");
// 清理被铺满的该行
removeLine(i);
}
}
// 判断 flag 值是否为 true
if (flag) {
// 该行已经被铺满
console.log("该行已经被铺满");
}
}
function removeLine(line) {
//遍历该行中的所有行
for (var i = 0; i < W_COUNT; i++) {
//删除该行中的所有块元素
if(!fixedBlocks[line]) fixedBlocks[line] = [];
$(fixedBlocks[line][i]).remove();
//删除该行中的所有块元素的数据源
fixedBlocks[line][i] = null;
}
downLine(line);
}
function downLine(line) {
//遍历被清理行之上的所有行
for (var i = line - 1; i >= 0; i--) {
//该行中所有列
for (var j = 0; j < H_COUNT; j++) {
if(!fixedBlocks[i]) fixedBlocks[i] = [];
//假如不存在
if (!fixedBlocks[i][j]) continue;
//存在数据
//1.被清理行之上的所有块元素数据源所在的行数+1
if(!fixedBlocks[i+1]) fixedBlocks[i+1] = [];
fixedBlocks[i+1][j] = fixedBlocks[i][j];
//2.让块元素在容器中的位置下落
fixedBlocks[i+1][j].style.top = (i + 1) * STEP + "px";
//3.清理掉之前的块元素
fixedBlocks[i][j] = null;
}
}
}
function isGameOver(){
//当第0行存在元素的时候,表示游戏结束
for (var i =0;i<W_COUNT;i++){
if(fixedBlocks[0][i]||fixedBlocks[1][0]||fixedBlocks[1][1]||fixedBlocks[1][2]||fixedBlocks[1][3]){
return true;
}
}
return false;
}
function gameOver(){
if ($("#model").length <= 0){ //校验页面是否存在提示框元素
var model = $('#container').append("<div id='model'<p>本轮游戏结束!</p></div>");
}
//淡入效果显示提示框
$('#model').fadeIn(100);
$('.fixed_model').remove();
}
//点击游戏界面,触发点击事件后游戏开始
document.getElementById("start").onclick = function () {
reStore();
//获取根据模型的数据创建对应的块元素
createModel();
//根据数据源定义移动块元素的位置
locationBlocks();
//监听用户的键盘事件()
onKeyDown();
}
关于jQury的相关功能
第一步:打开 index.html 文件,在页面引入 jquery 文件。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Russian Block</title>
<link rel="stylesheet" href="./css/index.css">
</head>
<body>
<!--游戏活动区域-->
<article class="container" id="container">
<!--开始游戏按钮-->
<button id="start">开始游戏</button>
</article>
<script src="./js/index.js"></script>
<script src="./js/jquery-3.5.1.min.js"></script>
</body>
</html>
第二步:在 index.js 文件中,创建 fixedBottonModel() 函数,固定模型。
-
使用 jQuery 的 $(“.actvity_model”) 获取页面中所有类名为 “actvity_model” 的元素,并保存在 activityModelEles 变量中。
-
在 fixedBottonModel 函数中遍历获取的类元素,获取当前遍历元素,使用 jQuery 操 作 DOM 属性,更改元素属性值。
-
获取当前方块坐标(currentX 和 currentY),根据方块模型 currentModel 中每个方块的相对坐标,计算出它们在 fixedBlocks 数组中的绝对坐标,并将对应的 activityModelEle 元素保存在 fixedBlocks 数组中,以便于后续处理。
-
调用 isRemoveLine() 函数判断是否需要清除行。
-
最后调用 createModel() 函数创建新的模型。
第三步:在 index.js 文件中,创建 isMent() 函数,判断模型之间是否有触碰。
- 函数名为 isMent,接收三个参数:x、y 和 model。这三个参数表示方块的当前位置和方块模型。
- 使用 for…in 循环遍历方块模型 model。在循环中,将当前方块的相对位置保存在变量 blockModel 中。
- 首先,判断一维数组 fixedBlocks 中的第 y + blockModel[0] 个元素是否存在,即判断方块下方是否有其他方块。如果不存在,说明方块碰撞,返回 true。其次,判断二维数组 fixedBlocks 中的第 y + blockModel[0] 行、第 x + blockModel[1] 列的元素是否存在,即判断方块的当前位置是否有其他方块。如果存在,说明方块碰撞,返回 true。
- 如果以上两个判断都没有返回 true,说明方块未发生碰撞,继续遍历下一个方块。如果遍历完所有方块后仍然没有返回 true,说明方块与其他方块没有发生碰撞,返回 false。
在 move() 函数顶部调用 isMent() ,如果当前存在模型之间发生触碰,并且模型 坐标在底部,使用 console.log 测试是否成功。
修改调试代码,调用 fixedBottonModel() 固定模型,终止函数执行
第四步:在 index.js 文件中,定义 deepClone(obj)函数,编辑 rotate()函数,设置方块 旋转后触碰。
- 函数名为 deepClone,接收一个参数 obj,表示待拷贝的对象。
- 在函数内部,将待拷贝的对象 obj 使用 JSON.stringify 方法转换为字符串形式,并将结果保存在 _obj 变量中。
- 然后,使用 JSON.parse 方法将 _obj 字符串解析成一个新的对象,并将其保存在 cloneObj 变量中。
- 最后,返回 cloneObj,即完成了深拷贝操作。
在 rotate() 函数中实例化 deepClone()
在 rotate() 函数中使用深拷贝过来的数据源 cloneModels 替换原有的数据 currentModel 。调用 isMent() 函数设置参数为当前坐标 currentX , currentY ,克隆 数据源 cloneModels 。
第五步:在 index.js 文件中,创建 isRemoveLine() 函数,判断一行是否被铺满。
-
使用循环遍历游戏区域的行。这里使用的是变量
i
,它从0开始逐渐增加,直到达到行数的上限H_COUNT
。在每一次循环中,代码会设置一个标记变量flag
,将其初始值设为true
。这个标记变量用于记录当前行是否被填满。 -
在嵌套的循环中,代码遍历当前行的所有列。同样,这里使用的是变量
j
,它从0开始逐渐增加,直到达到列数的上限W_COUNT
。在每一次列的循环中,代码会检查当前行中的每一列是否有方块。这里通过判断fixedBlocks[i][j]
是否存在来确定当前列是否有方块。如果当前列没有方块,则说明当前行没有被填满,此时将flag
设置为false
。 -
如果在列的循环中发现当前行没有被填满(即
flag
为false
),则会跳出内部循环,并继续处理下一行的判断。如果在列的循环结束后,标记变量flag
仍然为true
,则说明当前行被填满。这时,代码调用函数removeLine(i)
来移除该行并执行相应的操作。函数removeLine
的实现在代码中并未给出。 -
循环继续,处理下一行的判断和操作。循环结束后,代码会再次判断标记变量
flag
的值。如果flag
仍然为true
,则输出 “该行已经被铺满” 的消息。
在 fixedBottonModel()方法中调用 isRemoveLine()方法
第六步:在 index.js 文件中,创建 removeLine() 函数,清理被铺满的一行。
定义removeLine()函数,获取行号“i”,遍历该行中的所有列数,它从0开始逐渐增加,直到达到列数的上限 W_COUNT
。
在每次循环中,代码首先检查被删除行 line
是否存在于 fixedBlocks
数组中。
然后,代码使用 $(fixedBlocks[line][i]).remove()
将该行中的方块元素从DOM中删除。这里使用了 $
函数,可能是为了使用jQuery库的选择器来删除对应的DOM元素。
同时,代码将被删除行 line
中的方块元素在 fixedBlocks
数组中的对应位置设置为 null
,从而清除了方块元素的数据源。
第七步: 在 index.js 文件中,创建 downLine() 函数,让被清理行之上的块元素下 落。
遍历被清理行之上的所有行,遍历该行所有列,判断当前是否存在数据,如果存在数据。
使用遍历序号访问二维数组 fixedBlocks 中元 素,判断当前元素是否存在,如果不存在在被清理行之上的所有块元素数据源所在的行数 line+1。将方块元素的数据源复制到新的位置。将方块元素在容器中的位置向下移动一个单位,清空原来位置的方块元素的数据源,即 fixedBlocks[i][j] = null
。
设置该元素中的 css 样式,让块元素在容器中的位置下落
第八步:在 index.js 文件中,创建判断游戏结束的函数 isGameOver()。
(1)创建 isGameOver()函数,使用 for 循环遍历列数组,判断二维数组 fixedBlocks 第 0 行是否存在块元素,如果 存在表示游戏结束,返回 true。如果不存在元素,则返回 false 表示游戏还未结束。
(2)在生成模型 createModel 前判断游戏是否结束(isGameOver()返回 true),如果游戏结束,终止函数,
第九步:游戏结束,创建 gameOver()函数,生成游戏结束提示模态框
在 index.css 文件中设置 id 为#modal 模态框的样式,设置为垂直居中浮动在页面 中间,默认为隐藏状态。
在 index.js 文件中,创建 gameOver()函数。校验页面是否存在模态框元素,如果 不存在使用 jQuery Dom 操作在页面游戏区添加模态框。使用 jQuery 动画显示隐藏的提示框 元素,并删除游戏区中方块元素。
当 isGameOver()返回 true 表示游戏结束,调用 gameOver()函数,并终止创建模块 函数 createModel 运行。
在 index.js 文件中,在开始游戏点击事件时使用 jQuery Dom 操作清除掉页面中的 模态框元素。
运行此代码
开始游戏: