原生JavaScript实现连连看游戏(附源码)

  向大家推荐一款原生JavaScript版连连看游戏,源码下载,首页如下图所示:

原生JavaScript实现连连看游戏(附源码)

  首先看一下html的布局方式在index.html文件中:

  

复制代码 代码如下:

  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

  <html>

  <head>

  <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>

  <title>连连看</title>

  <link rel="stylesheet" type="text/css" href="css/index.css"/>

  </head>

  <body>

  <center>

  <div id="whole">

  <div id="gamePanel" tabindex="0">

  <div id="pieces">

  </div>

  </div>

  <div id="gameLogo">

  </div>

  <div id="scorePanel">

  <span>分 数</span>

  </div>

  <div id="score">

  <span>0</span>

  </div>

  <div id="timePanel">

  <span>时 间</span>

  </div>

  <div id="time">

  <span>0</span>

  </div>

  <div id="button">

  <input id="start" type="button" onclick="Start();" value="开始"></input>

  <input id="reset" type="button" onclick="Reset();"value="重置"></input>

  </div>

  </div>

  </center>

  <script type="text/javascript" src="js/map.js"></script>

  <script type="text/javascript" src="js/piece.js"></script>

  <script type="text/javascript" src="js/game.js"></script>

  </body>

  </html>

  css文件夹下的index.css文件如下:

  

复制代码 代码如下:

  body {

  font-size : 16px;

  font-weight : bold;

  color : grey;

  }

  #whole {

  border : 1px double #999999;

  border-width : 5px;

  width : 800px;

  height : 505px;

  position : relative;

  }

  #gamePanel {

  margin: 1px 1px 1px 1px;

  width : 602px;

  height : 502px;

  background : url(../img/background.gif) repeat;

  position : absolute;

  }

  #pieces {

  margin-top : 35px;

  border : 1px solid #999999;

  width : 546px;

  height : 434px;

  position: relative;

  }

  #pieces .piece {

  width : 32px;

  height : 36px;

  position : relative;

  cursor : pointer;

  float : left;

  }

  #pieces .track {

  width : 32px;

  height : 36px;

  position : relative;

  float : left;

  }

  #pieces .track2 {

  width : 32px;

  height : 36px;

  position : relative;

  float : left;

  background : red;

  }

  #gameLogo {

  margin-top : 60px;

  border : 1px solid #999999;

  left : 607px;

  width : 187px;

  height : 73px;

  background : url(../img/logo.gif);

  position: absolute;

  }

  #scorePanel {

  border : 1px solid #999999;

  left : 607px;

  top : 200px;

  width : 187px;

  height : 30px;

  position : absolute;

  }

  #score {

  border : 1px solid #999999;

  left : 607px;

  top : 240px;

  width : 187px;

  height : 30px;

  position : absolute;

  }

  #timePanel {

  border : 1px solid #999999;

  left : 607px;

  top : 300px;

  width : 187px;

  height : 30px;

  position : absolute;

  }

  #time {

  border : 1px solid #999999;

  left : 607px;

  top : 340px;

  width : 187px;

  height : 30px;

  position : absolute;

  }

  #button {

  border : 1px solid #999999;

  left : 607px;

  top : 400px;

  width : 187px;

  height : 30px;

  position : absolute;

  }

  下面让我们来看一下最核心的js部分实现代码,js部分分为三个源文件即game.js、map.js、piece.js每一个源文件对应一个类,其中本游戏通过game类来操纵map和图片piece对象:

  game.js代码如下:

  

复制代码 代码如下:

  // 游戏控制类

  var Game = {

  // 游戏背景

  gamePanel : null,

  // 分数

  score : 0,

  // 时间

  time : 0,

  // 图片映射表

  pieceMap : null,

  // 图片列表

  pieceList : [],

  // 图片列表不包含图片

  pieceImgList : [],

  // 图片随机数列表

  randomList : [],

  // 轨迹列表

  trackList : [],

  // 游戏是否开始

  isGameBigin : false,

  // 游戏是否结束

  isGameOver : false,

  // 游戏是否重置

  isGameReset : false,

  // 图片元素是否第一次点击

  isFirstClick : true,

  // 开始游戏

  start : function() {

  document.getElementById("start").disabled = true;

  document.getElementById("reset").disabled = false;

  if (this.isGameReset) {

  this.isGameOver = false;

  this.startTime();

  return;

  } else if (this.isGameBegin) {

  return;

  } else {

  this.init();

  return;

  }

  },

  reset : function() {

  document.getElementById("start").disabled = false;

  document.getElementById("reset").disabled = true;

  this.clear();

  this.initPieces();

  this.initImgPieces();

  this.time = 0;

  document.getElementById("time").innerHTML = 0;

  this.score = 0;

  document.getElementById("score").innerHTML = 0;

  this.isGameReset = true;

  this.isGameBegin = true;

  },

  // 初始化

  init : function() {

  if (this.isGameBegin) {

  return;

  }

  this.pieceMap = new Map();

  var _this = this;

  this.time = 0;

  this.startTime();

  this.gamePanel = document.getElementById("pieces");

  this.initPieces();

  this.initImgPieces();

  this.isGameBegin = true;

  },

  // 将随机生成的150张图片添加进画布

  initPieces : function() {

  var _this = this;

  this.initRandomList();

  // 打乱随机列表排序

  this.messRandomList();

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

  var piece = new Piece(this);

  this.pieceList.push(piece);

  var x = (i%17);

  var y = Math.floor(i/17);

  this.pieceMap.put(x+","+y, piece);

  piece.setPosition(x, y);

  this.gamePanel.appendChild(piece.dom);

  if (x == 0 || x == 16 || y == 0 || y == 11) {

  piece.track = document.createElement("div");

  piece.track.className = "track";

  piece.dom.appendChild(piece.track);

  piece.isTracked = true;

  continue;

  } else {

  if (x == 1 || x == 15 || y == 1 || y == 10) {

  piece.setAtEdge(true);

  }

  this.pieceImgList.push(piece);

  }

  }

  },

  // 初始化图片

  initImgPieces : function() {

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

  this.pieceImgList[i].initImg();

  this.pieceImgList[i].img.src = "img/pieces/"+this.randomList[i]+".gif"

  this.pieceImgList[i].setImgSrc(this.pieceImgList[i].img.src);

  // 执行图片点击事件

  this.pieceImgList[i].onClick();

  }

  },

  // 初始化随机表

  initRandomList : function() {

  // 获取随机数列,成双出现

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

  var random = parseInt(Math.random()*22*10000, 10);

  var number = random%23;

  this.randomList.push(number);

  this.randomList.push(number);

  }

  },

  // 打乱随机表

  messRandomList : function() {

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

  var random = parseInt(Math.random()*15*10000, 10);

  var number = random%150;

  var temp;

  temp = this.randomList[i];

  this.randomList[i] = this.randomList[number];

  this.randomList[number] = temp;

  }

  },

  // 开始计时

  startTime : function() {

  var _this = this;

  if (this.isGameOver) {

  return;

  } else {

  this.time ++;

  document.getElementById("time").innerHTML = this.time;

  this.isGameBegin = true;

  setTimeout(function() {_this.startTime();}, 1000);

  }

  },

  // 清除

  clear : function() {

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

  this.gamePanel.removeChild(this.pieceList[i].dom);

  }

  this.pieceList = [];

  this.randomList = [];

  this.pieceImgList = [];

  this.isGameOver = true;

  this.isGameBegin = false;

  }

  }

  window.onload = function() {

  document.getElementById("start").disabled = false;

  document.getElementById("reset").disabled = true;

  }

  // 游戏开始入口

  function Start() {

  Game.start();

  }

  // 游戏重置入口

  function Reset() {

  Game.reset();

  }

  自定义的js版映射结构map.js源文件如下:

  

复制代码 代码如下:

  var Map = function(){

  this.data = [];

  }

  Map.prototype = {

  put : function(key, value) {

  this.data[key] = value;

  },

  get : function(key) {

  return this.data[key];

  },

  remove : function(key) {

  this.data[key] = null;

  },

  isEmpty : function() {

  return this.data.length == 0;

  },

  size : function() {

  return this.data.length;

  }

  }

  图片类piece.js源文件如下:

  

复制代码 代码如下:

  var Piece = function(game) {

  // 游戏对象

  this.game = game;

  // 是否为边缘元素

  this.isEdge = false;

  // 是否挨着边缘元素

  this.atEdge = false;

  // 图片dom元素

  this.dom = null;

  // 图片元素

  this.img = null;

  // 图片元素来源

  this.src = null;

  // 轨迹元素

  this.track = null;

  // 是否可以作为轨迹

  this.isTracked = false;

  // 选中标记元素

  this.selected = null;

  // 图片横向排列

  this.x = 0;

  // 图片纵向排列

  this.y = 0;

  // 图片闪烁Id

  this.flashId = null;

  // 图片是否点击

  this.onClicked = false;

  // 闪烁次数

  this.flashCount = 0;

  this.init();

  }

  Piece.prototype = {

  // 初始化

  init : function() {

  this.dom = document.createElement("div");

  this.dom.className = "piece";

  this.selected = document.createElement("img");

  },

  // 初始化图片

  initImg : function() {

  this.img = document.createElement("img");

  this.dom.appendChild(this.img);

  },

  // 满足算法后初始化track元素

  initTrack : function() {

  if (this.flashId != null) {

  // 停止闪烁

  this.stopFlash();

  }

  //alert("initTrack middle");

  if (this.track != null) {

  return;

  }

  this.onClicked = false;

  this.dom.removeChild(this.img);

  this.track = document.createElement("div");

  this.track.className = "track";

  this.dom.appendChild(this.track);

  },

  // 位图片设置来源

  setImgSrc : function(src) {

  this.src = src;

  },

  // 为图片设置二维排列位置

  setPosition : function(x, y) {

  this.x = x;

  this.y = y;

  },

  // 为图片设置选中元素

  setSelected : function() {

  if (this.flashCount ++ % 2 == 0) {

  //this.dom.removeChild(this.img);

  //this.selected.src = "img/selected.gif";

  //this.dom.appendChild(this.selected);

  this.img.src = "img/pieces/flash.gif";

  } else {

  //if (this.selected != null) {

  // this.dom.removeChild(this.selected);

  //}

  this.img.src = this.src;

  //this.dom.appendChild(this.img);

  }

  },

  // 设置是否为边缘元素

  setEdge : function(isEdge) {

  this.isEdge = isEdge;

  },

  // 设置是否挨着边缘元素

  setAtEdge : function(atEdge) {

  this.atEdge = atEdge;

  },

  // 开始闪烁

  flash : function() {

  var _this = this;

  this.flashId = setInterval(function() {_this.setSelected();}, 500);

  },

  // 停止闪烁

  stopFlash : function() {

  clearInterval(this.flashId);

  if (this.flashCount % 2 == 1) {

  //if (this.selected != null) {

  // this.dom.removeChild(this.selected);

  //}

  this.img.src = this.src;

  //this.dom.appendChild(this.img);

  }

  },

  // 对象被选择的内部函数

  onClick : function() {

  if (this.onClicked) {

  return;

  }

  var _this = this;

  this.img.onclick = function() {

  if (!document.getElementById("start").disabled) {

  return;

  }

  if (_this.onClicked) {

  return;

  }

  if (_this.checkPiece()) {

  return;

  }

  _this.flash();

  _this.onClicked = true;

  };

  },

  // 检查是否有被点击的图片

  checkPiece : function() {

  for (var i = 0; i < this.game.pieceList.length; i ++) {

  if (this.game.pieceList[i].onClicked && !this.game.pieceList[i].equal(this)) {

  if (this.game.pieceList[i].equalImage(this)) {

  //alert("The same Image");

  this.searchTrack(this.game.pieceList[i]);

  } else {

  this.game.pieceList[i].stopFlash();

  this.game.pieceList[i].onClicked = false;

  this.onClicked = false;

  return false;

  }

  return true;

  } else {

  continue;

  }

  }

  return false;

  },

  // 是否为同一个对象

  equal : function(piece) {

  return (this.x == piece.x && this.y == piece.y);

  },

  // 是否为同一个图片

  equalImage : function(piece) {

  return this.src == piece.src;

  },

  // 搜寻路径

  searchTrack : function(piece) {

  if (this.isNear(piece)) {

  this.linkTrack(piece);

  return;

  }

  if (this.isReach(piece) || this.isReach2(piece)) {

  this.linkTrack(piece);

  return;

  }

  },

  // 是否相邻

  isNear : function(piece) {

  var a = (Math.abs(piece.x - this.x) == 1) && (piece.y == this.y)

  || (Math.abs(piece.y - this.y) == 1) && (piece.x == this.x);

  return a;

  },

  // 直线

  isStraightReach : function(piece) {

  //alert("isStraightReach");

  if (this.isNear(piece)) {

  return true;

  }

  var a = false;

  var b = false;

  // 沿y轴方向搜索

  if (this.x == piece.x) {

  //alert("!!!!!!!!!!!");

  for (var i = this.min(this.y, piece.y) + 1; i < this.max(this.y, piece.y); i ++) {

  //alert("this.x == piece.x: " + piece.x + "," + i);

  if (this.game.pieceMap.get(piece.x + "," + i).isPass()) {

  a = true;

  this.game.trackList.push(this.game.pieceMap.get(piece.x + "," + i));

  continue;

  } else {

  a = false;

  this.game.trackList = [];

  return a;

  }

  }

  }

  // 沿x轴方向搜索

  if (this.y == piece.y) {

  //alert("!!!!!!!!!!!");

  for (var i = this.min(this.x, piece.x) + 1; i < this.max(this.x, piece.x); i ++) {

  //alert("this.y == piece.y: " + i + "," + piece.y);

  if (this.game.pieceMap.get(i + "," + piece.y).isPass()) {

  b = true;

  this.game.trackList.push(this.game.pieceMap.get(i + "," + piece.y));

  continue;

  } else {

  b = false

  this.game.trackList = [];

  return b;

  }

  }

  }

  return a || b;

  },

  // 拐一次弯搜索

  isReach1 : function(piece) {

  //alert("isReach1");

  var corner_1 = this.game.pieceMap.get(this.x + "," + piece.y);

  var corner_2 = this.game.pieceMap.get(piece.x + "," + this.y);

  var _this = this;

  if ((_this.isStraightReach(corner_1))

  && (corner_1.isStraightReach(piece))

  && corner_1.isPass()) {

  //alert("corner_1: " + this.x + "," + piece.y);

  this.game.trackList.push(corner_1);

  return true;

  }

  if ((_this.isStraightReach(corner_2))

  && (corner_2.isStraightReach(piece))

  && corner_2.isPass()) {

  //alert("corner_2: " + piece.x + "," + this.y);

  this.game.trackList.push(corner_2);

  return true;

  }

  return false;

  },

  // 直接或拐一次弯搜索

  isReach : function(piece) {

  var a = this.isStraightReach(piece);

  var b = this.isReach1(piece);

  return a || b;

  },

  // 拐两次弯搜索

  isReach2 : function(piece) {

  // 沿x轴正向搜索

  for (var i = this.x + 1; i < 17; i ++) {

  if (!this.game.pieceMap.get(i + "," + this.y).isPass()) {

  this.game.trackList = [];

  break;

  } else if (this.game.pieceMap.get(i + "," + this.y).isReach(piece)

  && this.game.pieceMap.get(i + "," + this.y).isPass()) {

  this.game.trackList.push(this.game.pieceMap.get(i + "," + this.y));

  return true;

  }

  }

  // 沿x轴搜索

  for (var i = this.x - 1; i >= 0; i --) {

  if (!this.game.pieceMap.get(i + "," + this.y).isPass()) {

  this.game.trackList = [];

  break;

  } else if (this.game.pieceMap.get(i + "," + this.y).isReach(piece)

  && this.game.pieceMap.get(i + "," + this.y).isPass()) {

  this.game.trackList.push(this.game.pieceMap.get(i + "," + this.y));

  return true;

  }

  }

  // 沿y轴搜索

  for (var i = this.y - 1; i >= 0; i --) {

  if (!this.game.pieceMap.get(this.x + "," + i).isPass()) {

  this.game.trackList = [];

  break;

  } else if (this.game.pieceMap.get(this.x + "," + i).isReach(piece)

  && this.game.pieceMap.get(this.x + "," + i).isPass()) {

  this.game.trackList.push(this.game.pieceMap.get(this.x + "," + i));

  return true;

  }

  }

  // 沿y轴正向搜索

  for (var i = this.y + 1; i < 12; i ++) {

  if (!this.game.pieceMap.get(this.x + "," + i).isPass()) {

  this.game.trackList = [];

  break;

  } else if (this.game.pieceMap.get(this.x + "," + i).isReach(piece)

  && this.game.pieceMap.get(this.x + "," + i).isPass()) {

  this.game.trackList.push(this.game.pieceMap.get(this.x + "," + i));

  return true;

  }

  }

  return false;

  },

  // 路径连接

  linkTrack : function(piece) {

  this.initTrack();

  piece.initTrack();

  this.changeScore();

  this.showTrack(piece);

  },

  // 显示足迹

  showTrack : function(piece) {

  this.game.trackList.push(piece);

  this.track.className = "track2";

  for (var i = 0; i < this.game.trackList.length; i ++) {

  //alert(i);

  this.game.trackList[i].track.className = "track2";

  }

  var _this = this;

  setTimeout(function() {_this.hideTrack()}, 500);

  },

  // 隐匿足迹

  hideTrack : function() {

  for (var i = 0; i < this.game.trackList.length; i ++) {

  this.game.trackList[i].track.className = "track";

  }

  this.game.trackList = [];

  this.track.className = "track";

  this.isTracked = true;

  },

  // 分数增加

  changeScore : function() {

  this.game.score += 100;

  document.getElementById("score").innerHTML = this.game.score;

  },

  min : function(a, b) {

  if (a < b) {

  return a;

  } else {

  return b;

  }

  },

  max : function(a, b) {

  if (a > b) {

  return a;

  } else {

  return b;

  }

  },

  // 判断是否通过

  isPass : function() {

  return this.track != null;

  }

  }

  以上是源文件的代码,具体的实现代码请关注CSDN中zhangjinpeng66下载。下面讲一下连连看游戏最核心的部分,js实现搜索路径。

  js实现搜索路径算法首先最简单的是判断两个图片能否直线到达函数代码如下:

  

复制代码 代码如下:

  // 直线

  isStraightReach : function(piece) {

  //alert("isStraightReach");

  if (this.isNear(piece)) {

  return true;

  }

  var a = false;

  var b = false;

  // 沿y轴方向搜索

  if (this.x == piece.x) {

  //alert("!!!!!!!!!!!");

  for (var i = this.min(this.y, piece.y) + 1; i < this.max(this.y, piece.y); i ++) {

  //alert("this.x == piece.x: " + piece.x + "," + i);

  if (this.game.pieceMap.get(piece.x + "," + i).isPass()) {

  a = true;

  this.game.trackList.push(this.game.pieceMap.get(piece.x + "," + i));

  continue;

  } else {

  a = false;

  this.game.trackList = [];

  return a;

  }

  }

  }

  // 沿x轴方向搜索

  if (this.y == piece.y) {

  //alert("!!!!!!!!!!!");

  for (var i = this.min(this.x, piece.x) + 1; i < this.max(this.x, piece.x); i ++) {

  //alert("this.y == piece.y: " + i + "," + piece.y);

  if (this.game.pieceMap.get(i + "," + piece.y).isPass()) {

  b = true;

  this.game.trackList.push(this.game.pieceMap.get(i + "," + piece.y));

  continue;

  } else {

  b = false

  this.game.trackList = [];

  return b;

  }

  }

  }

  return a || b;

  },

  该函数实现了连连看判断两图片是否符合连接条件的最简单的一步,然后是拐一次弯搜索。

  

复制代码 代码如下:

  // 拐一次弯搜索

  isReach1 : function(piece) {

  //alert("isReach1");

  var corner_1 = this.game.pieceMap.get(this.x + "," + piece.y);

  var corner_2 = this.game.pieceMap.get(piece.x + "," + this.y);

  var _this = this;

  if ((_this.isStraightReach(corner_1))

  && (corner_1.isStraightReach(piece))

  && corner_1.isPass()) {

  //alert("corner_1: " + this.x + "," + piece.y);

  this.game.trackList.push(corner_1);

  return true;

  }

  if ((_this.isStraightReach(corner_2))

  && (corner_2.isStraightReach(piece))

  && corner_2.isPass()) {

  //alert("corner_2: " + piece.x + "," + this.y);

  this.game.trackList.push(corner_2);

  return true;

  }

  return false;

  },

  在拐一次弯搜索的函数中调用了直接搜索的函数,同样最复杂的拐两次弯搜索也会调用拐一次弯搜索的函数。

  

复制代码 代码如下:

  // 拐两次弯搜索

  isReach2 : function(piece) {

  // 沿x轴正向搜索

  for (var i = this.x + 1; i < 17; i ++) {

  if (!this.game.pieceMap.get(i + "," + this.y).isPass()) {

  this.game.trackList = [];

  break;

  } else if (this.game.pieceMap.get(i + "," + this.y).isReach(piece)

  && this.game.pieceMap.get(i + "," + this.y).isPass()) {

  this.game.trackList.push(this.game.pieceMap.get(i + "," + this.y));

  return true;

  }

  }

  // 沿x轴搜索

  for (var i = this.x - 1; i >= 0; i --) {

  if (!this.game.pieceMap.get(i + "," + this.y).isPass()) {

  this.game.trackList = [];

  break;

  } else if (this.game.pieceMap.get(i + "," + this.y).isReach(piece)

  && this.game.pieceMap.get(i + "," + this.y).isPass()) {

  this.game.trackList.push(this.game.pieceMap.get(i + "," + this.y));

  return true;

  }

  }

  // 沿y轴搜索

  for (var i = this.y - 1; i >= 0; i --) {

  if (!this.game.pieceMap.get(this.x + "," + i).isPass()) {

  this.game.trackList = [];

  break;

  } else if (this.game.pieceMap.get(this.x + "," + i).isReach(piece)

  && this.game.pieceMap.get(this.x + "," + i).isPass()) {

  this.game.trackList.push(this.game.pieceMap.get(this.x + "," + i));

  return true;

  }

  }

  // 沿y轴正向搜索

  for (var i = this.y + 1; i < 12; i ++) {

  if (!this.game.pieceMap.get(this.x + "," + i).isPass()) {

  this.game.trackList = [];

  break;

  } else if (this.game.pieceMap.get(this.x + "," + i).isReach(piece)

  && this.game.pieceMap.get(this.x + "," + i).isPass()) {

  this.game.trackList.push(this.game.pieceMap.get(this.x + "," + i));

  return true;

  }

  }

  return false;

  },

  该函数以点击的图片为中心分别沿x轴,y轴展开搜索。

  以上是本游戏代码的全部内容。具体游戏源码请到CSDN中zhangjinpeng66的资源里下载。