cocos creator2X生成网格进行寻路

摘要

在寻路算法中我们一般使用网格来进行搜索判断,在没接触时自己也好奇怎么生成地图下的网格,我相信也有很多朋友存在好奇,这里简单分享下个人的解决方案,有错误还希望大佬们多多指导。DOME地址

版本说明

使用 CocosCreator 的 2.4.3 版本。

开始

效果演示:

红色代表一个正方形的中心点

蓝色表示该正方形区域为障碍物
https://www.zhita.wang/2022/01/07/generate-gridding/a1.png

逻辑梳理:

  1. 设置地图节点大小,设置锚点位于左下角(0,0)位置
  2. 获取单个正方形的边长大小
  3. 获取节点下的障碍物并得到具体的行数、列数方便生成布局时标记为改点为障碍物
  4. 生成布局

一、设置地图等:

1.我们在Canvas下新建一个map节点,需要给map节点设置对应的宽度高度,该节点的大小就代表地图的大小,这里我使用的是960 640的画布。

2.并且需要把map节点的Anchor锚点设置到左下角(0,0),使map节点位于第一象限中,这样做的原因是方便计算正方形的大小、位置。

3.正方形预制体节点

二、获取正方形的边长大小:

1.获取当前地图map节点的大小,计算需要生成正方形的大小、需要生成多少。

计算逻辑为,初始设定一个正方形的大小,循环通过高度 宽度 / 正方形大小 得到一个可以被整除的最终正方形大小。

列:画布宽度为 960 高度为 640 初始定义正方形大小为 30

  1. 960 / 30 = 32 640 / 30 = 21.33333333333333 不满足整除条件 使默认正方形大小 +1

  2. 960 / 31 = 30.96774193548387 640 / 31 = 20.64516129032258 不满足整除条件 使默认正方形大小 +1

  3. 960 / 32 = 30 640 / 32 = 20 条件满足故正方形大小为 32

    /**
     * 计算正方形高度,当前this.node表示map节点
     */
    calculateSquareHeight (){
        // 地图的高度
        let _mapHeight = this.node.height;
        // 地图的宽度
        let _mapWidth = this.node.width;
        // 高度 这里我们定义一个初始的大小,通过程序去计算得到合适的正方形大小
        let _alt = 30;
        // 是否满足条件
        let _isEnd = true;
        // 得到指定的正方形的高度,以及需要的数量
        while (_isEnd) {
            // 行数
            let _longNum = _mapHeight / _alt;
            // 列数
            let _widthNum = _mapWidth / _alt;
            if(_longNum % 1 === 0 && _widthNum % 1 ===0){
                _isEnd = false;
                // 得到每个格子大小
                this.altSize = _alt;
                // 得到需要多少列
                this.longNum = _longNum;
                // 得到需要多少行
                this.widthNum = _widthNum;
                // 计算障碍物所在位置
                this.mapForObstacle();
            } else {
                _alt++;
            }
        }
    }

三、获取节点下的障碍物:

1.如果地图中存在障碍物,我们需要通过设置group把障碍物的分组设置为’MAP_OBSTACLE’,然后进行循环获取每个节点计算当前节点所在的行数、列数并存入Map方便生成布局时判断。

	/**
     * 拿到指定节点下的障碍物位置、大小
     * this.altSize 表示第二步时获取到的正方形大小
     */
    mapForObstacle (){
        // 在map节点下新建了level节点并在level节点下放入了障碍物节点
        let a = 'Canvas/map/level'
        let mapNode = cc.find(a);
        // 循环
        mapNode.children.forEach((r)=>{
            // 判断当前是否为障碍物节点
            if(r.group === 'MAP_OBSTACLE'){
                // 障碍物宽度的半径
                let _widthRadius = r.width - r.width / 2;
                // 障碍物高度的半径
                let _heightRadius = r.height - r.height / 2;
                // 得到障碍物左下角x y位置,方便计算具体位置
                let _xRange = r.x - _widthRadius;
                let _yRange = r.y - _heightRadius;
                // 计算从障碍物左边开始到右边的具体列数
                let _columnLeft = Math.ceil(_xRange / this.altSize);
                let _columnRight = Math.ceil((r.x + _widthRadius) / this.altSize);
                let _a = [];
                for(let i = 0,len = _columnRight -_columnLeft;i<=len;i++){
                    _a[i] = _columnLeft + i;
                };
                // 得到从下到上的行数
                let _lineDow = Math.ceil(_yRange / this.altSize);
                let _lineUp  = Math.ceil((r.y + _heightRadius) / this.altSize);
                let _b = [];
                for(let i = 0,len = _lineUp -_lineDow;i<=len;i++){
                    _b[i] = _lineDow + i;
                };
                // 进行 行数存储
                _b.forEach((r)=>{
                    let _obsGet = this.obstacleMap.get(r);
                    if(_obsGet){
                        let _arr1 = [..._a,..._obsGet];
                        let _arr = Array.from(new Set(_arr1));
                        this.obstacleMap.set(r,_arr);

                    } else {
                        this.obstacleMap.set(r,_a);
                    }
                });
            
            }
        })
        // 开始布局
        this.generateLayout(this.altSize,this.longNum,this.widthNum);
    }

四、生成布局:

1.根据已经得到的数据进行网格的生成

	/**
     * 生成布局
     * @param _initSquareHeight 正方形瓦块高度
     * @param _initLineNum      初始行数 需要用到的行数
     * @param _initColumnNum    初始列数 需要用到的列数
     */
    generateLayout (_initSquareHeight:number,_initLineNum:number,_initColumnNum:number){
        // 初始行数
        let _lineNum = 1;
        // 初始列数
        let _columnNum = 1;
        // 因为坐标位于中间 0.5 0.5 所以需要根据高度除以 2 得到半径 即位置
        let _radii = _initSquareHeight / 2;
        let _isEnd = true;
        let _arr = [];
        while (_isEnd) {
            // 计算位置 
            let _x = _columnNum === 1 ? _columnNum *_radii : (_columnNum-1) * _initSquareHeight + _radii;
            let _y = _lineNum === 1 ? _lineNum *_radii : (_lineNum-1) * _initSquareHeight + _radii;
            // 判断是否在障碍区域r
            let _isObsLine = this.obstacleMap.get(_lineNum);
            let _color = new cc.Color(255,0,0);
            let _type = 0;
            if(_isObsLine && _isObsLine.indexOf(_columnNum) >= 0){
                 _color = new cc.Color(71,53,234);
                 _type = 1;
            }
            // 生成节点this.ItemPrefab表示挂载的预制体
            let _node = cc.instantiate(this.ItemPrefab);
            _node.width = _initSquareHeight;
            _node.height = _initSquareHeight;
            _node.children[0].color = _color;
            _node.setPosition(cc.v2(_x,_y));
            // 设置父节点 this.ItemNode表示要生成几点的父节点
            _node.parent = this.ItemNode;
            _arr.push({type:_type,node:_node});
            // 判断是否到达列数
            if(_lineNum === _initLineNum && _columnNum === _initColumnNum){
                // 表示结束
                _isEnd = false;
                _arr = [];
            } else if(_columnNum === _initColumnNum){
                // 初始从1开始
                _columnNum = 1;
                // 行数增加
                _lineNum++;
                _arr = [];
            } else {
                _columnNum++;
            }
        }
        
    }

结尾:

暂时性更新,写这写这写不下去了…