标签:
这一个小节我们说一说传说中的A×算法,其实之前也上传过类似的小件件,这里我们就去剖析一下它
毕竟在游戏程序,我们要从一点移动到另一点,并得到最短路程的轨迹,类似这种算法还有好几种,执行效率都差不多,不过大多不能得到轨迹
首先,从一点移动到另一点,最快就是直接走过去了,就像小男生爱上小女生,最好的办法就是直接走到她面前说:我爱你
不过理想状态,几乎是没有的,弯路那是必然的经过,有曲线,其实更美……
那么弯路该怎么走呢,是不是先去背景看下毛主席,再去三亚晒个太阳,再回来告诉她外面的世界好美,不,不,不……
再怎么弯的曲线,越接近直接,越好,所以步骤是:
1.向周围4个或8个方向,所有不是边界的点,记录这些点到目标的距离,从开始到这个点的距离,上一个到这里的点
2.根据两个距离和最小为标准,依次“递归”所有的点直到目标点
3.反向得到路径,就是最近的曲线路径
问:会不会到达同一个点,存在不同的距离
答:是,但是根据最近距离标准递推下去,更远距离的将被丢弃
问:递归为什么加引号
答:用队列实现堆栈,也算是递归,但是不会耗尽线程堆栈
为了,方便在推箱子中检测是否能打到某个点,我修改下代码实现不记录上一个点,加速执行
不过后来就没有使用这个功能,主要代码如下:
alpha.cpp
// **************************************************************************************************** // 文件: alpha.cpp // 注释: // A*寻路算法模块, 判断两点之间是否通行, 并得到路径和各节点的权值 // 扩展不保存路径提速, 不使用Dijkstra, 参见http://www.2cto.com/kf/201104/87378.html // **************************************************************************************************** //#include <stdlib.h> #include <malloc.h> #define __LATTICE_DLL_INC_ // DLL内部编译 #include "api.h" // 创建一个场景, 并初始化 V32API PSCENE __stdcall AlphaInit(long dwSizeX, long dwSizeY, long dwMaxCost, UINT dwFlags) { PSCENE ps = (PSCENE)malloc(sizeof(SCENE)); if(ps == NULL) { return NULL; } V32Clear32(ps, 0, sizeof(SCENE)); // memset, 自动计算DWORD数量 ps->NodeCount = dwSizeX * dwSizeY; // 节点数量 ps->Nodes = (PNODE)malloc(ps->NodeCount * sizeof(NODE)); if(ps->Nodes == NULL) { AlphaExit(ps, 0xFFFFFFFF); return NULL; } ps->Matrix = (PBYTE)malloc(ps->NodeCount * sizeof(BYTE)); // 场景矩阵 if(ps->Matrix == NULL) { AlphaExit(ps, 0xFFFFFFFF); return NULL; } ps->Steps = (PSTEP)malloc(ps->NodeCount * sizeof(STEP)); if(ps->Steps == NULL) { AlphaExit(ps, 0xFFFFFFFF); return NULL; } if(dwMaxCost <= 0) { dwMaxCost = dwSizeX * dwSizeY; // 相邻格子逐个走遍 } ps->MaxCost = dwMaxCost * CDC_PROP_NEAR; // 最大消耗(距离值) ps->SizeY = dwSizeY; ps->SizeX = dwSizeX; ps->Flags = dwFlags; return ps; } // 编辑场景数据 V32API int __stdcall AlphaEdit(PSCENE pScene, long dwPosX, long dwPosY, void *lpValue) { PBYTE p; if(dwPosX < 0 && dwPosY < 0) { // 读取 p = pScene->Scene; if(p == NULL) p = pScene->Matrix; *(PBYTE)lpValue = p[dwPosY * pScene->SizeX + dwPosX]; return 2; } if(dwPosX < 0 || dwPosY < 0) { // 使用外部数据指针 pScene->Scene = (PBYTE)lpValue; return 3; } // 设置字节数值 if(dwPosX >= pScene->SizeX || dwPosY >= pScene->SizeY) { //memcpy(pScene->Matrix, lpValue, pScene->NodeCount); return 0; } p = pScene->Scene; if(p == NULL) p = pScene->Matrix; if(lpValue == NULL) { return p[dwPosY * pScene->SizeX + dwPosX]; // 读取 }else { p[dwPosY * pScene->SizeX + dwPosX] = *(PBYTE)lpValue; // 写入 } return 1; } inline long AlphaCalc(PSTAR pStar1, PSTAR pStar2) { // 直接使用坐标差和代替三角函数开方 if(pStar1->X > pStar2->X) { if(pStar1->Y > pStar2->Y) return (pStar1->X - pStar2->X) + (pStar1->Y - pStar2->Y); else return (pStar1->X - pStar2->X) + (pStar2->Y - pStar1->Y); }else { if(pStar1->Y > pStar2->Y) return (pStar2->X - pStar1->X) + (pStar1->Y - pStar2->Y); else return (pStar2->X - pStar1->X) + (pStar2->Y - pStar1->Y); } //return dX + dY; } inline void AlphaNode(PSCENE pScene, long dwIndex, long dwPrevId, long dwCost, long dwLast, long dwPosX, long dwPosY) { // 开始和循环各调用一次,dwIndex在调用之后自加,初始为0 if(dwIndex >= pScene->NodeCount) return; //pScene->Nodes[dwIndex].Star = *pStar; // 是否会创建临时结构体?? pScene->Nodes[dwIndex].Star.X = dwPosX; pScene->Nodes[dwIndex].Star.Y = dwPosY; pScene->Nodes[dwIndex].Prev = dwPrevId; if(dwPrevId != -1) pScene->Nodes[dwIndex].Step = pScene->Nodes[dwPrevId].Step + 1; else pScene->Nodes[dwIndex].Step = 1; pScene->Nodes[dwIndex].Cost = dwCost; pScene->Nodes[dwIndex].Last = dwLast * 10; // 每次dwLen重新计算得到格子数 pScene->Nodes[dwIndex].Flags = SNF_PROP_READY; return; } inline BYTE fnAlphaUnit(PSCENE pScene, PSTAR ps) { BYTE *pUnit = pScene->Scene; if(pUnit == NULL) pUnit = pScene->Matrix; return pUnit[ps->Y * pScene->SizeX + ps->X]; } inline PSTEP fnAlphaStep(PSCENE pScene, PSTAR ps) { return &pScene->Steps[ps->Y * pScene->SizeX + ps->X]; } // 寻路指定场景 V32API int __stdcall AlphaStar(PSCENE pScene, PSTAR lpStart, PSTAR lpTarget, long *pdwStep) { PSTEP pStep; long lCurCost, nMaxCost; // 暂存消耗,最大列表 long i, j; // temp & looping var STAR tps, cps; // test pos, current pos long dwValue; long dwIndex = 0; long dwNode = 0; // 当前Node数 long dwLoop; long dwLen; // 与终点距离 // check for memory address accessable if(lpStart == NULL || lpTarget == NULL) { return 0; // 始末坐标无效 } dwLen = AlphaCalc(lpStart, lpTarget); // dwLen = ΔX + ΔY if(dwLen == 0) { return -1; // 始末坐标相同 } // zero step memory(cell prop list) V32Clear32(pScene->Steps, 0, pScene->NodeCount * sizeof(STEP)); // 添加第一个点 dwNode = 0; AlphaNode(pScene, dwNode, -1, 0, dwLen, lpStart->X, lpStart->Y); dwNode++; // enter loop - check around cells while(1) { nMaxCost = pScene->MaxCost; // 不可能比这个大 dwIndex = -1; for(dwLoop = 0; dwLoop < dwNode; dwLoop++) { if(pScene->Nodes[dwLoop].Flags != SNF_PROP_ERROR) { //找未关闭中最小路程和的点 lCurCost = pScene->Nodes[dwLoop].Cost + pScene->Nodes[dwLoop].Last; if(lCurCost < nMaxCost) { nMaxCost = lCurCost; // 调整最大距离 dwIndex = dwLoop; // 保存节点序号 //break; // 所有节点都要计算 } } } if(dwIndex == -1) { return -2; // there is no path exist! } cps.X = pScene->Nodes[dwIndex].Star.X; cps.Y = pScene->Nodes[dwIndex].Star.Y; if((cps.X == lpTarget->X)&&(cps.Y == lpTarget->Y)) { break; // 当前点已是终点, 跳出while循环 } //sprintf(szText, "select best cell:[%d,%d] for check:", cps.X, cps.Y); for(i = -1; i <= 1; i++) { for(j = -1; j <= 1; j++) { //if(i == 0 && j == 0) continue; // 允许走对角线,只要两个不同时为零(即不是自身) if(i == 0 && j == 0) continue; if(i != 0 && j != 0) continue; // 禁止走对角线,必有且只有一个为零{[(i & j) == 0]&&[(i | j) != 0]} tps.X = cps.X + i; tps.Y = cps.Y + j; if(tps.X < 0) continue; // 左边越界 if(tps.X >= pScene->SizeX) continue; // 右边越界 if(tps.Y < 0) continue; // 顶边越界 if(tps.Y >= pScene->SizeY) continue; // 底边越界 // 该点坐标在矩阵范围内 if(fnAlphaUnit(pScene, &tps) & (BYTE)pScene->Flags) { continue; // 消除特殊位以后仍然不可通行(独立程序: 仅允许空格或目标) } pStep = fnAlphaStep(pScene, &tps); switch(pStep->Flags){ case SSF_PROP_UNKNOW: // it'v not been check dwValue = pScene->Nodes[dwIndex].Cost; //if(i * j != 0) // dwValue += CDC_PROP_AWAY; // 横纵坐标都是非零, 对角 //else dwValue += CDC_PROP_NEAR; // 任何一个坐标为零, 相邻 dwLen = AlphaCalc(&tps, lpTarget); //dwLen = ΔX + ΔY AlphaNode(pScene, dwNode, dwIndex, dwValue, dwLen, tps.X, tps.Y); pStep->Flags = SSF_PROP_OPENED; // open it pStep->Index = dwNode++; // sprintf(szText, "add cell:[%d,%d] for check:", tps.X, tps.Y); break; case SSF_PROP_OPENED: // this cell is valid dwValue = pStep->Index; dwLen = pScene->Nodes[dwIndex].Cost; dwLen += CDC_PROP_NEAR; // 只能相邻 if(dwLen < pScene->Nodes[dwValue].Cost) { pScene->Nodes[dwValue].Cost = dwLen; pScene->Nodes[dwValue].Prev = dwIndex; pScene->Nodes[dwValue].Step = pScene->Nodes[dwIndex].Step + 1; //change level } //sprintf(szText, "change pID for cell:[%d,%d] to %d.", tps.X, tps.Y, nID); break; default: //end if(lpOGrid[tps.X][tps.Y].State.. break; } //end if((lpCell.. //end if((tps.X.. //end if(((i.. } //next j } //next i // it will continue if any path and not at target pStep = fnAlphaStep(pScene, &cps); pScene->Nodes[dwIndex].Flags = SNF_PROP_ERROR; pStep->Flags = SSF_PROP_CLOSED; // close it //sprintf(szText, "close cell:[%d,%d] ok.", cps.X, cps.Y); } // 函数malloc()和realloc()时间消耗大,用空间换取时间 dwValue = pScene->Nodes[dwIndex].Cost + pScene->Nodes[dwIndex].Last; if(pdwStep == NULL) { // 不需要结果详情 return dwValue; // return cost to get here } //sprintf(szText, "best path found in cost %d.", dwValue); pScene->StarCount = pScene->Nodes[dwIndex].Step; SafeFree(pScene->Stars); // release old path pScene->Stars = (PSTAR)malloc(pScene->StarCount * sizeof(STAR)); if(pScene->Stars == NULL) { return -3; // Out of memory } // ... dwLoop = pScene->StarCount; *pdwStep = dwLoop; // set the steps while(dwLoop > 0) // it can also base on dwIndex { dwLoop--; //pScene->m_pStarPath[dwLoop].X = pScene->Nodes[dwIndex].Star.X; //pScene->m_pStarPath[dwLoop].Y = pScene->Nodes[dwIndex].Star.Y; pScene->Stars[dwLoop] = pScene->Nodes[dwIndex].Star; dwIndex = pScene->Nodes[dwIndex].Prev; // parent id //sprintf(szText, "the %d cell:[%d,%d] added to path.", i, lpRoad[i].X, lpRoad[i].Y); } return dwValue; // return cost to get here } V32API int __stdcall AlphaData(PSCENE pScene, UINT dwPropId, long dwIndex) { VAR v; v.pValue = (void*)pScene; v.dwValue += dwPropId; if(dwIndex < 0) { return (int)*v.plValue; // 直接定位成员 } v.dwValue = *v.pdwValue; v.dwValue += dwIndex; return (int)*v.plValue; } // 销毁一个场景, 释放资源 V32API int __stdcall AlphaExit(PSCENE pScene, UINT dwFlags) { if(pScene) { SafeFree(pScene->Stars); pScene->StarCount = 0; SafeFree(pScene->Steps); pScene->Scene = NULL; // 外部数据指针 SafeFree(pScene->Matrix); SafeFree(pScene->Nodes); pScene->NodeCount = 0; free(pScene); //pScene = NULL; } return 1; }
这是我的一个基础类库的基类,包括常用的C语言函数和C++封装类,还有一些宏定义
函数大多是内联汇编的裸函数,宏定义一般是快速释放内存用的,如
#define SafeDelete(p) if(p){delete p; p = NULL;}
标签:
原文地址:http://blog.csdn.net/prsniper/article/details/44264071