环境:cocos creator 2.0.10 + Mac环境 + TypeScript (文末附源码链接)
本文提供了游戏玩法的逻辑,支持关卡配置(可以自由配置关卡)
先看一下效果:
首先,我们看一下我们要处理的问题:
1. 区域划分
2. 角色摆放(即:关卡元素摆放)
3. 如何移动 (滑动处理,能否移动)
4. 是否过关
把游戏里的点,有条不紊的划分出来,并一一击破,这样实现起来才会更加轻松自如。
代码并不复杂,应该很容易解读
eROW:5行
eCOL:4列
eGridSideLength:单位格子宽度为150
eDIR:移动方向
HEDRoles:角色配置信息 place:12 拆开来看,即横向占用1个格子,纵向占用2个格子
/**
* 配置信息
*/
export var eROW = 5;
export var eCOL = 4;
export var eGridSideLength = 150;
export var eDIR = cc.Enum({
UP: 1,
DOWN: 2,
LEFT: 3,
RIGHT: 4,
});
export var HRDRoles = {
"1": {
"name": "曹操",
"engName": "CaoCao",
"place": 22,
"spriteFrame": "caocao22"
},
"2": {
"name": "关羽",
"engName": "GuanYu",
"place": 12,
"spriteFrame": "guanyu12"
},
"3": {
"name": "关羽",
"engName": "GuanYu",
"place": 21,
"spriteFrame": "guanyu21"
},
"4": {
"name": "张飞",
"engName": "ZhangFei",
"place": 12,
"spriteFrame": "zhangfei12"
},
"5": {
"name": "张飞",
"engName": "ZhangFei",
"place": 21,
"spriteFrame": "zhangfei21"
},
"6": {
"name": "黄忠",
"engName": "HuangZhong",
"place": 12,
"spriteFrame": "huangzhong12"
},
"7": {
"name": "黄忠",
"engName": "HuangZhong",
"place": 21,
"spriteFrame": "zuangzhong21"
},
"8": {
"name": "马超",
"engName": "MaChao",
"place": 12,
"spriteFrame": "machao12"
},
"9": {
"name": "马超",
"engName": "MaChao",
"place": 21,
"spriteFrame": "machao21"
},
"10": {
"name": "赵云",
"engName": "ZhaoYun",
"place": 12,
"spriteFrame": "zhaoyun12"
},
"11": {
"name": "赵云",
"engName": "ZhaoYun",
"place": 21,
"spriteFrame": "zhaoyun21"
},
"12": {
"name": "兵1",
"engName": "soldier",
"place": 11,
"spriteFrame": "soldier11"
},
"13": {
"name": "兵2",
"engName": "soldier",
"place": 11,
"spriteFrame": "soldier11"
},
"14": {
"name": "兵3",
"engName": "soldier",
"place": 11,
"spriteFrame": "soldier11"
},
"15": {
"name": "兵4",
"engName": "soldier",
"place": 11,
"spriteFrame": "soldier11"
}
};
关卡配置信息,每4个元素占用1行,一共5行,共20个元素 (其中0表示格子未被占用)
export var HRDLevels = {
"1": [2, 10, 0, 6, 2, 10, 0, 6, 8, 4, 1, 1, 8, 4, 1, 1, 12, 13, 14, 15],
};
import { HRDRoles, eGridSideLength, eROW, eCOL, eDIR } from "./Configs";
import { HRDLevels } from "./Levels";
const { ccclass, property } = cc._decorator;
/**
* 坐标索引,均以左上角为锚点
* &----&----&----&-----
* | 00 | 01 | 02 | 03 |
* &----&----&----&-----
* | 10 | 11 | 12 | 13 |
* &----&----&----&-----
* | 20 | 21 | 22 | 23 |
* &----&----&----&-----
* | 30 | 31 | 32 | 33 |
* &----&----&----&-----
* | 40 | 41 | 42 | 43 |
* ---------------------
*/
@ccclass
export default class HuaRongDao extends cc.Component {
@property(cc.Node)
nodeMoveArea: cc.Node = null;
@property(cc.Node)
nodeCong: cc.Node = null;
nodeRoleArr: Array = [];
// 左上角开始放置位置
m_placeStartPos: cc.Vec2 = cc.v2(0, 0);
placeArr: Array = [];
m_startPos: cc.Vec2 = null;
m_endPos: cc.Vec2 = null;
m_touchNode: cc.Node = null;
onLoad() {
this.addTouchEventListener();
this._initLevel("1");
}
private addTouchEventListener() {
this.node.on(cc.Node.EventType.TOUCH_START, this.onTouchStart, this);
this.node.on(cc.Node.EventType.TOUCH_MOVE, this.onTouchMove, this);
this.node.on(cc.Node.EventType.TOUCH_END, this.onTouchEnd, this);
this.node.on(cc.Node.EventType.TOUCH_CANCEL, this.onTouchCancel, this);
}
private onTouchStart(event) {
this.m_startPos = event.touch.getLocation();
// 找到touch的角色
let worldPos = this.node.convertToWorldSpace(this.m_startPos);
for (let i = 0; i < this.nodeRoleArr.length; i++) {
let curNode = this.nodeRoleArr[i];
if (curNode && curNode.isValid) {
let nodePos = this.nodeMoveArea.convertToNodeSpaceAR(worldPos);
let rect = cc.rect(curNode.getPosition().x, curNode.getPosition().y - curNode.getContentSize().height, curNode.getContentSize().width, curNode.getContentSize().height);
let isInRect = rect.contains(nodePos);
if (isInRect) {
this.m_touchNode = curNode;
let heroData = HRDRoles[this.m_touchNode.name];
cc.log("m_touchNode id = " + heroData.name);
break;
}
}
}
}
private onTouchMove(event) {
}
private onTouchEnd(event) {
this.m_endPos = event.touch.getLocation();
let dis = this.m_startPos.sub(this.m_endPos).mag();
if (dis >= 30 && this.m_touchNode) {
cc.log("滑动了");
this.moveDirection(this.m_startPos, this.m_endPos);
}
}
private onTouchCancel(event) {
this.m_endPos = event.touch.getLocation();
let dis = this.m_startPos.sub(this.m_endPos).mag();
if (dis >= 30 && this.m_touchNode) {
cc.log("滑动了cancel");
this.moveDirection(this.m_startPos, this.m_endPos);
}
}
/**
* 判断滑动方向
* @param sPos
* @param ePos
*/
private moveDirection(sPos: cc.Vec2, ePos: cc.Vec2) {
let deltaX = Math.abs(ePos.x - sPos.x);
let deltaY = Math.abs(ePos.y - sPos.y);
if (deltaX >= deltaY) {
if (ePos.x > sPos.x) {
cc.log("### right ###");
this._moveRole(eDIR.RIGHT);
} else {
cc.log("### left ###");
this._moveRole(eDIR.LEFT);
}
} else {
if (ePos.y > sPos.y) {
cc.log("### up ###");
this._moveRole(eDIR.UP);
} else {
cc.log("### down ###");
this._moveRole(eDIR.DOWN);
}
}
}
private _moveRole(dir: number) {
if (!this.m_touchNode || !this.m_touchNode.isValid) {
cc.error("[HuaRongDao] _moveRole m_touchNode error.");
return;
}
switch (dir) {
case eDIR.UP:
this._moveUp();
break;
case eDIR.DOWN:
this._moveDown();
break;
case eDIR.LEFT:
this._moveLeft();
break;
case eDIR.RIGHT:
this._moveRight();
break;
}
}
private _moveUp() {
let roleData = HRDRoles[this.m_touchNode.name];
let roleRow = Math.floor(roleData.place / 10);
let roleCol = roleData.place % 10;
// 检查索引
let rowAndCol = this._posTranslateIndex(this.m_touchNode);
let row = rowAndCol.row;
let col = rowAndCol.col;
cc.log("[HuaRongDao] _moveUp row = " + row + " col = " + col);
// 检查是否可以移动
let canMoveUp = true;
if (row - 1 < 0) {
canMoveUp = false;
} else {
for (let i = col; i < col + roleRow; i++) {
if (this.placeArr[row - 1][i] != -1) {
cc.log("不能移动");
canMoveUp = false;
break;
}
}
}
if (canMoveUp) {
// 移动
let tarPos = cc.v2(this.m_touchNode.getPosition().x, this.m_touchNode.getPosition().y + eGridSideLength);
this.m_touchNode.runAction(cc.sequence(cc.moveTo(0.1, tarPos), cc.callFunc(() => {
this._checkVictory();
})));
// 数组数据更新
for (let i = col; i < col + roleRow; i++) {
this.placeArr[row - 1][i] = 1;
this.placeArr[row + roleCol - 1][i] = -1;
}
this._printDataArr();
}
}
private _moveDown() {
let roleData = HRDRoles[this.m_touchNode.name];
let roleRow = Math.floor(roleData.place / 10);
let roleCol = roleData.place % 10;
// 检查索引
let rowAndCol = this._posTranslateIndex(this.m_touchNode);
let row = rowAndCol.row;
let col = rowAndCol.col;
cc.log("[HuaRongDao] _moveDown row = " + row + " col = " + col);
// 检查是否可以移动
let canMoveDown = true;
if (row + 1 >= eROW) {
canMoveDown = false;
} else {
for (let i = col; i < col + roleRow; i++) {
if (this.placeArr[row + roleCol][i] != -1) {
cc.log("不能移动");
canMoveDown = false;
break;
}
}
}
if (canMoveDown) {
// 移动
let tarPos = cc.v2(this.m_touchNode.getPosition().x, this.m_touchNode.getPosition().y - eGridSideLength);
this.m_touchNode.runAction(cc.sequence(cc.moveTo(0.1, tarPos), cc.callFunc(() => {
this._checkVictory();
})));
// 数组数据更新
for (let i = col; i < col + roleRow; i++) {
this.placeArr[row + roleCol][i] = 1;
this.placeArr[row][i] = -1;
}
this._printDataArr();
}
}
private _moveLeft() {
let roleData = HRDRoles[this.m_touchNode.name];
let roleRow = Math.floor(roleData.place / 10);
let roleCol = roleData.place % 10;
// 检查索引
let rowAndCol = this._posTranslateIndex(this.m_touchNode);
let row = rowAndCol.row;
let col = rowAndCol.col;
cc.log("[HuaRongDao] _moveLeft row = " + row + " col = " + col);
// 检查是否可以移动
let canMoveLeft = true;
if (col - 1 < 0) {
canMoveLeft = false;
} else {
for (let i = row; i < row + roleCol; i++) {
if (this.placeArr[row + roleRow - 1][col - 1] != -1) {
cc.log("不能移动");
canMoveLeft = false;
break;
}
}
}
if (canMoveLeft) {
// 移动
let tarPos = cc.v2(this.m_touchNode.getPosition().x - eGridSideLength, this.m_touchNode.getPosition().y);
this.m_touchNode.runAction(cc.sequence(cc.moveTo(0.1, tarPos), cc.callFunc(() => {
this._checkVictory();
})));
// 数组数据更新
for (let i = row; i < row + roleCol; i++) {
this.placeArr[i][col - 1] = 1;
this.placeArr[i][col + roleRow - 1] = -1;
}
this._printDataArr();
}
}
private _moveRight() {
let roleData = HRDRoles[this.m_touchNode.name];
let roleRow = Math.floor(roleData.place / 10);
let roleCol = roleData.place % 10;
// 检查索引
let rowAndCol = this._posTranslateIndex(this.m_touchNode);
let row = rowAndCol.row;
let col = rowAndCol.col;
cc.log("[HuaRongDao] _moveRight row = " + row + " col = " + col);
// 检查是否可以移动
let canMoveRight = true;
if (col + 1 >= eCOL) {
canMoveRight = false;
} else {
for (let i = row; i < row + roleRow; i++) {
if (this.placeArr[i][col + 1] != -1) {
cc.log("不能移动");
canMoveRight = false;
break;
}
}
}
if (canMoveRight) {
// 移动
let tarPos = cc.v2(this.m_touchNode.getPosition().x + eGridSideLength, this.m_touchNode.getPosition().y);
this.m_touchNode.runAction(cc.sequence(cc.moveTo(0.1, tarPos), cc.callFunc(() => {
this._checkVictory();
})));
// 数组数据更新
for (let i = row; i < row + roleCol; i++) {
this.placeArr[i][col + 1] = 1;
this.placeArr[i][col - roleRow + 1] = -1;
}
this._printDataArr();
}
}
/**
* 判断游戏是否过关
*/
private _checkVictory(): boolean {
let pass = false;
// let roleNode = this.nodeMoveArea.getChildByName("1");
let roleNode = null;
for (let i = 0; i < this.nodeRoleArr.length; i++) {
let node = this.nodeRoleArr[i];
if (node.name == "1") {
roleNode = node;
}
}
if (roleNode && roleNode.isValid) {
let rowAndCol = this._posTranslateIndex(roleNode);
if (rowAndCol.row === 3 && rowAndCol.col === 1) {
pass = true;
}
}
if (pass) {
cc.log("恭喜过关");
this.nodeCong.active = true;
// 过关逻辑
}
return pass;
}
/**
* 辅助打印数组信息
*/
private _printDataArr() {
for (let i = 0; i < this.placeArr.length; i++) {
cc.log("[" + [this.placeArr[i][0]] + ", " + [this.placeArr[i][1]] + ", " + [this.placeArr[i][2]] + ", " + [this.placeArr[i][3]] + "]");
}
}
/**
* 通过position获取节点占用位置的索引
* 注意:这里的位置有可能是带很多小数点的浮点数,所以使用Math.round
* @param node
*/
private _posTranslateIndex(node: cc.Node) {
let rowAndCol: any = {};
for (let i = 0; i < eCOL; i++) {
for (let j = 0; j < eROW; j++) {
let xEquel = this.m_placeStartPos.x + i * eGridSideLength === Math.round(node.getPosition().x);
let yEquel = this.m_placeStartPos.y - j * eGridSideLength === Math.round(node.getPosition().y);
if (xEquel && yEquel) {
rowAndCol.row = j;
rowAndCol.col = i;
return rowAndCol;
}
}
}
cc.error("[HuaRongDao] _posTranslateIndex get wrong!!!");
return null;
}
/**
* 重置游戏数据
*/
private _resetData() {
this.m_placeStartPos = cc.v2(-this.nodeMoveArea.getContentSize().width / 2, this.nodeMoveArea.getContentSize().height / 2);
this.nodeRoleArr = [];
this.nodeMoveArea.removeAllChildren(true);
this.placeArr = [];
for (let i = 0; i < eROW; i++) {
let arr: Array = [];
for (let j = 0; j < eCOL; j++) {
arr.push(-1);
}
this.placeArr.push(arr);
}
}
/**
* 初始化关卡信息
* @param lv 关卡等级
*/
private _initLevel(lv: string) {
this._resetData();
let leveInfo = HRDLevels[lv];
let index = 0;
let roleConfig = null;
for (let i = 0; i < eROW; i++) {
for (let j = 0; j < eCOL; j++) {
index = i * eCOL + j;
let heroID = leveInfo[index];
if (heroID === 0 || this.placeArr[i][j] === 1) {
continue;
}
roleConfig = HRDRoles[heroID];
if (roleConfig) {
this._createSprite(heroID, cc.v2(this.m_placeStartPos.x + j * eGridSideLength, this.m_placeStartPos.y - i * eGridSideLength));
let place = roleConfig.place;
let front = Math.floor(place / 10);
let rear = place % 10;
for (let k = i; k < i + rear; k++) {
for (let m = j; m < j + front; m++) {
if (this.placeArr[k][m]) {
this.placeArr[k][m] = 1;
}
}
}
}
}
}
}
private _createSprite(id: string, pos: cc.Vec2) {
let roleData = HRDRoles[id];
if (roleData) {
let url = "huarongdao/" + roleData.spriteFrame;
cc.loader.loadRes(url, cc.SpriteFrame, (err, spriteFrame) => {
if (err) {
cc.error("[CCUtils] createSprite err = " + err);
return;
} else {
let node = new cc.Node(id);
node.setAnchorPoint(cc.v2(0, 1));
let sprite = node.addComponent(cc.Sprite);
sprite.spriteFrame = spriteFrame;
node.setPosition(pos);
this.nodeMoveArea.addChild(node);
this.nodeRoleArr.push(node);
}
});
}
}
public onBtnNextLevel(event: cc.Event, data: string) {
if (event.type === cc.Node.EventType.TOUCH_END) {
this._initLevel("2");
}
}
}
源码地址: https://gitee.com/jackxieff/Klotski.git