如何在Cocos2D游戏中实现A*寻路算法(四)
大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处.
如果觉得写的不好请告诉我,如果觉得不错请多多支持点赞.谢谢! hopy ;)
免责申明:本博客提供的所有翻译文章原稿均来自互联网,仅供学习交流之用,请勿进行商业用途。同时,转载时不要移除本申明。如产生任何纠纷,均与本博客所有人、发表该翻译稿之人无任何关系。谢谢合作!
检查我们的起点和终点
现在前奏已经结束了,让我们用新的实现替换moveToward方法.
我们将从瓦片坐标系中取得现有开始位置(点A)和目标位置(点B)开始.然后我们将检查是否需要计算路径,并且最终测试目标位置是否为可达的(在我们的例子中只有墙是不可达的).
将moveToard方法替换为如下代码:
- (void)moveToward:(CGPoint)target
{
// Get current tile coordinate and desired tile coord
CGPoint fromTileCoord = [_layer tileCoordForPosition:self.position];
CGPoint toTileCoord = [_layer tileCoordForPosition:target];
// Check that there is a path to compute ;-)
if (CGPointEqualToPoint(fromTileCoord, toTileCoord)) {
NSLog(@"You're already there! :P");
return;
}
// Must check that the desired location is walkable
// In our case it's really easy, because only wall are unwalkable
if ([_layer isWallAtTileCoord:toTileCoord]) {
[[SimpleAudioEngine sharedEngine] playEffect:@"hitWall.wav"];
return;
}
NSLog(@"From: %@", NSStringFromCGPoint(fromTileCoord));
NSLog(@"To: %@", NSStringFromCGPoint(toTileCoord));
}
编译运行并且点击地图.如果你没有在墙上点击的话,在控制台中你将看到”from”等于{24,0},这时猫咪的位置.你将同样注意到”to”的坐标系的x和y值在[0;24]之间,这是用来在地图坐标系中表示你在什么位置点击.
实现A*算法
根据我们的算法,第一步是将当前位置添加到开放列表中.
我们同样需要3个帮助方法:
- 一个方法将ShortestPathStep插入到开放列表中合适的位置上(根据F值排序).
- 一个方法计算一个瓦块到其邻居瓦块的移动花费.
- 一个方法去计算瓦块的H值,依据”城区”算法.
打开CatSprite.m做出如下修改:
// In "private properties and methods" section
- (void)insertInOpenSteps:(ShortestPathStep *)step;
- (int)computeHScoreFromCoord:(CGPoint)fromCoord toCoord:(CGPoint)toCoord;
- (int)costToMoveFromStep:(ShortestPathStep *)fromStep toAdjacentStep:(ShortestPathStep *)toStep;
// Add these new methods after moveToward
// Insert a path step (ShortestPathStep) in the ordered open steps list (spOpenSteps)
- (void)insertInOpenSteps:(ShortestPathStep *)step
{
int stepFScore = [step fScore]; // Compute the step's F score
int count = [self.spOpenSteps count];
int i = 0; // This will be the index at which we will insert the step
for (; i < count; i++) {
if (stepFScore <= [[self.spOpenSteps objectAtIndex:i] fScore]) { // If the step's F score is lower or equals to the step at index i
// Then we found the index at which we have to insert the new step
// Basically we want the list sorted by F score
break;
}
}
// Insert the new step at the determined index to preserve the F score ordering
[self.spOpenSteps insertObject:step atIndex:i];
}
// Compute the H score from a position to another (from the current position to the final desired position
- (int)computeHScoreFromCoord:(CGPoint)fromCoord toCoord:(CGPoint)toCoord
{
// Here we use the Manhattan method, which calculates the total number of step moved horizontally and vertically to reach the
// final desired step from the current step, ignoring any obstacles that may be in the way
return abs(toCoord.x - fromCoord.x) + abs(toCoord.y - fromCoord.y);
}
// Compute the cost of moving from a step to an adjacent one
- (int)costToMoveFromStep:(ShortestPathStep *)fromStep toAdjacentStep:(ShortestPathStep *)toStep
{
// Because we can't move diagonally and because terrain is just walkable or unwalkable the cost is always the same.
// But it have to be different if we can move diagonally and/or if there is swamps, hills, etc...
return 1;
}
上面代码中的注释应该解释的足够详细了,请花时间把它们读一遍.
接下来,我们需要一个方法去取得指定瓦块的所有可到达的邻居瓦块.因为在这个游戏中HelloWorldLayer管理着地图,我们需要将方法添加都其中去.
打开HelloWorldLayer.h,在@interface之后增加方法的定义:
- (NSArray *)walkableAdjacentTilesCoordForTileCoord:(CGPoint)tileCoord;
然后添加实现代码到HelloWorldLayer.m中去:
- (NSArray *)walkableAdjacentTilesCoordForTileCoord:(CGPoint)tileCoord
{
NSMutableArray *tmp = [NSMutableArray arrayWithCapacity:4];
// Top
CGPoint p = CGPointMake(tileCoord.x, tileCoord.y - 1);
if ([self isValidTileCoord:p] && ![self isWallAtTileCoord:p]) {
[tmp addObject:[NSValue valueWithCGPoint:p]];
}
// Left
p = CGPointMake(tileCoord.x - 1, tileCoord.y);
if ([self isValidTileCoord:p] && ![self isWallAtTileCoord:p]) {
[tmp addObject:[NSValue valueWithCGPoint:p]];
}
// Bottom
p = CGPointMake(tileCoord.x, tileCoord.y + 1);
if ([self isValidTileCoord:p] && ![self isWallAtTileCoord:p]) {
[tmp addObject:[NSValue valueWithCGPoint:p]];
}
// Right
p = CGPointMake(tileCoord.x + 1, tileCoord.y);
if ([self isValidTileCoord:p] && ![self isWallAtTileCoord:p]) {
[tmp addObject:[NSValue valueWithCGPoint:p]];
}
return [NSArray arrayWithArray:tmp];
}
现在我们已经有了这些帮助方法,我们可以继续在CatSprite.m中实现我们的moveToward方法.添加如下的代码到moveToward方法的最后:
BOOL pathFound = NO;
self.spOpenSteps = [[[NSMutableArray alloc] init] autorelease];
self.spClosedSteps = [[[NSMutableArray alloc] init] autorelease];
// Start by adding the from position to the open list
[self insertInOpenSteps:[[[ShortestPathStep alloc] initWithPosition:fromTileCoord] autorelease]];
do {
// Get the lowest F cost step
// Because the list is ordered, the first step is always the one with the lowest F cost
ShortestPathStep *currentStep = [self.spOpenSteps objectAtIndex:0];
// Add the current step to the closed set
[self.spClosedSteps addObject:currentStep];
// Remove it from the open list
// Note that if we wanted to first removing from the open list, care should be taken to the memory
[self.spOpenSteps removeObjectAtIndex:0];
// If the currentStep is the desired tile coordinate, we are done!
if (CGPointEqualToPoint(currentStep.position, toTileCoord)) {
pathFound = YES;
ShortestPathStep *tmpStep = currentStep;
NSLog(@"PATH FOUND :");
do {
NSLog(@"%@", tmpStep);
tmpStep = tmpStep.parent; // Go backward
} while (tmpStep != nil); // Until there is not more parent
self.spOpenSteps = nil; // Set to nil to release unused memory
self.spClosedSteps = nil; // Set to nil to release unused memory
break;
}
// Get the adjacent tiles coord of the current step
NSArray *adjSteps = [_layer walkableAdjacentTilesCoordForTileCoord:currentStep.position];
for (NSValue *v in adjSteps) {
ShortestPathStep *step = [[ShortestPathStep alloc] initWithPosition:[v CGPointValue]];
// Check if the step isn't already in the closed set
if ([self.spClosedSteps containsObject:step]) {
[step release]; // Must releasing it to not leaking memory ;-)
continue; // Ignore it
}
// Compute the cost from the current step to that step
int moveCost = [self costToMoveFromStep:currentStep toAdjacentStep:step];
// Check if the step is already in the open list
NSUInteger index = [self.spOpenSteps indexOfObject:step];
if (index == NSNotFound) { // Not on the open list, so add it
// Set the current step as the parent
step.parent = currentStep;
// The G score is equal to the parent G score + the cost to move from the parent to it
step.gScore = currentStep.gScore + moveCost;
// Compute the H score which is the estimated movement cost to move from that step to the desired tile coordinate
step.hScore = [self computeHScoreFromCoord:step.position toCoord:toTileCoord];
// Adding it with the function which is preserving the list ordered by F score
[self insertInOpenSteps:step];
// Done, now release the step
[step release];
}
else { // Already in the open list
[step release]; // Release the freshly created one
step = [self.spOpenSteps objectAtIndex:index]; // To retrieve the old one (which has its scores already computed ;-)
// Check to see if the G score for that step is lower if we use the current step to get there
if ((currentStep.gScore + moveCost) < step.gScore) {
// The G score is equal to the parent G score + the cost to move from the parent to it
step.gScore = currentStep.gScore + moveCost;
// Because the G Score has changed, the F score may have changed too
// So to keep the open list ordered we have to remove the step, and re-insert it with
// the insert function which is preserving the list ordered by F score
// We have to retain it before removing it from the list
[step retain];
// Now we can removing it from the list without be afraid that it can be released
[self.spOpenSteps removeObjectAtIndex:index];
// Re-insert it with the function which is preserving the list ordered by F score
[self insertInOpenSteps:step];
// Now we can release it because the oredered list retain it
[step release];
}
}
}
} while ([self.spOpenSteps count] > 0);
if (!pathFound) { // No path found
[[SimpleAudioEngine sharedEngine] playEffect:@"hitWall.wav"];
}
再一次,上面代码中的注释应该对于每一点工作起了很好的解释作用.所以当你添加代码和阅读过注释之后,尝试去编译运行app!
如果你点击如下图所示的瓦片:
你应该在控制台中看到如下显示:
<ShortestPathStep: 0x6a336b0> pos=[22;3] g=9 h=0 f=9
<ShortestPathStep: 0x6a33570> pos=[21;3] g=8 h=1 f=9
<ShortestPathStep: 0x6a33400> pos=[20;3] g=7 h=2 f=9
<ShortestPathStep: 0x6a328c0> pos=[20;2] g=6 h=3 f=9
<ShortestPathStep: 0x6a32750> pos=[20;1] g=5 h=4 f=9
<ShortestPathStep: 0x6a7b940> pos=[21;1] g=4 h=3 f=7
<ShortestPathStep: 0x6a7b810> pos=[22;1] g=3 h=2 f=5
<ShortestPathStep: 0x6a7b700> pos=[23;1] g=2 h=3 f=5
<ShortestPathStep: 0x6a86150> pos=[24;1] g=1 h=4 f=5
<ShortestPathStep: 0x6a321c0> pos=[24;0] g=0 h=0 f=0
别忘了路径是反向建立起来的,所以你必须从后向前读.我建议你尝试去在地图上实际匹配这些瓦块,这样你就可以明白最短路径实际是如何工作的!
如何在Cocos2D游戏中实现A*寻路算法(四)的更多相关文章
- 如何在Cocos2D游戏中实现A*寻路算法(一)
大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请告诉我,如果觉得不错请多多支持点赞.谢谢! hopy ;) 免责申明:本博客提供的所有翻译文章原稿均来自互联网,仅供学习交流 ...
- 如何在Cocos2D游戏中实现A*寻路算法(六)
大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请告诉我,如果觉得不错请多多支持点赞.谢谢! hopy ;) 免责申明:本博客提供的所有翻译文章原稿均来自互联网,仅供学习交流 ...
- 如何在Cocos2D游戏中实现A*寻路算法(八)
大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请告诉我,如果觉得不错请多多支持点赞.谢谢! hopy ;) 免责申明:本博客提供的所有翻译文章原稿均来自互联网,仅供学习交流 ...
- 如何在Cocos2D游戏中实现A*寻路算法(二)
大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请告诉我,如果觉得不错请多多支持点赞.谢谢! hopy ;) 免责申明:本博客提供的所有翻译文章原稿均来自互联网,仅供学习交流 ...
- 如何在Cocos2D游戏中实现A*寻路算法(七)
大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请告诉我,如果觉得不错请多多支持点赞.谢谢! hopy ;) 免责申明:本博客提供的所有翻译文章原稿均来自互联网,仅供学习交流 ...
- 如何在Cocos2D游戏中实现A*寻路算法(五)
大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请告诉我,如果觉得不错请多多支持点赞.谢谢! hopy ;) 免责申明:本博客提供的所有翻译文章原稿均来自互联网,仅供学习交流 ...
- 如何在Cocos2D游戏中实现A*寻路算法(三)
大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请告诉我,如果觉得不错请多多支持点赞.谢谢! hopy ;) 免责申明:本博客提供的所有翻译文章原稿均来自互联网,仅供学习交流 ...
- 如何在cocos2d项目中enable ARC
如何在cocos2d项目中enable ARC 基本思想就是不支持ARC的代码用和支持ARC的分开,通过xcode中设置编译选项,让支持和不支持ARC的代码共存. cocos2d是ios app开发中 ...
- 游戏AI之A*寻路算法(3)
前言:寻路是游戏比较重要的一个组成部分.因为不仅AI还有很多地方(例如RTS游戏里操控人物点到地图某个点,然后人物自动寻路走过去)都需要用到自动寻路的功能. 本文将介绍一个经常被使用且效率理想的寻路方 ...
随机推荐
- Linux mint界面过小无法安装(解决方法)
安装Linux mint时,到了分区的时候,有时候会出现界面过小(就是你点击不到确定键). 如下图所示: 大家可以看到下面的确定键无法显示,导致无法安装.网上查了一下都是清一色的下载vwware to ...
- 10分钟 5步 发布以太坊 ERC20 代币
1.安装 METAMASK Brings Ethereum to your browser 一个可以浏览器上进行操作的以太坊钱包,推荐 Chrome. Chrome 插件安装地址: https://c ...
- CNN中的经典结构之AlexNet
AlexNet的基本结构 Alexnet是由5个卷积层和三个全连接层组成,一共8个权重层(池化层不是权重层因为其没有参数),其中ReLU激活函数作用在每个卷积层和全连接层上,在第一个卷积层和第二个卷积 ...
- 最新版-MySQL8.0 安装 - 改密码 之坑
1. 需求背景 最近需要在一台性能一般的电脑上使用数据库,所以决定安装MySQL数据库,以前安装都是使用WorkBench自动化安装,但安装过程太慢占用空间过大,于是下载zip压缩包.之所以选择选择M ...
- map内置函数、lambda表达式、快捷生成想要的列表、filter内置函数
map函数 语法 map(function, iterable, ...) 参数 function -- 函数,有两个参数 iterable ...
- java.lang.UnsatisfiedLinkError: D:\Tomcat\apache-tomcat-7.0.67\bin\tcnative-1.dll:
Can't load IA 32-bit .dll on a AMD 64-bit platform 错误原因 由错误提示可知,tcnative-1.dll是一个32位文件,但是运行在64位系统上 解 ...
- spark升级后 集成hbase-1.0.0-cdh5.4.5异常
.具体场景如下: spark1.6 升级 spark2.2 后 分析查询hbase 数据报异常: 具体错误如下: ERROR TableInputFormat: java.io ...
- Oracle中建库时报Exception in thread main
Linux操作系统上安装oracle 10g,在启动dbca的时候报 Exception in thread "main" 错误,详细内容如下: [oracle@centos ~] ...
- ABP文档笔记系列
ABP文档笔记 - 模块系统 及 配置中心 ABP文档笔记 - 事件BUS ABP文档笔记 - 数据过滤 ABP文档笔记 - 规约 ABP文档笔记 - 配置.设置.版本.功能.权限 ABP文档笔记 - ...
- 转:Xming + PuTTY 在Windows下远程Linux主机使用图形界面的程序
一.原理Linux/Unix的X Window具有网络透明性.X Window系统里有一个统一的Server来负责各个程序与显示器.键盘和鼠标等输入输出设备的交互,每个有GUI的应用程序都通过网络协议 ...