您的位置: 首页 > 技术文档 > 多媒体制作 > A*寻路,二叉堆优化及AS3实现
浅谈flash web的结构 回到列表 Apollo是危险的吗?
 A*寻路,二叉堆优化及AS3实现

作者:eidiot 时间: 2007-04-23 文档类型:原创 来自:蓝色理想

第 1 页 A*寻路,二叉堆优化及AS3实现
第 2 页 牛刀小试 - A*寻路算法简介
第 3 页 如虎添翼 - 使用二叉堆优化
第 4 页 锋芒毕露 - AS3代码和示例

地形数据不属于A*寻路的范围,这里定义一个 IMapTileModel 接口,由其它(模型)类来实现地图通路的判断。其它比如寻路超时的判断这里也不介绍,具体参考 AStar类及其测试代码。这里只介绍三部分主要内容:

* 数据存储
    * 寻路过程
    * 列表排序

数据存储
首先看看三个关键变量:

private var m_openList : Array; //开放列表,存放节点ID
private var m_openCount : int; //当前开放列表中节点数量
private var m_openId : int; //节点加入开放列表时分配的唯一ID(从0开始)

开放列表 m_openList 是个二叉堆(一维数组),F值最小的节点始终排在最前。为加快排序,开放列表中只存放节点ID ,其它数据放在各自的一维数组中:

private var m_xList : Array; //节点x坐标
private var m_yList : Array; //节点y坐标
private var m_pathScoreList : Array; //节点路径评分F值
private var m_movementCostList : Array; //(从起点移动到)节点的移动耗费G值
private var m_fatherList : Array; //节点的父节点(ID)

这些数据列表都以节点ID为索引顺序存储。看看代码如何工作:

//每次取出开放列表最前面的ID
currId = this.m_openList[0];
//读取当前节点坐标
currNoteX = this.m_xList[currId];
currNoteY = this.m_yList[currId];

还有一个很关键的变量:private var m_noteMap : Array;
//节点(数组)地图,根据节点坐标记录节点开启关闭状态和ID

使用 m_noteMap 可以方便的存取任何位置节点的开启关闭状态,并可取其ID进而存取其它数据。m_noteMap 是个三维数组,第一维y坐标(第几行),第二维x坐标(第几列),第三维节点状态和ID。判断点(p_x, p_y)是否在开启列表中:

return this.m_noteMap[p_y][p_x][NOTE_OPEN];

寻路过程
AStar类 寻路的方法是 find() :

/**
* 开始寻路
* @param p_startX        起点X坐标
* @param p_startY        起点Y坐标
* @param p_endX        终点X坐标
* @param p_endY        终点Y坐标
* @return                 找到的路径(二维数组 : [p_startX, p_startY], ... , [p_endX, p_endY])
*/      
public function find(p_startX : int, p_startY : int, p_endX : int, p_endY : int) : Array{/* 寻路 */}

注意这里返回数据的形式:从起点到终点的节点数组,其中每个节点为一维数组[x, y]的形式。为了加快速度,类里没有使用Object或是Point,节点坐标全部以数组形式存储。如节点note的x坐标为note[0],y坐标为note[1]。

下面开始寻路,第一步将起点添加到开启列表:

this.openNote(p_startX, p_startY, 0, 0, 0);

openNote() 方法将节点加入开放列表的同时分配一个唯一的ID、按此ID存储数据、对开启列表排序。接下来是寻路过程:

while (this.m_openCount > 0)
{
    //每次取出开放列表最前面的ID
    currId = this.m_openList[0];
    //将编码为此ID的元素列入关闭列表
    this.closeNote(currId);
    //如果终点被放入关闭列表寻路结束,返回路径
    if (currNoteX == p_endX && currNoteY == p_endY)
        return this.getPath(p_startX, p_startY, currId);
    //...每轮寻路过程
}
//开放列表已空,找不到路径
return null;

每轮的寻路:

//获取周围节点,排除不可通过和已在关闭列表中的
aroundNotes = this.getArounds(currNoteX, currNoteY);
//对于周围每个节点
for each (var note : Array in aroundNotes)
{
    //计算F和G值
    cost = this.m_movementCostList[currId] + (note[0] == currNoteX || note[1] == currNoteY) ? COST_STRAIGHT : COST_DIAGONAL;
    score = cost + (Math.abs(p_endX - note[0]) + Math.abs(p_endY - note[1])) * COST_STRAIGHT;
    if (this.isOpen(note[0], note[1])) //如果节点已在开启列表中
    {
        //测试节点的ID
    checkingId = this.m_noteMap[note[1]][note[0]][NOTE_ID];
    //如果新的G值比节点原来的G值小,修改F,G值,换父节点
    if(cost < this.m_movementCostList[checkingId])
    {
        this.m_movementCostList[checkingId] = cost;
        this.m_pathScoreList[checkingId] = score;
        this.m_fatherList[checkingId] = currId;
        //对开启列表重新排序
        this.aheadNote(this.getIndex(checkingId));
    }
    } else //如果节点不在开放列表中
    {
    //将节点放入开放列表
    this.openNote(note[0], note[1], score, cost, currId);
    }
}

从终点开始依次沿父节点回到到起点,返回找到的路径:

/**
* 获取路径
* @param p_startX    起始点X坐标
* @param p_startY    起始点Y坐标
* @param p_id        终点的ID
* @return             路径坐标数组
*/      
private function getPath(p_startX : int, p_startY : int, p_id: int) : Array
{
    var arr : Array = [];
    var noteX : int = this.m_xList[p_id];
    var noteY : int = this.m_yList[p_id];
    while (noteX != p_startX || noteY != p_startY)
    {
    arr.unshift([noteX, noteY]);
    p_id = this.m_fatherList[p_id];
    noteX = this.m_xList[p_id];
    noteY = this.m_yList[p_id];
    }
    arr.unshift([p_startX, p_startY]);
    this.destroyLists();
    return arr;
}

列表排序
这部分看代码和注释就可以了,不多说:

/** 将(新加入开放别表或修改了路径评分的)节点向前移动 */
private function aheadNote(p_index : int) : void
{
    var father : int;
    var change : int;
    //如果节点不在列表最前
    while(p_index > 1)
    {
    //父节点的位置
    father = Math.floor(p_index / 2);
    //如果该节点的F值小于父节点的F值则和父节点交换
    if (this.getScore(p_index) < this.getScore(father))
    {
        change = this.m_openList[p_index - 1];
        this.m_openList[p_index - 1] = this.m_openList[father - 1];
        this.m_openList[father - 1] = change;
        p_index = father;
    } else
    {
        break;
    }
    }
}
/** 将(取出开启列表中路径评分最低的节点后从队尾移到最前的)节点向后移动 */
private function backNote() : void
{
    //尾部的节点被移到最前面
    var checkIndex : int = 1;
    var tmp : int;
    var change : int;
    while(true)
    {
    tmp = checkIndex;
    //如果有子节点
    if (2 * tmp <= this.m_openCount)
    {
        //如果子节点的F值更小
        if(this.getScore(checkIndex) > this.getScore(2 * tmp))
        {
        //记节点的新位置为子节点位置
        checkIndex = 2 * tmp;
        }
        //如果有两个子节点
        if (2 * tmp + 1 <= this.m_openCount)
        {
        //如果第二个子节点F值更小
        if(this.getScore(checkIndex) > this.getScore(2 * tmp + 1))
        {
            //更新节点新位置为第二个子节点位置
            checkIndex = 2 * tmp + 1;
        }
        }
    }
    //如果节点位置没有更新结束排序
    if (tmp == checkIndex)
    {
        break;
    }
    //反之和新位置交换,继续和新位置的子节点比较F值
    else
    {
        change = this.m_openList[tmp - 1];
        this.m_openList[tmp - 1] = this.m_openList[checkIndex - 1];
        this.m_openList[checkIndex - 1] = change;
    }
    }
}

其中 getScore() 方法:

/**
* 获取某节点的路径评分F值
* @param p_index    节点在开启列表中的索引(从1开始)
*/      
private function getScore(p_index : int) : int
{
    //开启列表索引从1开始,ID从0开始,数组索引从0开始
    return this.m_pathScoreList[this.m_openList[p_index - 1]];
}

经典论坛讨论:
http://bbs.blueidea.com/thread-2738527-1-1.html

本文链接:http://www.blueidea.com/tech/multimedia/2007/4665.asp 

出处:蓝色理想
责任编辑:elesa

上一页 如虎添翼 - 使用二叉堆优化 下一页

◎进入论坛Flash专栏版块参加讨论

相关文章 更多相关链接
浅谈flash web的结构
雅致Flash打包工具1.0
用Flash制作的站长工具箱
Apollo是危险的吗?
Apollo 开发技巧
作者文章
Conjee_Album 1.0 发布
AS3学习笔记
关键字搜索 常规搜索 推荐文档
热门搜索:CSS Fireworks 设计比赛 网页制作 web标准 用户体验 UE photoshop Dreamweaver Studio8 Flash 手绘 CG
站点最新 站点最新列表
周大福“敬•自然”设计大赛开启
国际体验设计大会7月将在京举行
中国国防科技信息中心标志征集
云计算如何让安全问题可控
云计算是多数企业唯一拥抱互联网的机会
阿里行云
云手机年终巨献,送礼标配299起
阿里巴巴CTO王坚的"云和互联网观"
1499元买真八核 云OS双蛋大促
首届COCO桌面手机主题设计大赛
栏目最新 栏目最新列表
浅谈JavaScript编程语言的编码规范
如何在illustrator中绘制台历
Ps简单绘制一个可爱的铅笔图标
数据同步算法研究
用ps作简单的作品展示页面
CSS定位机制之一:普通流
25个最佳最闪亮的Eclipse开发项目
Illustrator中制作针线缝制文字效果
Photoshop制作印刷凹凸字体
VS2010中创建自定义SQL Rule
>> 分页 首页 前页 后页 尾页 页次:4/41个记录/页 转到 页 共4个记录

蓝色理想版权申明:除部分特别声明不要转载,或者授权我站独家播发的文章外,大家可以自由转载我站点的原创文章,但原作者和来自我站的链接必须保留(非我站原创的,按照原来自一节,自行链接)。文章版权归我站和作者共有。

转载要求:转载之图片、文件,链接请不要盗链到本站,且不准打上各自站点的水印,亦不能抹去我站点水印。

特别注意:本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有,文章若有侵犯作者版权,请与我们联系,我们将立即删除修改。

您的评论
用户名:  口令:
说明:输入正确的用户名和密码才能参与评论。如果您不是本站会员,你可以注册 为本站会员。
注意:文章中的链接、内容等需要修改的错误,请用报告错误,以利文档及时修改。
不评分 1 2 3 4 5
注意:请不要在评论中含与内容无关的广告链接,违者封ID
请您注意:
·不良评论请用报告管理员,以利管理员及时删除。
·尊重网上道德,遵守中华人民共和国的各项有关法律法规
·承担一切因您的行为而直接或间接导致的民事或刑事法律责任
·本站评论管理人员有权保留或删除其管辖评论中的任意内容
·您在本站发表的作品,本站有权在网站内转载或引用
·参与本评论即表明您已经阅读并接受上述条款
推荐文档 | 打印文档 | 评论文档 | 报告错误  
专业书推荐 更多内容
网站可用性测试及优化指南
《写给大家看的色彩书1》
《跟我去香港》
众妙之门—网站UI 设计之道
《Flex 4.0 RIA开发宝典》
《赢在设计》
犀利开发—jQuery内核详解与实践
作品集 更多内容

杂⑦杂⑧ Gold NORMANA V2