您的位置: 首页 > 技术文档 > 多媒体制作 > 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 设计比赛 网页制作 Dreamweaver Studio8 Flash
站点最新 站点最新列表
疯狂的程序员 第五十回
疯狂的程序员 第四十九回
疯狂的程序员 第四十八回
疯狂的程序员 第四十七回
疯狂的程序员 第四十六回
疯狂的程序员 第四十五回
疯狂的程序员 第四十四回
疯狂的程序员 第四十三回
疯狂的程序员 第四十二回
疯狂的程序员 第四十一回
栏目最新 栏目最新列表
火星人的耳机
公司正式宣布创业失败
用corelDEAW 12打造唇膏
二行代码解决全部网页木马
一行代码解决iframe挂马
Photoshop制作星空爆炸效果
CorelDraw 12打造休闲裤
Firework如何画特殊的切角图形
Firework打造韩式风格的手提袋
flash实例:打造佛光效果
>> 分页 首页 前页 后页 尾页 页次:4/41个记录/页 转到 页 共4个记录

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

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

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

本文现有 2 条评论 评分:- llllllllllllllllllll + 评分人数: 2 ,平均分: 4.50


yilianxin Publish at 2007-10-11 14:48:19 评分5
不错
onechenxing Publish at 2007-5-28 10:31:29 评分4
good
您的评论
用户名:  口令:
说明:输入正确的用户名和密码才能参与评论。如果您不是本站会员,你可以注册 为本站会员。
注意:文章中的链接、内容等需要修改的错误,请用报告错误,以利文档及时修改。
不评分 1 2 3 4 5
注意:请不要在评论中含与内容无关的广告链接,违者封ID
请您注意:
·不良评论请用报告管理员,以利管理员及时删除。
·尊重网上道德,遵守中华人民共和国的各项有关法律法规
·承担一切因您的行为而直接或间接导致的民事或刑事法律责任
·本站评论管理人员有权保留或删除其管辖评论中的任意内容
·您在本站发表的作品,本站有权在网站内转载或引用
·参与本评论即表明您已经阅读并接受上述条款
推荐文档 | 打印文档 | 评论文档 | 报告错误  
专业书推荐 更多内容
大师之路--Photoshop 完全解析
《超越CSS》新书上市
Don't Make Me Think 第2版
HTML与CSS入门经典(第7版)
《FLASH MX2004网站开发精粹》
《CSS入门经典》
《设计师谈网页设计思维》
作品集 更多内容

雅图琴行品牌设计 圣诞 ps&#43;鼠标 部分图标作品 JobEMS.com网站首页 视觉摄影 性感&nbsp;Jessica 行者武松