整体思路:
1. 基础设置:游戏的背景是一个4*4的格子背景, 游戏中分别有2、4、8、16等不同的色块,每种色块的颜色由js脚本来控制。
2. 游戏开始: 游戏开始的时候,进行初始化操作。在4*4的格子上面,随机生成2个色块,色块的里面的数字是2或4。这里应当注意的是格子和色块不是同一个元素,即整个页面中有16个格子元素和若干个色块元素。这里要注意一下格子元素和色块元素的CSS样式布局。
3. 游戏控制:通过键盘事件来对色块进行控制。每按下一次键盘上面的"上下左右"键,页面中的色块向对应的方向移动,这里通过调用。如果色块发生移动,则在页面内再随机生成一个色块,色块的内容是2或4。
4.检测游戏是否结束:每当色块移动之后,就进行一次检测,检测游戏是否已经结束。游戏结束判断的依据是:当前没有空白格子,即每一个格子上面都有一个色块;任何一个色块,其紧邻的色块没有与之相同的。
5.游戏结束:游戏结束,进行一些细节方面的处理。比如我这里会显示游戏结束四个字,会让游戏的时间停下来等,当然,这是游戏的收尾工作,该怎么来写,根据个人的想法来就行了。
6.源码获取方式在文末
具体代码:
-
HTML代码:
<!-- 游戏的得分和持续时间 -->
<div id="game_show">
<div id="score">
<b>得分:</b>
<span id="score_val">0</span>
</div>
<div id="time">
<b>时间:</b>
<span id="time_val">0</span>
</div>
</div>
<div id="box">
<!-- 4*4格子 -->
<div class="grid-cell" id="grid-cell-0-0"></div>
<div class="grid-cell" id="grid-cell-0-1"></div>
<div class="grid-cell" id="grid-cell-0-2"></div>
<div class="grid-cell" id="grid-cell-0-3"></div>
<div class="grid-cell" id="grid-cell-1-0"></div>
<div class="grid-cell" id="grid-cell-1-1"></div>
<div class="grid-cell" id="grid-cell-1-2"></div>
<div class="grid-cell" id="grid-cell-1-3"></div>
<div class="grid-cell" id="grid-cell-2-0"></div>
<div class="grid-cell" id="grid-cell-2-1"></div>
<div class="grid-cell" id="grid-cell-2-2"></div>
<div class="grid-cell" id="grid-cell-2-3"></div>
<div class="grid-cell" id="grid-cell-3-0"></div>
<div class="grid-cell" id="grid-cell-3-1"></div>
<div class="grid-cell" id="grid-cell-3-2"></div>
<div class="grid-cell" id="grid-cell-3-3"></div>
<!-- js中要向页面中插入的色块元素 -->
<!--<div class="number-cell" id="number-cell-0-0">2</div>-->
<!-- 游戏结束的时候要显示的字体和后面的半透明遮罩 -->
<div id="cover">
<div class="text_box">游</div>
<div class="text_box">戏</div>
<div class="text_box">结</div>
<div class="text_box">束</div>
</div>
<div id="cover_bg"></div>
</div>
-
CSS代码:
*{
margin: 0;
padding: 0;
}
#game_show{
width: 500px;
height: 100px;
background-color: #b8aea4;
margin: 10px auto;
text-align: center;
padding: 20px 0px;
box-sizing: border-box;
}
#game_show div{
width: 200px;
height: 56px;
background-color: rgb(121, 111, 101);
border-radius: 5px;
display: inline-block;
margin: 0px 20px;
line-height: 56px;
box-sizing: border-box;
}
#game_show div span{
color: white;
margin: 0px 10px;
}
#box{
width: 500px;
height: 500px;
background-color: #b8aea4;
margin: 5px auto;
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
position: relative;
}
#box .grid-cell{
width: 100px;
height: 100px;
background-color: #ccc0b2;
margin: 10px;
border-radius: 5px;
text-align: center;
line-height: 100px;
}
.number-cell{
width: 100px;
height: 100px;
/*background-color: #eee4da;*/
border-radius: 5px;
font-size: 36px;
font-weight: bolder;
color: #796f65;
text-align: center;
line-height: 100px;
position: absolute;
left: 20px; /* 20+(120)n */
top: 13px; /* 13+(125)n */
}
#cover_bg{
width: 100%;
height: 100%;
background-color: #333333;
position: absolute;
z-index: 998;
opacity: 0.5;
display: none;
}
#cover{
z-index: 999;
opacity: 1.0;
position: absolute;
text-align: center;
display: none;
}
#cover .text_box{
width: 80px;
height: 80px;
background-color: #f57c5f;
border-radius: 5px;
display: inline-block;
margin: 0px 10px;
line-height: 80px;
font-size: 26px;
font-weight: bolder;
color: white;
opacity: 1.0;
}
#cover .text_box:nth-child(1){
background-color: #f2b179;
}
#cover .text_box:nth-child(2){
background-color: #f59562;
}
#cover .text_box:nth-child(4){
background-color: #f75e3c;
}
-
JS代码(使用了jQuery插件):
0.代码中设置的一些全局变量
var lock = false; // 按键锁,防止连续按键盘造成色块不停运动
var piece = new Array(); // piece[3][3]表示某个位置上是否有色块
var move_mark = false; // 表示当前是否有色块移动
var timer = 200; // 随机出现色块的时间间隔
var score=0; //游戏得分
1.游戏开始,随机显示两个色块
// 游戏初始化,首先将每个位置设置为没有色块
for (var i = 0; i < 4; i++) {
piece[i] = new Array();
for (var j = 0; j < 4; j++) {
piece[i][j] = 0;
}
}
// 随机显示色块
radom_piece();
radom_piece();
/**
* 在空白的格子上面随机产生一个"2"或"4"的色块
*/
function radom_piece() {
var test_num = 0;
var randomX = Math.floor(Math.random() * 4);
var randomY = Math.floor(Math.random() * 4);
var randomText = Math.random() < 0.5 ? 2 : 4;
while (true) {
if (!piece[randomX][randomY]) { // 当前格子上面没有色块
// 给当前格子上面添加色块,以及设置色块里面的数字
piece[randomX][randomY] = randomText;
var newPiece = '<div class="number-cell" merged="false" x="' + randomX + '" y="' + randomY + '" id="number-cell-' + randomX + '-' + randomY + '">' + randomText + '</div>';
$('#box').append(newPiece);
setColor(); //添加色块成功,设置色块的颜色
// 根据随机数字,安排色块的位置
$("#number-cell-" + randomX + "-" + randomY).css('left', 20 + 120 * randomY);
$("#number-cell-" + randomX + "-" + randomY).css('top', 13 + 125 * randomX);
break; //成功产生随机色块,跳出循环
}
test_num++;
var randomX = Math.floor(Math.random() * 4);
var randomY = Math.floor(Math.random() * 4);
}
}
2.检测键盘事件,执行一系列函数
备注:
- 这是整个游戏中最关键的部分,特别是四个方向的运动函数,不仅要使色块运动起来,还要判断相同色块进行合并的问题。色块运动执行完毕之后,要执行随机产生新的色块、判断游戏是否结束等函数。
- 色块运动函数一共有四个,四个函数大体相同,这里只给出一个函数,其他的函数可以在源码中获得。
// 键盘事件
$(document).keydown(function (e) {
move_mark = false; // 每一次按下键盘之前,将是否移动标志置为false,表示当前没有移动
$('.number-cell').attr('merged','false'); // 每一次按下键盘,将是否合并的标志位初始化,没有合并
if (lock) {
return false;
}
lock = true;
switch (e.keyCode) {
case 37: // 左边
{
/*------ 向左移动,左边第二列先移动,然后第三列,然后第四列 ------*/
for (var a = 0; a <= 3; a++) {
for (var b = 0; b <= 3; b++) {
var tar_piece = $('#number-cell-' + b + '-' + a);
if (tar_piece.hasClass('number-cell')) {
// console.log(tar_piece);
moveToLeft(tar_piece);
}
}
}
// 每移动一步,随机在空白格子上面出现一个色块,中间有一定时间间隔
setTimeout(function () {
if (move_mark) {
radom_piece();
game_score(); //每按下键盘一次,刷新当前的分数
if(is_game_over()){
game_over(); // 游戏停止,显示遮罩
clearInterval(game_timer); // 停止时间
}
}
lock = false;
}, timer);
break;
}
case 38: // 上边
{
for (var a = 0; a <= 3; a++) {
for (var b = 0; b <= 3; b++) {
var tar_piece = $('#number-cell-' + a + '-' + b);
if (tar_piece.hasClass('number-cell')) {
// console.log(tar_piece);
moveToUp(tar_piece);
}
}
}
// 每移动一步,随机在空白格子上面出现一个色块,中间有一定时间间隔
setTimeout(function () {
if (move_mark) {
radom_piece();
game_score(); //每按下键盘一次,刷新当前的分数
if(is_game_over()){
game_over(); // 游戏停止,显示遮罩
clearInterval(game_timer); // 停止时间
}
}
lock = false;
}, timer);
break;
}
case 39: // 右边
{
for (var a = 3; a >= 0; a--) {
for (var b = 3; b >= 0; b--) {
var tar_piece = $('#number-cell-' + b + '-' + a);
if (tar_piece.hasClass('number-cell')) {
// console.log(tar_piece);
moveToRight(tar_piece);
}
}
}
// 每移动一步,随机在空白格子上面出现一个色块,中间有一定时间间隔
setTimeout(function () {
if (move_mark) {
radom_piece();
game_score(); //每按下键盘一次,刷新当前的分数
if(is_game_over()){
game_over(); // 游戏停止,显示遮罩
clearInterval(game_timer); // 停止时间
}
}
lock = false;
}, timer);
break;
}
case 40: // 下边
{
for (var a = 3; a >= 0; a--) {
for (var b = 3; b >= 0; b--) {
var tar_piece = $('#number-cell-' + a + '-' + b);
if (tar_piece.hasClass('number-cell')) {
// console.log(tar_piece);
moveToDown(tar_piece);
}
}
}
// 每移动一步,随机在空白格子上面出现一个色块,中间有一定时间间隔
setTimeout(function () {
if (move_mark) {
radom_piece();
game_score(); //每按下键盘一次,刷新当前的分数
if(is_game_over()){
game_over(); // 游戏停止,显示遮罩
clearInterval(game_timer); // 停止时间
}
}
lock = false;
}, timer);
break;
}
}
});
/**
* 色块向左移动
* @param piece_selectd 选中的色块
* @returns {boolean}
*/
function moveToLeft(piece_selectd) {
var grid_num = 0; // 当前色块左边的空白格子个数
var x = parseInt(piece_selectd.attr('x'));
var y = parseInt(piece_selectd.attr('y'));
// 最左边一列不移动
if (y == 0) {
return true;
}
// 得到当前色块,左边空白格子的数量
for (var i = y - 1; i >= 0; i--) {
if (piece[x][i] == 0) { // 当前色块的左边格子上面没有色块
grid_num++;
}
}
var merge=$('#number-cell-'+x+'-'+(y - grid_num - 1)).attr('merged');
// 当前色块与相邻(不一定紧贴在一起)的色块一样,且相邻的色块之前没有合并过
if (piece[x][y] == piece[x][y - grid_num - 1] && merge == 'false') {
// 计算出色块移动到的位置,并移动色块
var moveTo = parseInt(piece_selectd.css('left')) - 120 * (grid_num + 1) + 'px';
piece_selectd.animate({left: moveTo}, 'fast', 'linear');
// 将与当前色块相邻且一样的色块删除
$('#number-cell-'+ x +'-'+(y - grid_num - 1)).remove();
// 更新当前色块以及色块数组的信息
piece_selectd.attr('y', y - grid_num-1);
piece_selectd.attr('id', 'number-cell-' + x + '-' + (y - grid_num-1));
piece_selectd.text(parseInt(piece_selectd.text()) * 2);
piece[x][y - grid_num-1] = parseInt(piece_selectd.text());
// 表示该色块已经合并过了,后面的色块不能合并这个
piece_selectd.attr('merged','true');
piece[x][y] = 0;
move_mark = true; // 表示当前有色块移动
setColor(); //添加色块成功,设置色块的颜色
}
else{
// 计算出色块移动到的位置,并移动色块
var moveTo = parseInt(piece_selectd.css('left')) - 120 * grid_num + 'px';
// console.log(moveTo);
piece_selectd.animate({left: moveTo}, 'fast', 'linear');
// 更新当前色块以及色块数组的信息
piece_selectd.attr('y', y - grid_num);
piece_selectd.attr('id', 'number-cell-' + x + '-' + (y - grid_num));
piece[x][y - grid_num] = parseInt(piece_selectd.text());
if (grid_num) {
piece[x][y] = 0;
move_mark = true; // 表示当前有色块移动
}
}
// console.log(grid_num);
return true;
}
/**
* 游戏的得分
*/
function game_score(){
score=0;
$.each($('.number-cell'),function(index,value){
score=score+parseInt($(value).text());
});
$('#score_val').text(score);
}
/**
* 判断游戏是否结束
* @returns {number} 返回0表示游戏没有结束,返回1表示游戏结束
*/
function is_game_over(){
console.log(666);
// 一次判断16个格子上面是否有色块,只要有一个格子没有色块,则返回0,表示游戏没有结束
for(var i=0;i<4;i++) {
for(var j=0;j<4;j++){
if(piece[i][j] == 0){
return 0;
}
}
}
// 任何一个色块,如果其上下左右紧邻的色块有与自己想通的色块,表示游戏没有结束
for(var i=0;i<4;i++){
for(var j=0;j<4;j++){
if(piece[i][j] == piece[i+1][j]){
return 0;
}
if(piece[i][j] == piece[i-1][j]){
return 0;
}
if(piece[i][j] == piece[i][j+1]){
return 0;
}
if(piece[i][j] == piece[i][j-1]){
return 0;
}
}
}
return 1; //上面条件均不满足,则表示游戏已经结束
}
/**
* 游戏结束,显示出遮罩
*/
function game_over(){
$('#cover').show();
$('#cover_bg').show();
}
3.一旦有色块出现,或者是色块合并产生新的色块,进行设置或更新色块的颜色
// 根据色块里面的数字,设置色块的背景
setColor();
/**
* 设置页面中所有色块的背景色和前景色
*/
function setColor() {
$.each($('.number-cell'), function (index, value) {
$(value).css('background-color', setBgColorFromNum($(value).text()));
$(value).css('color', setTextColorFromNum($(value).text()));
});
}
/**
* 根据数字,返回色块的背景色
* @param num 色块里面的数字
* @returns {string} 返回色块的背景色
*/
function setBgColorFromNum(num) {
switch (num) {
case '2':
return "#eee4da";
case '4':
return "#ece0ca";
case '8':
return "#f2b179";
case '16':
return "#f59562";
case '32':
return "#f57c5f";
case '64':
return "#f75e3c";
case '128':
return "#edce71";
case '256':
return "#f1c967";
case '512':
return "#ecc850";
case '1024':
return "#efc53f";
default:
return "red"
}
}
/**
* 根据数字,返回色块中数字的颜色
* @param num 色块里面的数字
* @returns {string} 返回色块中数字的颜色
*/
function setTextColorFromNum(num) {
switch (num) {
case '2':
return "#796f65";
case '4':
return "#796f65";
default:
return "#ffffff";
}
}
4.显示游戏进行的时间
// 游戏的已经持续的时间
var game_timer=setInterval(function(){
$('#time_val').text(parseInt($('#time_val').text())+1);
},1000);
5.源码获取
- 这里首先给出一个CSDN下载地址:https://download.csdn.net/download/fengzhen8023/10545750
- 后期会给出GitHub地址