# H5 六边形消除游戏开发

## 功能介绍

• [x] 六边形棋盘绘制、方块随机生成
• [x] 方块能否落入棋盘的判定
• [x] 方块消除与游戏结束的判定
• [x] 各种动画效果
• [x] 游戏计分

## 棋盘绘制

### 从六角网格到像素

``````  hex2pixel(hex, h) {
let size = h / 2;
let x = size * Math.sqrt(3) * (hex.q + hex.r / 2);
let y = ((size * 3) / 2) * hex.r;
return cc.p(x, y);
}
``````

### 网格坐标系生成

``````  setHexagonGrid() {
this.hexSide = 5;
this.hexSide--;
for (let q = -this.hexSide; q <= this.hexSide; q++) {
let r1 = Math.max(-this.hexSide, -q - this.hexSide);
let r2 = Math.min(this.hexSide, -q + this.hexSide);
for (let r = r1; r <= r2; r++) {
let col = q + this.hexSide;
let row = r - r1;
if (!this.hexes[col]) {
this.hexes[col] = [];
}
this.hexes[col][row] = this.hex2pixel({ q, r }, this.tileH);
}
}
}
``````

``````  setSpriteFrame(hexes) {
for (let index = 0; index < hexes.length; index++) {
let node = new cc.Node('frame');
let sprite = node.addComponent(cc.Sprite);
sprite.spriteFrame = this.tilePic;
node.x = hexes[index].x;
node.y = hexes[index].y;
node.parent = this.node;
hexes[index].spriteFrame = node;
this.setFillNode(node);
this.boardFrameList.push(node);
}
}
``````

## 方块随机生成

``````const Tiles = [
{
type: 1,
list: [[[0, 0]]]
},
{
type: 2,
list: [
[[1, -1], [0, 0], [1, 0], [0, 1]],
[[0, 0], [1, 0], [-1, 1], [0, 1]],
[[0, 0], [1, 0], [0, 1], [1, 1]]
]
},
{
type: 3,
list: [
[[0, -1], [0, 0], [0, 1], [0, 2]],
[[0, 0], [1, -1], [-1, 1], [-2, 2]],
[[-1, 0], [0, 0], [1, 0], [2, 0]]
]
},
{
type: 4,
list: [
[[0, 0], [0, 1], [0, -1], [-1, 0]],
[[0, 0], [0, -1], [1, -1], [-1, 1]],
[[0, 0], [0, 1], [0, -1], [1, 0]],
[[0, 0], [1, 0], [-1, 0], [1, -1]],
[[0, 0], [1, 0], [-1, 0], [-1, 1]]
]
},
{
type: 5,
list: [
[[0, 0], [0, 1], [0, -1], [1, -1]],
[[0, 0], [1, -1], [-1, 1], [-1, 0]],
[[0, 0], [1, -1], [-1, 1], [1, 0]],
[[0, 0], [1, 0], [-1, 0], [0, -1]],
[[0, 0], [1, 0], [-1, 0], [0, 1]]
]
},
{
type: 6,
list: [
[[0, -1], [-1, 0], [-1, 1], [0, 1]],
[[-1, 0], [0, -1], [1, -1], [1, 0]],
[[0, -1], [1, -1], [1, 0], [0, 1]],
[[-1, 1], [0, 1], [1, 0], [1, -1]],
[[-1, 0], [-1, 1], [0, -1], [1, -1]],
[[-1, 0], [-1, 1], [0, 1], [1, 0]]
]
}
];
``````

``````const getRandomInt = function(min, max) {
let ratio = cc.random0To1();
return min + Math.floor((max - min) * ratio);
};
``````

## 方块落入棋盘逻辑

### 碰撞检测 （重合判定）

``````  checkCollision(event) {
const tiles = this.node.children; // this.node 为 方块的父级，拖拽改变的是这个节点的坐标
this.boardTiles = []; // 保存棋盘与方块重合部分。
this.fillTiles = []; // 保存方块当前重合的部分。
for (let i = 0; i < tiles.length; i++) {
const tile = tiles[i];
const pos = cc.pAdd(this.node.position, tile.position); // pAdd 是cc早期提供的 api，可用 vec2 中向量相加替换
const boardTile = this.checkDistance(pos);
if (boardTile) {
this.fillTiles.push(tile);
this.boardTiles.push(boardTile);
}
}
},
checkDistance(pos) {
const distance = 50;
const boardFrameList = this.board.boardFrameList;
for (let i = 0; i < boardFrameList.length; i++) {
const frameNode = boardFrameList[i];
const nodeDistance = cc.pDistance(frameNode.position, pos);
if (nodeDistance <= distance) {
return frameNode;
}
}
},
``````

## 落子判定

``````checkCanDrop() {
const boardTiles = this.boardTiles; // 当前棋盘与方块重合部分。
const fillTiles = this.node.children; // 当前拖拽的方块总数
const boardTilesLength = boardTiles.length;
const fillTilesLength = fillTiles.length;

// 如果当前棋盘与方块重合部分为零以及与方块数目不一致，则判定为不能落子。
if (boardTilesLength === 0 || boardTilesLength != fillTilesLength) {
return false;
}

// 如果方块内以及存在方块，则判定为不能落子。
for (let i = 0; i < boardTilesLength; i++) {
if (this.boardTiles[i].isFulled) {
return false;
}
}

return true;
},
``````

## 落子提示

``````  dropPrompt(canDrop) {
const boardTiles = this.boardTiles;
const boardTilesLength = boardTiles.length;
const fillTiles = this.fillTiles;

this.resetBoardFrames();
if (canDrop) {
for (let i = 0; i < boardTilesLength; i++) {
const spriteFrame = fillTiles[i].getComponent(cc.Sprite).spriteFrame;
}
}
}
``````

## 落入逻辑

``````  tileDrop() {
this.resetBoardFrames();
if (this.checkCanDrop()) {
const boardTiles = this.boardTiles;
const fillTiles = this.fillTiles;
const fillTilesLength = fillTiles.length;

for (let i = 0; i < fillTilesLength; i++) {
const boardTile = boardTiles[i];
const fillTile = fillTiles[i];
const fillNode = boardTile.getChildByName('fillNode');
const spriteFrame = fillTile.getComponent(cc.Sprite).spriteFrame;

boardTile.isFulled = true;
fillNode.getComponent(cc.Sprite).spriteFrame = spriteFrame;
this.resetTile();
}
this.board.curTileLength = fillTiles.length;
this.board.node.emit('dropSuccess');
} else {
this.backSourcePos();
}
this.board.checkLose();
}
``````

## 消除逻辑

``````const DelRules = [
//左斜角
[0, 1, 2, 3, 4],
[5, 6, 7, 8, 9, 10],
[11, 12, 13, 14, 15, 16, 17],
[18, 19, 20, 21, 22, 23, 24, 25],
[26, 27, 28, 29, 30, 31, 32, 33, 34],
[35, 36, 37, 38, 39, 40, 41, 42],
[43, 44, 45, 46, 47, 48, 49],
[50, 51, 52, 53, 54, 55],
[56, 57, 58, 59, 60],

//右斜角
[26, 35, 43, 50, 56],
[18, 27, 36, 44, 51, 57],
[11, 19, 28, 37, 45, 52, 58],
[5, 12, 20, 29, 38, 46, 53, 59],
[0, 6, 13, 21, 30, 39, 47, 54, 60],
[1, 7, 14, 22, 31, 40, 48, 55],
[2, 8, 15, 23, 32, 41, 49],
[3, 9, 16, 24, 33, 42],
[4, 10, 17, 25, 34],

//水平
[0, 5, 11, 18, 26],
[1, 6, 12, 19, 27, 35],
[2, 7, 13, 20, 28, 36, 43],
[3, 8, 14, 21, 29, 37, 44, 50],
[4, 9, 15, 22, 30, 38, 45, 51, 56],
[10, 16, 23, 31, 39, 46, 52, 57],
[17, 24, 32, 40, 47, 53, 58],
[25, 33, 41, 48, 54, 59],
[34, 42, 49, 55, 60]
];
``````

``````  deleteTile() {
let fulledTilesIndex = []; // 存储棋盘内有方块的的索引
let readyDelTiles = []; // 存储待消除方块
const boardFrameList = this.boardFrameList;
this.isDeleting = true; // 方块正在消除的标识，用于后期添加动画时，充当异步状态锁

// 首先获取棋盘内存在方块的格子信息
for (let i = 0; i < boardFrameList.length; i++) {
const boardFrame = boardFrameList[i];
if (boardFrame.isFulled) {
fulledTilesIndex.push(i);
}
}

for (let i = 0; i < DelRules.length; i++) {
const delRule = DelRules[i]; // 消除规则获取
// 逐一获取规则数组与存在方块格子数组的交集
let intersectArr = _.arrIntersect(fulledTilesIndex, delRule);
if (intersectArr.length > 0) {
// 判断两数组是否相同，相同则将方块添加到待消除数组里
const isReadyDel = _.checkArrIsEqual(delRule, intersectArr);
}
}
}

// 开始消除
let count = 0;
for (let i = 0; i < readyDelTiles.length; i++) {
for (let j = 0; j < readyDelTile.length; j++) {
const delTileIndex = readyDelTile[j];
const boardFrame = this.boardFrameList[delTileIndex];
const delNode = boardFrame.getChildByName('fillNode');
boardFrame.isFulled = false;
// 这里可以添加相应消除动画
const finished = cc.callFunc(() => {
delNode.getComponent(cc.Sprite).spriteFrame = null;
delNode.opacity = 255;
count++;
}, this);
}
}

if (count !== 0) {
this.checkLose();
}

this.isDeleting = false;
}
``````

## 游戏结束逻辑

``````  checkLose() {
let canDropCount = 0;
const tiles = this.node.children;
const tilesLength = tiles.length;
const boardFrameList = this.board.boardFrameList;
const boardFrameListLength = boardFrameList.length;

// TODO: 存在无效检测的情况，可优化
for (let i = 0; i < boardFrameListLength; i++) {
const boardNode = boardFrameList[i];
let srcPos = cc.p(boardNode.x, boardNode.y);
let count = 0;
if (!boardNode.isFulled) {
// 过滤出未填充的棋盘格子
for (let j = 0; j < tilesLength; j++) {
let len = 27; // 设定重合判定最小间距

// 将方块移到未填充的棋盘格子原点，并获取当前各方块坐标值
let tilePos = cc.pAdd(srcPos, cc.p(tiles[j].x, tiles[j].y));

// 遍历棋盘格子，判断方块中各六边形是否可以放入
for (let k = 0; k < boardFrameListLength; k++) {
const boardNode = boardFrameList[k];
let dis = cc.pDistance(cc.p(boardNode.x, boardNode.y), tilePos);
if (dis <= len && !boardNode.isFulled) {
count++;
}
}
}

if (count === tilesLength) {
canDropCount++;
}
}
}

if (canDropCount === 0) {
return true;
} else {
return false;
}
}
``````

## 计分制

``````  scoreRule(count, isDropAdd) {
let x = count + 1;
let addScoreCount = isDropAdd ? x : 2 * x * x;
}
``````

## 参考

YouTube视频网站现在默认使用HTML5播放器，这意味着更好的性能、 稳定性、 电池寿命和甚至是更好的安全性。现在用户通过Chrome、IE 11、Safari 8和Beta版本的Firefox进行浏览的时候都默认使用HTML5视频播放...
2015-11-12
JavaScript常用特效chm下载

2015-11-12
HTML5的5个不错的开发工具推荐
HTML5规范终于在今年正式定稿，对于从事多年HTML5开发的人员来说绝对是一个重大新闻。数字天堂董事长，DCloud CEO王安也发表了文章，从开发者和用户两个角度分析了HTML对两个人群的优势。其实，关于HTML5的开发工具，我们以往的...
2015-11-12
HTML5凭什么可以代替Flash

2015-11-12
three.js实现围绕某物体旋转

2017-02-17
HTML5究竟会火到什么地步

2015-11-12
Node.js学习(1)----HTTP服务器与客户端
Node.js 标准库提供了 http 模块，其中封装了一个高效的 HTTP 服务器和一个简易的HTTP 客户端。http.Server 是一个基于事件的 HTTP 服务器，它的核心由 Node.js 下层 C++部分实现，而接口由 Jav...
2015-11-12
2014年最流行前端开发框架对比评测

2015-11-12
2015年3月国内浏览器市场份额概括，chrome占32.97

2015-11-12
CSS2.0帮助文档（参考手册）chm下载

2015-11-12