前一部分的最后,我们给出了一个寻路的示例,在大多数情况下,运行还算良好,但是有一个小问题,如下图:

很明显,障碍物已经把路堵死了,但是小球仍然穿过对角线跑了出来!

问题在哪里:我们先回顾一下AStar.as中用于判断的if语句

 //如果是当前节点,或者是不可通过的,则跳过
if (test == node || !test.walkable)
{
continue;
}

在这个判断中,并没有规定说不允许走对象线。来看看如何修正:

在以node为中心考查四周节点时,如果遇到水平和垂直方向都是障碍物时,既使对角节点是可穿越的普通节点,也不能通过。所以只要再加二个条件判断即可

 //如果是当前节点,或者是不可通过的,且排除水平和垂直方向都是障碍物节点时的特例情况
if (test == node || !test.walkable || !_grid.getNode(node.x, test.y).walkable || !_grid.getNode(test.x, node.y).walkable)
{
continue;
}

再运行一下:

一切正常了!

前面提到的这些示例,终点与目标点都是固定的,但在实际游戏中,正好相反,比如"星际",选定一个农民后,在地图上随便点击一下,农民就能自动找到去目标点的路径。

 package
{
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.events.MouseEvent; [SWF(width=600,height=600)]
public class Game extends Sprite
{
private var _cellSize:int=20;
private var _grid:Grid;
private var _player:Sprite;
private var _index:int;
private var _path:Array; public function Game()
{
stage.align=StageAlign.TOP_LEFT;
stage.scaleMode=StageScaleMode.NO_SCALE;
makePlayer();
makeGrid();
stage.addEventListener(MouseEvent.CLICK, onGridClick);
} /** 生成一个player角色(简单起见,就是一个圈) */
private function makePlayer():void
{
_player=new Sprite();
_player.graphics.beginFill(0xff0000);
_player.graphics.drawCircle(0, 0, 5);
_player.graphics.endFill();
_player.x=Math.random() * 600;
_player.y=Math.random() * 600;
addChild(_player);
} /** 生成网格,并随机放置一些障碍 */
private function makeGrid():void
{
_grid=new Grid(30, 30);
for (var i:int=0; i < 200; i++)
{
_grid.setWalkable(Math.floor(Math.random() * 30), Math.floor(Math.random() * 30), false);
}
drawGrid();
} /** 画网格线以及为障碍物填充颜色*/
private function drawGrid():void
{
graphics.clear();
for (var i:int=0; i < _grid.numCols; i++)
{
for (var j:int=0; j < _grid.numRows; j++)
{
var node:Node=_grid.getNode(i, j);
graphics.lineStyle(0);
graphics.beginFill(getColor(node));
graphics.drawRect(i * _cellSize, j * _cellSize, _cellSize, _cellSize);
}
}
} /** 返回节点颜色 */
private function getColor(node:Node):uint
{
if (!node.walkable)
return 0;
if (node == _grid.startNode)
return 0xcccccc;
if (node == _grid.endNode)
return 0xff0000;
return 0xffffff;
} /** 鼠标点击时随机设置终点,并以player当前位置做为起点 */
private function onGridClick(event:MouseEvent):void
{
var xpos:int=Math.floor(mouseX / _cellSize);
var ypos:int=Math.floor(mouseY / _cellSize);
_grid.setEndNode(xpos, ypos);
xpos=Math.floor(_player.x / _cellSize);
ypos=Math.floor(_player.y / _cellSize);
_grid.setStartNode(xpos, ypos);
drawGrid();
findPath();
} /** 寻路 */
private function findPath():void
{
var astar:AStar=new AStar();
if (astar.findPath(_grid))
{
_path=astar.path;
_index=0;
addEventListener(Event.ENTER_FRAME, onEnterFrame);
}
} /**每帧的动画处理*/
private function onEnterFrame(event:Event):void
{
var targetX:Number=_path[_index].x * _cellSize + _cellSize / 2;
var targetY:Number=_path[_index].y * _cellSize + _cellSize / 2; //把经过的点,涂上黄色
var passedNode:Node=_path[_index];
graphics.lineStyle(0);
graphics.beginFill(0xffff00);
graphics.drawRect(passedNode.x * _cellSize, passedNode.y * _cellSize, _cellSize, _cellSize); var dx:Number=targetX - _player.x;
var dy:Number=targetY - _player.y;
var dist:Number=Math.sqrt(dx * dx + dy * dy);
if (dist < 1)
{
_index++;//索引加1,即取一个路径节点
if (_index >= _path.length)//达到最后一个节点时,移除ENTER_FRAME监听
{
removeEventListener(Event.ENTER_FRAME, onEnterFrame);
}
}
else
{
_player.x+=dx * .5;
_player.y+=dy * .5;
}
}
}
}

拿鼠标在空白节点上随便点点,看看会发生些什么?

考虑最后一个问题:实际游戏地图中有平地,有高坡,有沙地,有雪地...不同的路面状况,行走的难度(即代价)应该不同吧?而我们刚才的所有示例中,对所有可穿越的节点都是平等对待的。如何区分出不同情况的地形呢?

关注一下:Node.as中的

 public var costMultiplier:Number=1.0;//代价因子

以及AStar.as中的

 //计算test节点的总代价
var g:Number=node.g + cost * test.costMultiplier;

聪明的你一定看出端倪了!没错,costMultiplier就是代价的权重因子,如果让每个节点的权重因子不同,就能体现出不同地形的行走难度程度。

 package
{
import flash.display.Sprite;
import flash.events.MouseEvent; public class GridView2 extends Sprite
{
private var _cellSize:int = 20;
private var _grid:Grid; public function GridView2(grid:Grid)
{
_grid = grid;
for(var i:int = 0; i < _grid.numCols; i++)
{
for(var j:int = 0; j < _grid.numRows; j++)
{
//为每个节点设置不同的“代价权重因子”
var mult:Number = Math.sin(i * .50) + Math.cos(j * .2 + i * .05);
_grid.getNode(i, j).costMultiplier = Math.abs(mult) + 1;
}
}
drawGrid();
findPath();
addEventListener(MouseEvent.CLICK, onGridClick);
} //画网格
public function drawGrid():void
{
graphics.clear();
for(var i:int = 0; i < _grid.numCols; i++)
{
for(var j:int = 0; j < _grid.numRows; j++)
{
var node:Node = _grid.getNode(i, j);
graphics.lineStyle(0);
graphics.beginFill(getColor(node));
graphics.drawRect(i * _cellSize, j * _cellSize, _cellSize, _cellSize);
}
}
} //取得单元格的颜色(与权重因子关联,costMultiplier越小,颜色越深)
private function getColor(node:Node):uint
{
if(!node.walkable) return 0;
if(node == _grid.startNode) return 0x666666;
if(node == _grid.endNode) return 0x666666;
var shade:Number = 300 - 70 * node.costMultiplier;
return shade << 16 | shade << 8 | shade;
} //单元格点击时,切换节点为普通节点或障碍物节点
private function onGridClick(event:MouseEvent):void
{
var xpos:int = Math.floor(event.localX / _cellSize);
var ypos:int = Math.floor(event.localY / _cellSize); _grid.setWalkable(xpos, ypos, !_grid.getNode(xpos, ypos).walkable);
drawGrid();
findPath();
} //找路
private function findPath():void
{
var astar:AStar = new AStar();
if(astar.findPath(_grid))
{
//showVisited(astar);
showPath(astar);
}
} private function showVisited(astar:AStar):void
{
var visited:Array = astar.visited;
for(var i:int = 0; i < visited.length; i++)
{
graphics.beginFill(0xcccccc);
graphics.drawRect(visited[i].x * _cellSize, visited[i].y * _cellSize, _cellSize, _cellSize);
graphics.endFill();
}
} private function showPath(astar:AStar):void
{
var path:Array = astar.path;
for(var i:int = 0; i < path.length; i++)
{
graphics.lineStyle(0);
graphics.beginFill(0);
graphics.drawCircle(path[i].x * _cellSize + _cellSize / 2,
path[i].y * _cellSize + _cellSize / 2,
_cellSize / 3);
}
}
}
}

跟上一部分里的GridView.as比较起来,GridView2.as在构造函数里根据sin与cos函数,为节点设置了不同的权重因子,而且在节点着色上,深色的代价要比浅色的代价大,测试一下:

 package
{
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.MouseEvent; [SWF(backgroundColor=0xffffff,width=440,height=440)]
public class Pathfinding_2 extends Sprite
{
private var _grid:Grid;
private var _gridView:GridView2; public function Pathfinding_2()
{
stage.align=StageAlign.TOP_LEFT;
stage.scaleMode=StageScaleMode.NO_SCALE;
_grid=new Grid(20, 20);
_grid.setStartNode(1, 1);
_grid.setEndNode(18, 18); _gridView=new GridView2(_grid);
_gridView.x=20;
_gridView.y=20;
addChild(_gridView);
}
}
}

可以看出,调整权重因子后,路径尽量在靠近浅色的区域前进!可能这样对比还不强烈,把上面测试代码中的GridView2换回GridView,对比看下没有权重因子干扰时的路径

当然,在具体游戏开发过程中,A*算法还要结合其它很多技术(比如加载地图,配合地图设置权重因子,把地图分配到网格单元等)才能最终做出不错的游戏,我们在这里只是讨论寻路算法的原理,其它方面留给大家自行去完善吧.

作者:菩提树下的杨过
出处:http://yjmyzz.cnblogs.com 
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
 
标签: 动画flashflexas3寻路A*A星

“AS3.0高级动画编程”学习:第四章 寻路(AStar/A星/A*)算法 (下)的更多相关文章

  1. “AS3.0高级动画编程”学习:第一章高级碰撞检测

    AdvancED ActionScript 3.0 Animation 是Keith Peters大师继"Make Things Move"之后的又一力作,网上已经有中文翻译版本了 ...

  2. “AS3.0高级动画编程”学习:第二章转向行为(上)

    因为这一章的内容基本上都是涉及向量的,先来一个2D向量类:Vector2D.as (再次强烈建议不熟悉向量运算的童鞋,先回去恶补一下高等数学-07章空间解释几何与向量代数.pdf) 原作者:菩提树下的 ...

  3. “AS3.0高级动画编程”学习:第二章转向行为(下)

    在上一篇里,我们学习了“自主角色”的一些基本行为:寻找(seek).避开(flee).到达(arrive).追捕(pursue).躲避(evade).漫游(wander).这一篇将继续学习其它更复杂, ...

  4. “AS3.0高级动画编程”学习:第三章等角投影(上)

    什么是等角投影(isometric)? 原作者:菩提树下的杨过出处:http://yjmyzz.cnblogs.com 刚接触这个概念时,我也很茫然,百度+google了N天后,找到了一些文章: [转 ...

  5. Intel汇编语言程序设计学习-第四章 数据传送、寻址和算术运算-下

    4.3  和数据相关的操作符和伪指令 操作符和伪指令并非机器可执行的指令,相反,它们是由汇编器进行解释的.开发者可以使用一系列的MASM操作符或伪指令获取数据的地址以及大小等特征信息: OFFSET操 ...

  6. [书籍翻译] 《JavaScript并发编程》第四章 使用Generators实现惰性计算

    本文是我翻译<JavaScript Concurrency>书籍的第四章 使用Generators实现惰性计算,该书主要以Promises.Generator.Web workers等技术 ...

  7. C#高级编程 (第六版) 学习 第四章:继承

    第四章 继承 1,继承的类型 实现继承: 一个类派生于一个基类型,拥有该基类型所有成员字段和函数. 接口继承 一个类型只继承了函数的签名,没有继承任何实现代码.   2,实现继承 class MyDe ...

  8. JavaScript高级程序设计:第十四章

    第十四章 一.表单的基础知识 在HTML中,表单是由<form>元素来表示的,而在javascript中,表单对应的则是HTMLFormElement类型.HTMLFormElement继 ...

  9. Java基础知识二次学习--第四章 异常

    第四章 异常处理   时间:2017年4月26日11:16:39~2017年4月26日11:28:58 章节:04章_01节 04章_02节 视频长度:20:46+01:16 内容:异常的概念 心得: ...

随机推荐

  1. matlab学习(3) 保存和导入工作区

    1.保存和导入工作区变量mat文件 假如创建了两个矩阵A=[1,2;3,4],B=[0,1;1,0] 则工作区就是这样的: 当函数有一个数据量非常大的返回值时,每次调用函数都要执行一遍函数,每次都要等 ...

  2. MAC如何生成SSH key与GitHub关联

    一.检查 SSH key 是否存在 在终端输入如下代码: ls -al ~/.ssh 如果没有,终端显示如下: No such file or directory 如果有,终端显示如下: ➜ ~ ls ...

  3. PyCharm:ModuleNotFoundError: No module named 'selenium'

    Mac安装PyCharm后,将已有工程导入,之前使用Mac终端执行脚本时正常,现在报错ModuleNotFoundError: No module named 'selenium',解决方法是在PyC ...

  4. hive入门学习线路指导

    hive被大多数企业使用,学习它,利于自己掌握企业所使用的技术,这里从安装使用到概念.原理及如何使用遇到的问题,来讲解hive,希望对大家有所帮助.此篇内容较多:看完之后需要达到的目标1.hive是什 ...

  5. QT4.8.6-VS2010开发环境配置

    目录 1.下载软件 2.环境配置 3.VAssistX配置 1.下载软件 VS2010下载地址:链接: https://pan.baidu.com/s/1gvPjZWBtSEwW37H1xf2vbA ...

  6. dos2unix 批量转化文件

    在windows和linux双平台下开发,同时也用git作为同步工具,但前期没有注意,导致很多文件使用windows下的换行符CRLF 参考资料了解dos2unix可以转化格式. 但有个问题,虽然可以 ...

  7. DHCP Option43配置

    在配置Option 43之前,需要保证:1. AP与DHCP服务器之间路由可达,即AP可以获取到IP地址.2. AP与AC之间路由可达,保证AP获取到AC地址后,能够与AC交互信息,建立CAPWAP隧 ...

  8. sourcetree file status checkbox gone (文件状态下的勾选文件 list 消失)

    原来是这样的(sourcetree 版本 2.7.1) 不知道触发了什么条件,sourcetree 变成了以下状态(官方解释是拖动面板小于 1 px 后会导致这个问题) 中间的那一列可勾选的已修改的文 ...

  9. pyhdfs安装

    参考: http://blog.csdn.net/sinat_33741547/article/details/54428726 1.先更新pip,防止版本过低pip install --upgrad ...

  10. Delphi XE5 Android 调用 Google ZXing

    { Google ZXing Call demo Delphi Version: Delphi XE5 Version 19.0.13476.4176 By: flcop(zylove619@hotm ...