cocos 拼图思路

1.选择关卡:

利用点击事件的返回值event获取点击的坐标,用坐标转换为节点坐标来获取需要的坐标,通过点击坐标的所属象限选择四个象限的来进入对应关卡

 

    onTouchStart(event){//获取鼠标点击的位置
        if(this.lock){
            return ;
        }
        this.lock = true;
        // cc.log(event.getLocation());//获取的是世界坐标
        //获取点击的位置(基于世界坐标)
        let location = event.getLocation();
        //坐标系转换,转到节点坐标系里面来
        //this.你需要的节点.convertToNodeSpaceAR(你需要转换的坐标)
        //this.你需要的节点.convertToWorldSpaceAR(你需要转换的坐标)---将节点内坐标转到世界坐标上
        location = this.clover.convertToNodeSpaceAR(location);
        let res = this.getPos(location);
        let pos = res.pos;
        cc.vv.currentLevel = res.degree;//设置对应关卡等级
        //让星星移动
        cc.tween(this.star)
            .parallel(
                cc.tween().to(0.2,{position:pos}),
                cc.tween().by(0.2,{angle:72})
            )
            .call(()=>{//当前面动作都执行完毕以后才会调用这个回调函数,写法是箭头函数
                this.lock = false;
            })
            .start();
    },
    getPos(location){
        let res = {};
        let dis = this.clover.width / 4;//将移动的坐标规定出来
        if(location.x > 0 && location.y < 0){//第四象限
            res.pos = cc.v2(dis,-dis);
            res.degree = 4;
        }//第三象限
        else if(location.x < 0 && location.y < 0){
            res.pos = cc.v2(-dis,-dis);
            res.degree = 3;
        }//第二象限
        else if(location.x < 0 && location.y > 0){
            res.pos = cc.v2(-dis,dis);
            res.degree = 1;
        }
        else{   
            res.pos = cc.v2(dis,dis);
            res.degree = 2;
        }
        return res;
    },

2.游戏准备:

这里可以用layout来排布切分的图片块,也可以设置基准点来逐个设置拼图位置

    createCell() {
        //用盒子的大小除以行数或列数得到每个方块的大小
        let cellSize = this.answerSkin.node.width / this.line;
        let gap = (this.cellPar.width - this.answerSkin.node.width) / (this.line + 1);
        //保证放置方块的位置从左上角开始
        let basePos = {
            x: -this.cellPar.width / 2 + gap + cellSize / 2,
            y: this.cellPar.height / 2 - gap - cellSize / 2,
        };
        for (let i = 0; i < this.row; i++) {//行
            this.cellList[i] = [];//添加二维数组
            for (let j = 0; j < this.line; j++) {//列
                let cell = cc.instantiate(this.cellPrefab);//实例化预制体
                //调用预制体上的函数将参数传进去
                cell.getComponent("Cell").init(this.skin, [i, j], cellSize);
                //将预制体的宽高重新规定
                cell.width = cellSize;
                cell.height = cellSize;
                //设置预制体的位置
                cell.x = basePos.x + i * (gap + cellSize);
                cell.y = basePos.y - j * (gap + cellSize);
                this.cellPar.addChild(cell);
                this.cellList[i][j] = cell.getComponent("Cell");//方便调用各自脚本
                if (i === this.row - 1 && j === this.line - 1) {
                    cell.opacity = 0;
                    this.blackIndex = [i, j];
                }
            }
        }
    },

3.拼图移动:

首先打乱拼图,需要交换图片和数据层:

    //打乱拼图
    chaos() {
        //定义方向
        let dir = [[0, -1], [0, 1], [-1, 0], [1, 0]];//下上左右
        //打乱次数
        let count = 0;
        while (count < 100) {
            //随机一个方向
            let ran = Math.random() * dir.length | 0;
            //让下标随机变换--实际上是下标的变换方块并没有动
            let index = [this.blackIndex[0] + dir[ran][0], this.blackIndex[1] + dir[ran][1]];
            if (this.cross(index)) {//交换方块的条件满足之后才会进行交换
                //交换方块(传入的是黑块的下标和要去交换的下标)
                this.exchangeCell(this.blackIndex, index);
                //将黑块的坐标变为移动后的坐标
                this.blackIndex = index;
                count++;
            }
        }
    },
    //判断是否越界
    cross(index) {
        //行数不能超过定义的行数,并且行数要大于0,列数不能超过定义的列数,并且列数也要大于0
        return index[0] < this.row && index[0] >= 0 && index[1] < this.line && index[1] >= 0;
    },
    exchangeCell(blackIndex, index) {
        //黑块当前的位置(通过黑块在cellList里面的下标去拿到对应位置)
        let pos = this.cellList[blackIndex[0]][blackIndex[1]].node.getPosition();//getPosition可以拿到节点在父坐标系的位置
        //将黑块设置到要index所在的地方
        this.cellList[blackIndex[0]][blackIndex[1]].node.setPosition(this.cellList[index[0]][index[1]].node.getPosition());
        //将index所在的位置设置成原来黑块所在的位置
        this.cellList[index[0]][index[1]].node.setPosition(pos);
        //交换完图片交换cellList下标
        let obj = this.cellList[blackIndex[0]][blackIndex[1]];
        this.cellList[blackIndex[0]][blackIndex[1]] = this.cellList[index[0]][index[1]];
        this.cellList[index[0]][index[1]] = obj;
    },
    blackCellGoHome() {
        //当黑块不在最后一行的时候
        while (this.blackIndex[0] < this.row - 1) {
            //让黑块一直向下走直到最后一行为止
            this.exchangeCell(this.blackIndex, [this.blackIndex[0] + 1, this.blackIndex[1]]);
            this.blackIndex = [this.blackIndex[0] + 1, this.blackIndex[1]];
        }
        while (this.blackIndex[1] < this.line - 1) {
            //一直往右边走
            this.exchangeCell(this.blackIndex, [this.blackIndex[0], this.blackIndex[1] + 1]);
            this.blackIndex = [this.blackIndex[0], this.blackIndex[1] + 1];
        }
    },
    //判断点击的方块是不是和我想要的判断的方块相邻
    isNeighbor(index1, index2) {
        return (Math.abs(index1[0] - index2[0]) + Math.abs(index1[1] - index2[1]) === 1);
    },

    //开启点击事件后的逻辑
    onCellClicked(cell) {//这里的参数是为了知道点击的是哪个方块
        let audio = this.node.getComponent(cc.AudioSource);
        audio.play();//不需要名字因为只会播放组件上面有的
        //拿到当前点击方块的下标
        let currentIndex = this.getIndexByCell(cell);
        if (this.isNeighbor(this.blackIndex, currentIndex)) {
            this.exchangeCell(this.blackIndex, currentIndex);
            this.blackIndex = currentIndex;
            this.addStep();
            if (this.isGameWon()) {
                cc.vv.isGameOver = true;
                this.cellList[this.blackIndex[0]][this.blackIndex[1]].node.opacity = 255;
                this.showWonAnima();
                this.unschedule(this.addTime);
            }
        }
        else {
            //表示位置不相邻
            if (currentIndex != this.blackIndex) {
                cell.node.color = cc.color(255, 255, 255);
                cc.Tween.stopAllByTarget(cell.node);
                cc.tween(cell.node)
                    .to(0, { color: cc.color(255, 100, 100) })
                    .delay(0.2)
                    .to(0, { color: cc.color(255, 255, 255) })
                    .start();
            }
        }
        // let bool = this.isNeighbor(this.blackIndex,currentIndex);
        // cc.log(bool);
    },

因为通过点击获取的坐标比较难判断所在的方块,所以直接把点击事件放在生成的预制体里面,通过点击对应的预制体进行方块的交换

    onTouchStart(){
        cc.find("Canvas/gameLayer").getComponent("Game").onCellClicked(this);
    },

getPosition()可以拿到节点在父坐标系的位置

setPosition()可以设置位置

需要设置两个数组,一个存图片用来图片的交换一个存下标用来记录交换节点的下标相当于ID

获取下标(通过方块获取)遍历数组cellList里面的元素是否和这个cell===(相等)

只有需要移动的cell周遭有黑块才能移动交换(数据层也要跟着换)

拼图BFS思路:(还原)

1.创建一个State类来存状态(目标还原数组,父状态,当前坐标,移动方向,记录走过的路,黑块下标)

//越界判断
function checkCross(index) {
    return index[0] >= 0 && index[0] < global.row && index[1] >= 0 && index[1] < global.line;
};

//在语法层面,引用数据类型的深拷贝
function arrClone(arr) {
    return JSON.parse(JSON.stringify(arr));
};


class State {
    //包含的属性
    constructor(target, par, codeID, dir, recordList, blackIndex) {
        //目标  --->最终还原的数组
        this.target = target;
        //父状态
        this.par = par;
        //当前状态的唯一识别码,当前位置
        this.codeID = codeID
        //父状态到目前状态行走的方位
        this.dir = dir;
        //记录表---->记录走过的路   key:value
        this.recordList = recordList;
        //黑块下标
        this.blackIndex = blackIndex;
    };

    //以当前状态诞生子状态,获取所有可行的下一步  方法
    getChildren() {
        let children = [];
        //4个方向遍历尝试
        let dirList = [[1, 0], [-1, 0], [0, 1], [0, -1]];
        for (let i = 0; i < dirList.length; i++) {
            //获取方向
            let dir = dirList[i];
            //获取到下一步
            let nextIndex = [this.blackIndex[0] + dir[0], this.blackIndex[1] + dir[1]];
            //判断是否越界
            if (checkCross(nextIndex)) {
                //克隆数据,需要深拷贝
                let clone = arrClone(this.codeID);
                let temp = clone[nextIndex[0]][nextIndex[1]];
                clone[nextIndex[0]][nextIndex[1]] = clone[this.blackIndex[0]][this.blackIndex[1]];
                clone[this.blackIndex[0]][this.blackIndex[1]] = temp;
                //查看改变后的数据是否重复  不存在就记录
                if (!this.recordList.hasOwnProperty(JSON.stringify(clone))) {
                    this.recordList[JSON.stringify(clone)] = "";
                    //诞生子状态
                    let state = new State(this.target, this, clone, dir, this.recordList, nextIndex);
                    children.push(state);
                }

            }
        }
        return children
    }

}

//设置一个接口,对应的模块,对外暴露内容
module.exports = State;
//在语法层面,引用数据类型的深拷贝 但是对于vec类型会自动转换为object类型
function arrClone(arr) {
    return JSON.parse(JSON.stringify(arr));
};

2.创建一个专门放BFS算法的文件,里面只有function定义的方法

通过递归每次调用生成子状态,子状态会拥有父状态,通过子状态达到终点的情况倒退父节点,把路径塞入path,最后绘制路径

这里两个文件都是需要module.exports = 内容

在game脚本里面导入

import 名字 from "路径"

//检查数组是否完全一致
function checkIsSame(arr1, arr2) {
    return JSON.stringify(arr1) === JSON.stringify(arr2);
}



function search(start) {
    let path = [];

    //每一层的容器
    let stateList = [start];
    //终点
    let destination = null;

    f(stateList);
    //核心逻辑,递归到终点为止
    function f(list){
        //获取所有的子状态
        let allChildren = [];
        for(let i = 0;i < list.length;i++){
            let children = list[i].getChildren();
            children.forEach(e => {
                allChildren.push(e);
            });
        }

        //判断子节点中是否存在终点
        for(let j = 0;j < allChildren.length;j++){
            if(checkIsSame(allChildren[j].target,allChildren[j].codeID)){
                destination = allChildren[j];
            }
        }
        // cc.log(destination);
        //检查是否找到了终点
        if(destination){
            cc.log("找到啦");
            //找到则绘制路径
            drawPath();
        }else{
            f(allChildren);
        }
    }

    //绘制还原路径
    function drawPath(){
        let p = destination;
        while(p.par){
            path.unshift(p.dir);
            p = p.par;
        }
    }

    return path;
}
module.exports = search;

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Cocos分层是指在Cocos游戏引擎中,将游戏场景或游戏对象按照不同的层级进行管理和渲染的一种技术。通过分层可以实现游戏元素的层次感和深度感,同时也方便了游戏对象的管理和控制。 在Cocos中,可以通过以下方式进行分层: 1. 场景层(Scene Layer):场景层是最底层的分层,用于管理整个游戏场景。一个游戏通常包含多个场景,每个场景都有自己的层级结构。场景层可以包含多个其他类型的层,如UI层、游戏对象层等。 2. UI层(UI Layer):UI层用于管理游戏中的用户界面元素,如按钮、标签、进度条等。UI层通常位于最上方,不受游戏对象的渲染顺序影响。通过UI层可以实现游戏中的菜单、设置界面等功能。 3. 游戏对象层(Game Object Layer):游戏对象层用于管理游戏中的各种游戏对象,如角色、敌人、道具等。游戏对象层可以根据需要创建多个,每个对象层可以控制不同类型的游戏对象的渲染顺序和行为。 4. 特效层(Effect Layer):特效层用于管理游戏中的特效效果,如爆炸、闪电等。特效层通常位于游戏对象层之上,可以通过特效层实现游戏中的动态效果和视觉冲击。 5. 背景层(Background Layer):背景层用于管理游戏中的背景元素,如地图、背景图片等。背景层通常位于最底部,用于呈现游戏的背景环境。 以上是Cocos分层的一些常见方式,通过合理的分层可以提高游戏的性能和可维护性,同时也能够更好地控制游戏元素的显示和交互。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

永远的毅嘉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值