简单四方向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();
    }
    
    
}
相关推荐
喏喏心2 小时前
深度强化学习:价值迭代与Bellman方程实践
人工智能·python·学习·机器学习
kkkkkkkkk_12013 小时前
【强化学习】06周博磊强化学习纲要学习笔记——第三课下
笔记·学习·强化学习
白杨SEO营销3 小时前
白杨SEO:看“20步:从0-1做项目的笨办法”来学习如何选一个项目做及经验分享
前端·学习
无所事事的程序员3 小时前
Claude指令学习
学习
学习路上_write3 小时前
AD5293驱动学习
c语言·单片机·嵌入式硬件·学习
菥菥爱嘻嘻4 小时前
组件测试--React Testing Library的学习
前端·学习·react.js
白帽子黑客罗哥4 小时前
零基础转行渗透测试 系统的学习流程(非常详细)
学习·网络安全·渗透测试·漏洞挖掘·护网行动
李洛克074 小时前
RDMA 编程完整学习路线图
学习·rdma·路线
你想知道什么?4 小时前
JNI简单学习(java调用C/C++)
java·c语言·学习