Cocos Creator 华容道

2023-11-02

环境:cocos creator 2.0.10 + Mac环境 + TypeScript   (文末附源码链接)

本文提供了游戏玩法的逻辑,支持关卡配置(可以自由配置关卡)

先看一下效果:

 

首先,我们看一下我们要处理的问题:

1. 区域划分

2. 角色摆放(即:关卡元素摆放)

3. 如何移动 (滑动处理,能否移动)

4. 是否过关

把游戏里的点,有条不紊的划分出来,并一一击破,这样实现起来才会更加轻松自如。

 

代码并不复杂,应该很容易解读

Configs.ts  

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"
    }

};

Levels.ts

关卡配置信息,每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],
};

HuaRongDao.ts

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<cc.Node> = [];

    // 左上角开始放置位置
    m_placeStartPos: cc.Vec2 = cc.v2(0, 0);

    placeArr: Array<any> = [];

    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<number> = [];
            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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Cocos Creator 华容道 的相关文章

随机推荐

  • python 深浅拷贝 赋值

    本质上和语言无关 是三种复制对象的方式 数据在物理地址中的内容 这个物理地址需要用地址寻址 那么复制就有多种办法 例如使两个变量指向同一个地址 或是创建一份新的物理地址存储同样内容 深拷贝 对象的本质是 名称 地址 引用 编程时用名称代替
  • fmp4打包H265视频流

    1 fmp4打包H265视频流 文章目录 1 fmp4打包H265视频流 1 1 码流存储和传输格式介绍 1 1 1 Annex B封装格式 1 1 2 AVCC封装格式 1 1 2 HVCC封装格式 1 2 fmp4封装H265 1 2
  • python相对路径找不到文件_Python 解决相对路径问题:"No such file or directory"

    如果你取相对路径不是在主文件里 可能就会有相对路径问题 No such file or directory 因为 python 的相对路径 相对的都是主文件 如下目录结构 main py conf py start png config t
  • css实现文本超出容器部分显示省略号

    多行 overflow hidden display webkit box webkit line clamp 2 超出行数 webkit box orient vertical 单行 overflow hidden text overfl
  • 抖音壁纸小程序,星光壁纸小程序2.0版本,升级版

    抖音壁纸星光壁纸小程序2 0版本 升级版 引入超多功能 源码获取方式见抖音 星光壁纸 小程序 我的页面 抖音壁纸用户端 以下是创作者系统端
  • excel_IF函数

    IF AND H4 XXXX H5 XXXX H6 XXXXX 1 0 如果满足H4并且满足H5 H6 就为1 否则为0
  • 图遍历

    第七章 图 7 14 Status Build AdjList ALGraph G 输入有向图的顶点数 边数 顶点信息和边的信息建立邻接表 InitALGraph G scanf d v if v lt 0 return ERROR 顶点数
  • 产生死锁的四个必要条件

    1 互斥条件 一个资源每次只能被一个进程使用 2 请求与保持条件 一个进程因请求资源而阻塞时 对已获得资源保持不放 3 不剥夺条件 进程已获得的资源 在未使用完之前 不能强行剥夺 4 循环等待条件 若干进程之间形成一种头尾相接的循环等待资源
  • [Linux用户空间编程-5]:用IPTable实现NAT功能

    作者主页 文火冰糖的硅基工坊 文火冰糖 王文兵 的博客 文火冰糖的硅基工坊 CSDN博客 本文网址 https blog csdn net HiWangWenBing article details 123427800 目录 前言 第1章
  • 5个高清视频素材网站,免费商用,建议收藏~

    1 Mixkit https mixkit co 站内有非常多的高清视频素材 音频素材 视频全部都分类好了 像自然 航拍 科技 人物等等这里都能找到 还有很多视频模板 达芬奇模板全部都是免费 可商用 但每天有限制下载次数 素材的授权方式也不
  • 2022春招前端最新面试题分享(诺亚财富)

    诺亚财富面经 公司及岗位信息 公司 诺亚财富 岗位 前端开发工程师 地点 上海 薪资 10k 15k 14薪 面试结果 三轮技术面全部通过 约了HR面了 一面 2022 04 21 自我介绍 为什么不在实习公司转正 JS常见数据类型 基本类
  • windows Ubuntu loongnix 网络通信

    当我们进行Linux应用开发时 我们面临的首要问题便是如何让windows ubuntu和开发板三者完成通信 以便于代码文件的传输 1 windows和ubuntu 的通信 首先我们在建立ubuntu虚拟机时将其网络适配器模式配置为NAT
  • To create POPUP menu

    To create popupmenu Create new menu as follows Select popup opion Click on define transfer Assign any menu component pag
  • 飞猪单品详情采集

    api detail f ret SUCCESS 调用成功 v 1 0 data sold data sold 月销 5647 笔 soldCount 5647 tag sold pageContainer children detailC
  • python(chapter02)

    字符串 字符串 pystr python iscool is cool print pystr 0 使用切片法来进行字符串的切割 索引是2 到 5 的字符串 print pystr 2 5 索引是0到2的 print iscool 2 索引
  • ChatGLM-6B的安装和使用最全面细节讲解

    ChatGLM 6B是一个基于General Language Model GLM 架构的开源对话语言模型 支持中英双语 该模型使用了和ChatGPT类似的技术进行优化 经过1T标识符的中英双语训练 同时辅以监督微调 反馈自助和人类反馈强化
  • 【Solidity】Storage和memory关键字的区别和用法

    在 Solidity 中 有两个地方可以存储变量 storage以及memory Storage 变量是指永久存储在区块链中的变量 Memory 变量则是临时的 当外部函数对某合约调用完成时 内存型变量即被移除 状态变量 在函数之外声明的变
  • Work Tips

    目录 IAR环境下 使用Live Watch实时监控变量值 IAR替换mcu库 Linux中删除swp文件 使用J Link通过SWD接口直接下载 hex文件和 bin文件到MCU 下载J Flash软件 linux设置时间命令 Jlink
  • chatgpt综述和报告

    ChatGPT究竟强在哪 复旦大学邱锡鹏教授 大型语言模型的能力分析与应用 哔哩哔哩 bilibili2022年底 美国OpenA1公司发布了ChatGPT 一个可以与人类对话交互的千亿规模参数的大型语言模型 它可以根据用户输入的指令完成各
  • Cocos Creator 华容道

    环境 cocos creator 2 0 10 Mac环境 TypeScript 文末附源码链接 本文提供了游戏玩法的逻辑 支持关卡配置 可以自由配置关卡 先看一下效果 首先 我们看一下我们要处理的问题 1 区域划分 2 角色摆放 即 关卡