请添加图片描述预览

AStarMove:

import AStar from "./AStar";


const { ccclass, property } = cc._decorator;

@ccclass
export default class AStarMove extends cc.Component {
    @property(cc.Node)
    mapNode: cc.Node = null;
    @property(AStar)
    aStar: AStar = null;
    @property(cc.Node)
    player: cc.Node = null;
    @property(cc.Node)
    camera: cc.Node = null;//和mapNode需要为同一父节点

    start() {
        this.mapNode.on(cc.Node.EventType.TOUCH_END, this.onTouchEnd, this);
        this.mapNode.on(cc.Node.EventType.TOUCH_CANCEL, this.onTouchEnd, this);
    }

    onTouchEnd(event) {
        this.aStar.graphicsClear();
        this.aStar.drawBlock();
        this.player.stopAllActions();
        let oPos = event.getLocation();

        let ws = this.mapNode.convertToNodeSpaceAR(oPos);
        let touchPos = this.mapNode.convertToNodeSpaceAR(oPos);
        touchPos.x += this.camera.x;
        touchPos.y += this.camera.y;
        let lastPos = this.player.position;
        let startpos = this.aStar.getColRow(lastPos);
        let endpos = this.aStar.getColRow(touchPos);
        this.aStar.draw(startpos.x, startpos.y, cc.Color.YELLOW);
        this.aStar.draw(endpos.x, endpos.y, cc.Color.GREEN);
        // 开始寻路
        let path = this.aStar.findPath(startpos, endpos);
        if (!path) return;
        let tween = cc.tween(this.player);
        let speed = 600;
        let moveCount = 0
        for (let i = path.length - 2; i >= 0; i--) {
            const element = path[i];
            // let type = this.aStar.getMap(element.x, element.y)
            if (element.type == -1) break;
            let data = this.aStar.getMap(element.x, element.y)
            if (data == -1 || data.type == -1) continue
            let pos = this.aStar.getPos(cc.v2(element.x + 0.5, element.y + 0.5))
            let dis = lastPos.sub(pos).mag();
            let t = dis / speed;
            let tween2 = cc.tween().to(t, { position: pos });
            tween.then(tween2);
            lastPos = pos;
            moveCount++
        }
        if (moveCount) tween.start()
    }



    update(dt) {
        this.player.zIndex = -this.player.y + cc.winSize.height
        // let scale = 2 - 1.5 * Math.abs(this.player.y + cc.winSize.height) / cc.winSize.height
        // if (this.needScale) {
        //     this.player.scale = scale
        //     this.player.scaleX = this.playerScaleX * scale
        // }
    }
}

AStar:




const { ccclass, property } = cc._decorator;

@ccclass
export default class AStar extends cc.Component {
    @property(cc.Node)
    mapNode: cc.Node = null;//整个地图
    @property(cc.Graphics)
    mapGraphics: cc.Graphics = null;
    /**障碍物根节点 */
    @property(cc.Node)
    itemBlockRoot: cc.Node = null;

    _gridW = 50;   // 单元格子宽度
    _gridH = 50;   // 单元格子高度
    
    boxAngel = 30;//地图菱形,根据ui素材调整


    mapH = 13;     // 纵向格子数量
    mapW = 25;     // 横向格子数量

    is8dir = true; // 是否8方向寻路
    gridsList = null

    onLoad() {
        this.creatMap(this.mapNode, this._gridW, this._gridH)
    }

    creatMap(mapNode, gridW, gridH) {
        const manager = cc.director.getCollisionManager();
        manager.enabled = true;
        manager.enabledDebugDraw = true;
        manager.enabledDrawBoundingBox = true;

        this.mapH = Math.round(mapNode.height / gridH);
        this.mapW = Math.round(mapNode.width / gridW);
        this.initMap();
        this.initBlock()
    }

    graphicsClear() {
        this.mapGraphics.clear()
    }

    initBlock() {
        this.itemBlockRoot.children.forEach((value) => {
            let collider = value.getComponent(cc.PolygonCollider)
            if (!collider) return
            let points = collider.points;

            for (let col = 0; col <= this.mapW; col++) {
                for (let row = 0; row <= this.mapH; row++) {
                    let oPos = this.getPos(cc.v2(col + 0.5, row + 0.5))
                    let w_pos = this.mapNode.convertToWorldSpaceAR(oPos);
                    let pos = value.convertToNodeSpaceAR(w_pos);
                    let isIn = cc.Intersection.pointInPolygon(cc.v2(pos), points)
                    if (isIn) {
                        this.addBlock(cc.v2(col, row))
                    }
                }
            }
        })
        this.drawBlock()
    }

    getMap(i, j) {
        if (!this.gridsList || !this.gridsList[i] || !cc.isValid(this.gridsList[i][j])) return -1
        return this.gridsList[i][j]
    }

    initMap() {
        // 初始化格子二维数组
        this.gridsList = new Array(this.mapW + 1);
        for (let col = 0; col < this.gridsList.length; col++) {
            this.gridsList[col] = new Array(this.mapH + 1);
        }

        this.mapGraphics.clear();
        for (let col = 0; col <= this.mapW; col++) {
            for (let row = 0; row <= this.mapH; row++) {
                this.addGrid(col, row, 0);
            }
        }
        cc.log(this.gridsList)
    }

    drawBlock() {
        for (let col = 0; col <= this.mapW + 1; col++) {
            this.drawLine(cc.v2(col, 0), cc.v2(col, this.mapH + 1));
        }
        for (let row = 0; row <= this.mapH + 1; row++) {
            this.drawLine(cc.v2(0, row), cc.v2(this.mapW + 1, row));
        }
        for (let col = 0; col <= this.mapW; col++) {
            for (let row = 0; row <= this.mapH; row++) {
                if (this.gridsList[col][row] == -1) {
                    this.draw(col, row, cc.Color.RED);
                }
            }
        }
    }

    addBlock(newPos) {
        const { x, y } = newPos;
        if (!this.gridsList[x] || !cc.isValid(this.gridsList[x][y])) return
        if (this.gridsList[x][y] == 0) {
            this.gridsList[x][y] = -1;
        }
    }

    checkBlock(newPos) {
        const { x, y } = newPos;
        if (!this.gridsList[x] || !cc.isValid(this.gridsList[x][y])) return
        return this.gridsList[x][y] == -1
    }

    addGrid(x, y, type) {
        // -1障碍物, 0正常, 1起点, 2目的点
        this.gridsList[x][y] = type;
    }

    _sortFunc(x, y) {
        let a = x.f || 0
        let b = y.f || 0
        return a - b;
    }

    generatePath(grid) {
        let path = []
        path.push(grid);
        while (grid.parent) {
            grid = grid.parent;
            path.push(grid);
        }
        cc.log("path.length: " + path.length, path);
        for (let i = 0; i < path.length; i++) {
            // 起点终点不覆盖,方便看效果
            let grid = path[i];
            if (i == 0) {
                this.draw(grid.x, grid.y, cc.Color.ORANGE);
            }
            else if (i == path.length - 1) {
                this.draw(grid.x, grid.y, cc.Color.BLUE);
            }
            else {
                this.draw(grid.x, grid.y, cc.Color.GREEN);
            }
        }
        return path;
    }

    findPath(startPos, endPos, gridsArr = this.gridsList) {
        let openList = [];
        let closeList = [];
        let gridsList = this.crearArr(gridsArr)
        let startGrid = gridsList[startPos.x][startPos.y];
        let endGrid = gridsList[endPos.x][endPos.y];
        endGrid.type = 2;

        openList.push(startGrid);
        let curGrid = openList[0];
        while (openList.length > 0 && curGrid.type != 2) {
            // 每次都取出f值最小的节点进行查找
            curGrid = openList[0];
            if (curGrid.type == 2) {
                cc.log("find path success.");
                endGrid.type = 0;
                return this.generatePath(curGrid);
            }
            this.findPathCorrect(curGrid, gridsList, closeList, endPos, openList, this.is8dir)
            // 遍历完四周节点后把当前节点加入关闭列表
            closeList.push(curGrid);
            // 从开放列表把当前节点移除
            openList.splice(openList.indexOf(curGrid), 1);
            if (openList.length <= 0) {
                cc.log("find path failed.");
                break;
            }
            // 重新按照f值排序(升序排列)
            openList.sort(this._sortFunc);
        }
        //无视障碍查找
        openList = [];
        closeList = [];
        gridsList = this.crearArr(gridsArr)
        openList.push(startGrid);
        startGrid = gridsList[startPos.x][startPos.y];
        endGrid = gridsList[endPos.x][endPos.y];
        endGrid.type = 2;
        while (openList.length > 0 && curGrid.type != 2) {
            // 每次都取出f值最小的节点进行查找
            curGrid = openList[0];
            if (curGrid.type == 2) {
                cc.log("find path success.");
                endGrid.type = 0;
                return this.generatePath(curGrid);
            }
            this.findPathCorrect(curGrid, gridsList, closeList, endPos, openList, false, false)
            // 遍历完四周节点后把当前节点加入关闭列表
            closeList.push(curGrid);
            // 从开放列表把当前节点移除
            openList.splice(openList.indexOf(curGrid), 1);
            if (openList.length <= 0) {
                cc.log("find path failed.");
                break;
            }
            // 重新按照f值排序(升序排列)
            openList.sort(this._sortFunc);
        }
        return false;
    }

    crearArr(gridsArr) {
        let gridsList = []
        for (let i = 0; i < gridsArr.length; i++) {
            gridsList[i] = [];
            for (let j = 0; j < gridsArr[i].length; j++) {
                let grid = {
                    x: i,
                    y: j,
                    type: (gridsArr[i][j] + "")// -1障碍物, 0正常, 1起点, 2目的点
                }
                gridsList[i][j] = grid
            }
        }
        return gridsList
    }

    findHave(data, list) {
        let index = list.findIndex((value) => { return value.x == data.x && data.y == value.y })
        return index == -1
    }

    findPathCorrect(curGrid, gridsList, closeList, endPos, openList, is8dir = true, checkBlock = true) {
        for (let i = -1; i <= 1; i++) {
            for (let j = -1; j <= 1; j++) {
                if (i != 0 || j != 0) {
                    let col = curGrid.x + i;
                    let row = curGrid.y + j;
                    if (col >= 0 && row >= 0 && col <= this.mapW && row <= this.mapH
                        && (!checkBlock || (checkBlock && gridsList[col][row].type != -1))
                        && this.findHave(gridsList[col][row], closeList)) {
                        if (is8dir) {
                            // 8方向 斜向走动时要考虑相邻的是不是障碍物
                            if (gridsList[col - i][row].type == -1 || gridsList[col][row - j].type == -1) {
                                if (checkBlock) continue;
                            }
                        } else {
                            // 四方形行走
                            if (Math.abs(i) == Math.abs(j)) {
                                continue;
                            }
                        }
                        let g = curGrid.g || 0
                        // 计算g值
                        g = g + Math.sqrt(Math.pow(i * 10, 2) + Math.pow(j * 10, 2));
                        if (gridsList[col][row].type == -1) {
                            g += 100;//如果是不可走,排位放后
                        }
                        if (!gridsList[col][row].g || gridsList[col][row].g > g) {
                            gridsList[col][row].g = g;
                            // 更新父节点
                            gridsList[col][row].parent = curGrid;
                        }
                        // 计算h值 manhattan估算法
                        gridsList[col][row].h = Math.abs(endPos.x - col) + Math.abs(endPos.y - row);
                        if (gridsList[col][row].type == -1) {
                            gridsList[col][row].h += 100;//如果是不可走,排位放后
                        }
                        // 更新f值
                        gridsList[col][row].f = gridsList[col][row].g + gridsList[col][row].h;
                        // 如果不在开放列表里则添加到开放列表里
                        if (this.findHave(gridsList[col][row], openList)) {
                            openList.push(gridsList[col][row]);
                        }
                        // // 重新按照f值排序(升序排列)
                        // openList.sort(this._sortFunc);
                    }
                }
            }
        }
    }

    getPos(pos) {
        const { x, y } = pos
        let posX = (x - this.mapW / 2) * (this._gridW);
        let posY = (y - this.mapH / 2) * (this._gridH);
        return cc.v3(posX, posY)
    }

    getColRow(pos) {
        let x = Math.floor(pos.x / (this._gridW) + this.mapW / 2);
        let y = Math.floor(pos.y / (this._gridH) + this.mapH / 2);
        return cc.v3(x, y)
    }

    drawLine(start_col_cow, end_col_cow, color = cc.Color.GRAY, width = 4, a = 100) {
        this.mapGraphics.strokeColor = color;
        this.mapGraphics.strokeColor.a = a;//添加透明度
        let pos1 = this.getPos(start_col_cow)
        let pos2 = this.getPos(end_col_cow)
        this.mapGraphics.lineWidth = width;
        this.mapGraphics.moveTo(pos1.x, pos1.y);
        this.mapGraphics.lineTo(pos2.x, pos2.y);
        this.mapGraphics.stroke();
    }

    draw(col, row, color = new cc.Color().fromHEX("#FF000033"), a = 100) {
        this.mapGraphics.fillColor = color;
        this.mapGraphics.fillColor.a = a;//添加透明度
        let pos = this.getPos(cc.v2(col, row))
        this.mapGraphics.fillRect(pos.x, pos.y, this._gridW, this._gridH);
    }
}


SceneCtrl2D


const { ccclass, property } = cc._decorator;

@ccclass
export default class SceneCtrl2D extends cc.Component {

    @property(cc.Node)
    moveSide: cc.Node = null;
    @property(cc.Node)
    player: cc.Node = null;
    @property(cc.Camera)
    camera: cc.Camera = null;
    @property(cc.Node)
    itemRoot: cc.Node = null;

    onLoad() {
    }

    start() {
        let ws = this.player.convertToWorldSpaceAR(cc.v2(0, 0))
        let pos = this.moveSide.parent.convertToNodeSpaceAR(ws)
        this.scheduleOnce(() => {
            this.camera.node.setPosition(pos);
        })
        this.itemRoot.children.forEach(element => {
            element.zIndex = -element.y + cc.winSize.height
        });
    }

    lateUpdate(dt) {
        let ws = this.player.convertToWorldSpaceAR(cc.v2(0, 0))
        let pos = this.moveSide.parent.convertToNodeSpaceAR(ws)
        // let dis = pos.sub(cc.v2(this.moveSide.position)).mag()
        let disX = cc.v2(pos.x, 0).sub(cc.v2(this.moveSide.position.x, 0)).mag()
        let disY = cc.v2(pos.y, 0).sub(cc.v2(this.moveSide.position.y, 0)).mag()

        if (pos.x <= this.moveSide.x - this.moveSide.width / 2 + cc.winSize.width / 2) {
            pos.x = this.moveSide.x - this.moveSide.width / 2 + cc.winSize.width / 2
            this.camera.node.x = pos.x;
        }
        else if (pos.x >= this.moveSide.x + this.moveSide.width / 2 - cc.winSize.width / 2) {
            pos.x = this.moveSide.x + this.moveSide.width / 2 - cc.winSize.width / 2
            this.camera.node.x = pos.x;
        }
        else {
            this.camera.node.x = pos.x;
            if (disX < 1) {
                this.camera.node.x = pos.x;
            }
            else if (this.camera.node.x < pos.x) {
                this.camera.node.x += Math.max(1, 10 / disX)
            }
            else {
                this.camera.node.x -= Math.max(1, 10 / disX)
            }
        }

        if (pos.y <= this.moveSide.y - this.moveSide.height / 2 + cc.winSize.height / 2) {
            pos.y = this.moveSide.y - this.moveSide.height / 2 + cc.winSize.height / 2
            this.camera.node.y = pos.y;
        }
        else if (pos.y >= this.moveSide.y + this.moveSide.height / 2 - cc.winSize.height / 2) {
            pos.y = this.moveSide.y + this.moveSide.height / 2 - cc.winSize.height / 2
            this.camera.node.y = pos.y;
        }
        else {
            this.camera.node.y = pos.y;
            if (disY < 1) {
                this.camera.node.y = pos.y;
            }
            else if (this.camera.node.y < pos.y) {
                this.camera.node.y += Math.max(1, 10 / disY)
            }
            else {
                this.camera.node.y -= Math.max(1, 10 / disY)
            }
        }
    }

}

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐