简单四方向a*学习记录10 重写前面的所有逻辑

因为前面的没有开放列表 关闭列表 只是个贪心算法 现在重新写

A*寻路算法(四方向移动)完整步骤

  1. 初始化数据结构
    开启列表:一个优先队列(按F值从小到大排列),存放待检查的节点
    关闭列表:一个集合或字典,存放已检查过的节点
    将起点加入开启列表,设置起点的:
    G值 = 0(从起点到该点的实际代价)
    H值 = 估计到终点的距离
    F值 = G + H
    父节点 = null
  2. 主循环(处理开启列表)
    WHILE 开启列表不为空:

取出F值最小的节点作为当前节点

将当前节点移到关闭列表

检查是否到达终点

如果是当前节点 == 终点:

回溯路径(通过父节点指针从终点回到起点)

返回找到的路径

遍历四个邻居(上下左右)

邻居节点坐标 = 当前节点坐标 + (0,1), (0,-1), (1,0), (-1,0)

对每个邻居节点检查:

是否在地图范围内?→ 如果否,跳过

是否是障碍物(墙)?→ 如果否,跳过

是否在关闭列表中?→ 如果否,跳过

为有效邻居节点计算代价值:

G_temp = 当前节点的G值 + 1(四方向,每次移动代价为1)

H值 = |邻居x - 终点x| + |邻居y - 终点y|(曼哈顿距离,四方向专用)

F_temp = G_temp + H

判断邻居是否在开启列表中:

如果不在:

创建邻居节点,设父节点 = 当前节点

设G值 = G_temp,F值 = F_temp

加入开启列表

如果已在开启列表:

比较G_temp与原来的G值

如果G_temp < 原G值(找到更优路径):

更新该节点的G值 = G_temp,F值 = F_temp

更新父节点 = 当前节点

  1. 循环结束

如果开启列表为空且没到达终点 → 路径不存在

返回"无路径"或空路径

go 复制代码
import { _decorator, Collider2D, Component, Contact2DType, director, IPhysics2DContact, Node } from 'cc';
const { ccclass, property,executeInEditMode} = _decorator;
interface AStarNode {
    x: number;
    y: number;
    f: number;
    g: number;
    h: number;
    parent: AStarNode | null;
}
@ccclass('star')

@executeInEditMode
export class star extends Component {
    juesex:number=null;
    juesey:number=null;
    mubiaox:number=null;
    mubiaoy:number=null;
    shangf:number=0;
    shangg:number=0;

    shangh:number=0;


    zuof:number=0;
    zuoh:number=0;
    zuog:number=0;
    youf:number=0;
    youh:number=0;
    youg:number=0;
    xiaf:number=0;
    xiah:number=0;
    xiag:number=0;
    shangx:number=0;
    shangy:number=0;
    zuox:number=0;
    zuoy:number=0;
    youx:number=0;
    youy:number=0;
    xiax:number=0;
    xiay:number=0;
    nextx:number=0;
    nexty:number=0;
    nextg:number=0;
    private closedSet: Set<string> = new Set();
    private openList: AStarNode[] = [];
    onLoad(){

        // 监听碰撞事件

      const collider = this.getComponent(Collider2D);
      
      if (collider) {
          collider.on(Contact2DType.BEGIN_CONTACT, this.onBeginContact, this);
          
          
      }
      director.on('gezi_ditu_xiaobing', this.huoquditu, this);
        
    }

    
    // 碰撞回调
    onBeginContact(selfCollider: Collider2D, otherCollider: Collider2D, contact: IPhysics2DContact | null) {
        if(otherCollider.node.name.includes("shantou")){
            this.scheduleOnce(()=>{

                selfCollider.node.destroy();
            },0.7);
        }
    }
    start() {

        director.emit('xiaobing_position_gezi', this.node.worldPosition);
    }

    update(deltaTime: number) {

    }







    huoquditu(ditu:[][]){
       let len=ditu.length;
       console.log(len+"=");

       for(let y=0;y<len;y++){
        for(let x=0;x<ditu[y].length;x++){
            console.log(ditu[y][x]);
            if(ditu[y][x]==1){
                this.mubiaox=x;

                this.mubiaoy=y;

                //console.log("/"+this.mubiaox,this.mubiaoy)
            }else if(ditu[y][x]==7){
                this.juesex=x;
                this.juesey=y;
                //console.log("/"+this.juesex,this.juesey);
            }
            
        }
           
       }
       //this.jisuanf(this.juesex,this.juesey);
       this.huoqushangxiazuoyou(this.juesex,this.juesey,0);
    }

    jisuannext(x:number,y:number,f:number,g:number){
        
        
        if(f>0){

            //g=g+1;
            this.huoqushangxiazuoyou(x,y,(g+1));
        }


    }

    huoqushangxiazuoyou(qidianx:number,qidiany:number,g:number){
        



        this.openList = [];
        this.closedSet.clear();
        // 创建起点节点
        const startNode: AStarNode = {
            x: this.juesex,
            y: this.juesey,
            g: 0,
            h: this.jisuanh(qidianx, qidiany),
            f: 0,
            parent: null
        };
        startNode.f = startNode.g + startNode.h;
        // 添加到开放列表
        this.openList.push(startNode);
        while(this.openList.length>0){
            //const dangqiangezi=// 1. 找到F值最小的节点
            const dangqiangezi = this.openhuoquzuixiaofgezi();;//在开放列表先寻最小f的格子 由这个作为起点 开始计算四周格子 之前用的是递归能获取上一步的格子 现在是循环 得自己找

            this.removeFromOpenList(dangqiangezi);//因为是准备走的格子 所以从开放列表移除 最后几个放在关闭列表

            this.closedSet.add(''+dangqiangezi.x+','+dangqiangezi.y+'');

            if(dangqiangezi.x==this.mubiaox&&dangqiangezi.y==this.mubiaoy){
                console.log("find");
                this.fujiedian(dangqiangezi);
            }

            // 检查四个方向的邻居
            const neighbors = [
                {dx: 0, dy: -1, name: '上'},   // 上
                {dx: 0, dy: 1, name: '下'},    // 下
                {dx: -1, dy: 0, name: '左'},   // 左
                {dx: 1, dy: 0, name: '右'}     // 右
            ];
            for(let i=0;i<neighbors.length;i++){
                let newx=neighbors[i].dx+dangqiangezi.x;
                let newy=neighbors[i].dy+dangqiangezi.y;

                // 边界检查
            if (!this.isValidPosition(newx, newy)) {
                continue;
            }
                //if(this.bianjie(newx,newy)==false){//跳过 不处理越界的
                    //continue;
                //}
                if(this.closedSet.has(''+newx+','+newy+'')){//跳过 不处理在关闭列表的
                    continue;
                }
                const newg=dangqiangezi.g+1;

                

                const existingNode=this.shifouzaiopenlist(newx,newy);

                if(existingNode==null){
                    // 创建起点节点
                    const newh=this.jisuanh(newx,newy);
                    const newNode: AStarNode = {//好像是将四周的格子都存进去 将走过的取出放进关闭 每次取出都是最小f
                        x: newx,
                        y: newy,
                        g:newg,
                        h: newh,
                        f: newg+newh,
                        parent: dangqiangezi
                    };
                    
                    this.openList.push(newNode);
                }
                    else{
                        
                    

                
                // 如果找到更好的路径(g值更小),更新节点
                if (newg< existingNode.g) {
                    //console.log(`  🔄 更新 ${dir.name}[${neighborX},${neighborY}] g=${tentativeG} < ${existingNode.g}`);
                    existingNode.g =newg;
                    existingNode.f = existingNode.h+existingNode.g;
                    existingNode.parent = dangqiangezi;
                }
        
            }}
    }
        
    }


















    // 添加边界检查方法
isValidPosition(x: number, y: number): boolean {
    return x >= 0 && x < 10 && y >= 0 && y < 10;
}
    jisuanh(x:number,y:number){
        return Math.abs(x-this.mubiaox)+Math.abs(y-this.mubiaoy);
        
    }

    openhuoquzuixiaofgezi(): AStarNode | null{
        if (this.openList.length === 0) return null;
    
    let minIndex = 0;
    let minF = this.openList[0].f;
    
    for (let i = 1; i < this.openList.length; i++) {
        if (this.openList[i].f < minF) {
            minF = this.openList[i].f;
            minIndex = i;
        }
    }
    
    return this.openList[minIndex];
    }
    private removeFromOpenList(node: AStarNode) {
        const index = this.openList.findIndex(n => 
            n.x === node.x && n.y === node.y
        );
        if (index !== -1) {
            this.openList.splice(index, 1);
        }
        
    }

    bianjie(x:number,y:number): boolean{


        return x >= 0 && x < 10 && y >= 0 && y < 10;

    }

    shifouzaiopenlist(x:number,y:number): AStarNode | null{
        for (const node of this.openList) {
            if (node.x === x && node.y === y) {
                return node;
            }
        }
        return null;
    }


















    fujiedian(endnode:AStarNode){
        const path: {x: number, y: number}[] = [];
        let currentNode: AStarNode | null = endnode;
        
        // 从终点回溯到起点
        while (currentNode) {
            path.unshift({x: currentNode.x, y: currentNode.y});
            currentNode = currentNode.parent;
        }
        // 发送路径信息给gezi组件
        this.sendPathToGrid(path);
        
        
    }
    

    // 发送路径到网格显示
    sendPathToGrid(path: {x: number, y: number}[]) {
        // 排除起点(角色当前位置)
        const displayPath = path.slice(1);
        
        // 发送每条路径点
        for (let i = 0; i < displayPath.length; i++) {
            const point = displayPath[i];
            
            // 发送路径点数据,9表示路径点
            const data = {
                type: 9,  // 路径点类型
                x: point.x,
                y: point.y,
                index: i,
                total: displayPath.length
            };
            
            director.emit('xiaobing_path_point', data);
        }
    }
    
}

格子

go 复制代码
import { _decorator, Color, Component, director, Graphics, Node, Vec3 } from 'cc';
const { ccclass, property,executeInEditMode} = _decorator;

@ccclass('gezi')

@executeInEditMode
export class gezi extends Component {
    @property
    gridWidth: number = 10;  // 水平格子数
    
    @property
    gridHeight: number = 10; // 垂直格子数
    
    @property
    cellSize: number = 100;   // 每个格子的大小(像素)
    
    @property
    lineWidth: number = 2;   // 线宽
    
    @property

    lineColor: Color = Color.GRAY;  // 线颜色
    





    @property
    targetColor: Color = new Color(1, 27, 3);  // 线颜色
    @property
    jueseColor: Color = new Color(1, 27, 3);  // 线颜色
    @property
    fillColor: Color = new Color(240, 240, 240);  // 填充颜色(浅灰)
    @property
    xiaColor: Color = new Color(240, 240, 240);  // 填充颜色(浅灰)
    @property
    zuixiaoColor: Color = new Color(240, 240, 240);  // 填充颜色(浅灰)
    @property
    zhangaiColor: Color = new Color(240, 240, 240);  // 填充颜色(浅灰)
    @property(Node)
    xiaobing:Node=null;
    private graphics: Graphics | null = null;

    gezi=[[0,0,0,0,0,0,0,0,0,0],
          [0,0,0,0,0,0,0,0,1,0],
          [0,0,0,0,0,0,0,0,0,0],

          [0,0,0,0,0,0,0,0,0,0],
          [0,0,0,0,0,0,0,0,0,0],








          
          [0,0,0,0,0,0,0,0,0,0],
          [0,0,0,0,0,0,0,0,0,0],
          [0,0,0,0,0,0,0,0,0,0],
          [0,0,0,0,0,0,0,0,0,0],
          [0,0,0,0,0,0,0,0,0,0],
          
        ];

    


















    shangx:number=0;
    shangy:number=0;
    shangf:number=0;
   
    xianshigezi=[[0,0,0,0,0,0,0,0,0,0],
          [0,0,0,0,0,0,0,0,1,0],
          [0,0,0,0,0,0,0,0,0,0],
          [0,0,0,0,0,0,0,0,0,0],
          [0,0,0,0,0,0,0,0,0,0],
          [0,0,0,0,0,0,0,0,0,0],
          [0,0,0,0,0,0,0,0,0,0],
          [0,0,0,0,0,0,0,0,0,0],
          [0,0,0,0,0,0,0,0,0,0],
          [0,0,0,0,0,0,0,0,0,0],
          
        ];
    onLoad() {
        director.on('xiaobing_sxzy_gezi', this.huasxzy, this);
        director.on('xiaobing_path_point', this.showPathPoint, this);
        // 1. 创建并获取 Graphics 组件
        this.graphics = this.node.getComponent(Graphics);
        if (!this.graphics) {
            this.graphics = this.node.addComponent(Graphics);
        }
        
        // 2. 绘制网格
        
        director.on('xiaobing_position_gezi', this.xiaobingposition, this);
        // 获取角色位置并标记
    const worldPos = this.xiaobing.getWorldPosition();
    const gridPos = this.worldPositionToGrid(worldPos);
    

    console.log("转换结果:", gridPos);
    
    // 关键修正:检查边界并更新数组
    if (this.isInGrid(gridPos.x, gridPos.y)) {
        // 注意:你的数组是 gezi[y][x] 结构!所以要 [gridPos.y][gridPos.x]
        this.gezi[gridPos.y][gridPos.x] = 7;

        this.xianshigezi[gridPos.y][gridPos.x] = 7;
    } else {
        console.warn("角色在网格外!位置:", gridPos);
    }
    
    this.drawGrid(this.gezi);
    director.emit('gezi_ditu_xiaobing', this.gezi);
   
}

// 添加边界检查方法
private isInGrid(x: number, y: number): boolean {
    return x >= 0 && x < this.gridWidth && y >= 0 && y < this.gridHeight;

    }

    // 在gezi.ts中添加一个**可靠的**坐标转换方法
public worldPositionToGrid(worldPos: Vec3): { x: number, y: number } {
    const gridWorldPos = this.node.getWorldPosition();
    const offsetX = worldPos.x - gridWorldPos.x;
    const offsetY = worldPos.y - gridWorldPos.y;
    
    // DEBUG: 打印调试信息

    //console.log("=== 坐标转换调试 ===");
    //console.log("1. 网格中心:", gridWorldPos);

    //console.log("2. 角色位置:", worldPos);
    //console.log("3. 偏移量 offsetX=", offsetX, "offsetY=", offsetY);
    


    // 网格半宽/半高 (注意:这里有坐标轴方向问题)
    const halfWidth = (this.gridWidth * this.cellSize) / 2;
    const halfHeight = (this.gridHeight * this.cellSize) / 2;
    

    // Cocos Y轴向上为正,但数组通常是左下角为(0,0)
    // 所以需要调整Y轴方向
    const gridX = Math.floor((offsetX + halfWidth) / this.cellSize);
    const gridY = Math.floor((offsetY + halfHeight) / this.cellSize);
    
    // Y轴翻转(因为屏幕上方是正Y,但数组从上到下索引)
    const flippedY = (this.gridHeight - 1) - gridY;
    
    //console.log(`4. 计算:gridX=${gridX} (${offsetX}+${halfWidth})/${this.cellSize}`);
    //console.log(`5. 计算:gridY=${gridY} (${offsetY}+${halfHeight})/${this.cellSize}`);


    //console.log(`6. 翻转后:gridX=${gridX}, flippedY=${flippedY}`);
    //console.log(`7. 结果:[${this.gezi[flippedY][gridX]}]`);

    
    // 使用 flippedY 因为数组是从上到下存储
    return { x: gridX, y: flippedY };
}
huoquditu(){
    return this.gezi;
}






















huasxzy(data:any){

    //console.log("....");
    //console.log("/"+data.shangf+"z"+data.zuof+"y"+data.youf+"x"+data.xiaf);
    let xiaof=0;
    //this.hua(data.shangx,data.shangy);
    //this.hua(data.zuox,data.zuoy);
    //this.hua(data.youx,data.youy);

    //this.hua(data.xiax,data.xiay);
    this.huazuixiaof(data.zuixiaofangxiangx,data.zuixiaofangxiangy,data.mubiaox,data.mubiaoy,data.juesex,data.juesey);
    this.drawGrid(this.xianshigezi);

}
hua(x:number,y:number){




    this.xianshigezi[y][x]=4;


}

huazuixiaof(x:number,y:number,mubiaox:number,mubiaoy:number,juesex:number,juesey:number){

    //let mubiaox=0;
    //let mubiaoy=0;

    if(this.gezi[y][x]==11){

        console.log("no");
        return;
        
    }

    // 判断是否是目标点
    const isTargetPoint = (x === mubiaox && y === mubiaoy);
    
    if(isTargetPoint) {
        console.log(`⭐ 这是目标点[${x},${y}],保持为1`);
        // 保持目标标记不变,或者标记为其他区分色
        this.xianshigezi[y][x] = 1; // 保持原目标值
    } else {
        // 普通路径点
        console.log(`📝 标记路径点[${x},${y}]为9`);
        this.xianshigezi[y][x] = 9;
    }

    

}

















// 显示路径点
showPathPoint(data: any) {
    const { x, y, type, index, total } = data;
    
    if (this.isInGrid(x, y)) {
        this.xianshigezi[y][x] = type;  // type为9表示路径点
        
        // 可以添加特殊标记,比如根据index显示不同颜色
        console.log(`路径点 ${index + 1}/${total}: [${x}, ${y}]`);
    }
    
    this.drawGrid(this.xianshigezi);
}
public gridToWorldPosition(gridX: number, gridY: number): Vec3 {
    const gridWorldPos = this.node.getWorldPosition();


    
    // 计算世界坐标(格子中心点)

    const worldX = gridWorldPos.x + (gridX - this.gridWidth / 2 + 0.5) * this.cellSize;

    const worldY = gridWorldPos.y + 
                  (gridY - this.gridHeight / 2 + 0.5) * this.cellSize;
    
    return new Vec3(worldX, worldY, 0);
}

    xiaobingposition(position:Vec3){
        console.log("xiaobing",position);
    }
    
    drawGrid(g:number[][]) {
        if (!this.graphics) return;
        
        // 清空之前的绘制
        this.graphics.clear();
        
        // 设置线条样式
        this.graphics.lineWidth = this.lineWidth;
        this.graphics.strokeColor = this.lineColor;
        
        // 首先填充背景色
        this.graphics.fillColor = this.fillColor;
        
        // 计算网格的总宽高(用于居中显示)
        const totalWidth = this.gridWidth * this.cellSize;
        const totalHeight = this.gridHeight * this.cellSize;
        
        // 设置绘制原点在节点中心(Cocos默认)
        // 也可以从左上角开始画,看个人习惯
        
        // 绘制每个格子的填充背景
        for (let x = 0; x < this.gridWidth; x++) {
            for (let y = 0; y < this.gridHeight; y++) {
                let huoqugezi=g[y][x];
                // 然后反转Y坐标,让第一行显示在最上面
                const screenY = this.gridHeight - 1 - y;

                const posX = (x - this.gridWidth/2) * this.cellSize;
                //const posY = (y - this.gridWidth/2) * this.cellSize;
                const posY = (screenY - this.gridHeight/2) * this.cellSize;
                if(huoqugezi==1){
                    this.graphics.fillColor = this.targetColor;
                    //this.graphics.rect(posX, posY, this.cellSize, this.cellSize);
                    
                    //console.log(huoqugezi+"shi");
                }else if(huoqugezi==7){
                    this.graphics.fillColor = this.jueseColor;
                    //this.graphics.rect(posX, posY, this.cellSize, this.cellSize);
                    
                    //console.log(huoqugezi+"shi");
                }else if(huoqugezi==4){
                    this.graphics.fillColor = this.xiaColor;
                }else if(huoqugezi==9){
                    this.graphics.fillColor = this.zuixiaoColor;
                }
                else if(huoqugezi==11){
                    this.graphics.fillColor = this.zhangaiColor;
                }
                else{
                    this.graphics.fillColor = this.fillColor;
                }

                    
                    
                
                this.graphics.rect(posX, posY, this.cellSize, this.cellSize);
                    //console.log(huoqugezi);
                    this.graphics.fill();
            }
            
        }
        
        
        // 绘制垂直线
        for (let x = 0; x <= this.gridWidth; x++) {
            const lineX = (x - this.gridWidth/2) * this.cellSize;
            const startY = (-this.gridHeight/2) * this.cellSize;
            const endY = (this.gridHeight/2) * this.cellSize;
            
            this.graphics.moveTo(lineX, startY);
            this.graphics.lineTo(lineX, endY);
        }
        
        // 绘制水平线
        for (let y = 0; y <= this.gridHeight; y++) {
            const lineY = (y - this.gridHeight/2) * this.cellSize;
            const startX = (-this.gridWidth/2) * this.cellSize;
            const endX = (this.gridWidth/2) * this.cellSize;
            
            this.graphics.moveTo(startX, lineY);
            this.graphics.lineTo(endX, lineY);
        }
        
        // 描边完成绘制
        this.graphics.stroke();
        
        // 可选:绘制坐标标签(调试用)
        this.drawCoordinates();
    }
    
    // 绘制坐标标签(可选,调试用)
    private drawCoordinates() {
        const font = '14px Arial';
        for (let x = 0; x < this.gridWidth; x++) {
            for (let y = 0; y < this.gridHeight; y++) {
                const centerX = (x - this.gridWidth/2) * this.cellSize + this.cellSize/2;
                const centerY = (y - this.gridHeight/2) * this.cellSize + this.cellSize/2;
                
                // Graphics 不支持文字,可以用Label或另想办法
                // 这里先注释,下文有改进方法
            }
        }
    }
    
    // 获取格子中心的世界坐标(有用!)
    public getCellCenterPosition(gridX: number, gridY: number): { x: number, y: number } {
        const worldX = this.node.position.x + 
                      (gridX - this.gridWidth/2) * this.cellSize + 
                      this.cellSize/2;
        const worldY = this.node.position.y + 
                      (gridY - this.gridHeight/2) * this.cellSize + 
                      this.cellSize/2;
        return { x: worldX, y: worldY };
    }
    
    // 世界坐标转格子坐标(有用!)
    public worldToGrid(worldX: number, worldY: number): { x: number, y: number } {
        const localX = worldX - this.node.position.x;
        const localY = worldY - this.node.position.y;
        
        const gridX = Math.floor((localX + this.gridWidth * this.cellSize / 2) / this.cellSize);
        const gridY = Math.floor((localY + this.gridHeight * this.cellSize / 2) / this.cellSize);
        
        return { x: gridX, y: gridY };
    }
    
    // 绘制带颜色的格子(用于标记障碍、起点、终点等)
    public drawColoredCell(gridX: number, gridY: number, color: Color) {
        if (!this.graphics) return;
        
        const posX = (gridX - this.gridWidth/2) * this.cellSize;
        const posY = (gridY - this.gridHeight/2) * this.cellSize;
        
        this.graphics.fillColor = color;
        this.graphics.rect(posX, posY, this.cellSize, this.cellSize);
        this.graphics.fill();
    }
    
    
}
相关推荐
西岸行者4 天前
学习笔记:SKILLS 能帮助更好的vibe coding
笔记·学习
悠哉悠哉愿意4 天前
【单片机学习笔记】串口、超声波、NE555的同时使用
笔记·单片机·学习
别催小唐敲代码4 天前
嵌入式学习路线
学习
毛小茛5 天前
计算机系统概论——校验码
学习
babe小鑫5 天前
大专经济信息管理专业学习数据分析的必要性
学习·数据挖掘·数据分析
winfreedoms5 天前
ROS2知识大白话
笔记·学习·ros2
在这habit之下5 天前
Linux Virtual Server(LVS)学习总结
linux·学习·lvs
我想我不够好。5 天前
2026.2.25监控学习
学习
im_AMBER5 天前
Leetcode 127 删除有序数组中的重复项 | 删除有序数组中的重复项 II
数据结构·学习·算法·leetcode
CodeJourney_J5 天前
从“Hello World“ 开始 C++
c语言·c++·学习