分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow
也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!
本文实践自 Johann Fradj 的文章《How To Implement A* Pathfinding with Cocos2D Tutorial》,文中使用Cocos2D,我在这里使用Cocos2D-x 3.0进行学习和移植。在这篇文章,将会学习到如何在Cocos2D中实现A星算法。在开始之前,先阅读文章《Introduction to A* Pathfinding》将会有所帮助。
步骤如下:
1.下载本文章的准备工程,编译运行,如下图所示:
在这款游戏中,猫需要通过由狗守卫的地牢,除非拿骨头贿赂狗,不然狗会将猫吃掉。注意到猫只能水平或垂直的移动,每次只能移动一个方块。
2.开始修改成A星寻路算法。打开CatSprite.h文件,创建ShortestPathStep内部类,代表路径上的一步操作。代码如下:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
class ShortestPathStep : public cocos2d::Object
{ public: ShortestPathStep(); ~ShortestPathStep(); static ShortestPathStep *createWithPosition( const cocos2d::Point &pos); bool initWithPosition( const cocos2d::Point &pos); int getFScore() const; bool isEqual( const ShortestPathStep *other) const; std::string getDescription() const; CC_SYNTHESIZE(cocos2d::Point, _position, Position); CC_SYNTHESIZE( int, _gScore, GScore); CC_SYNTHESIZE( int, _hScore, HScore); CC_SYNTHESIZE(ShortestPathStep*, _parent, Parent); }; |
正如所见,这是一个很简单的类,记录了以下内容:
方块的坐标
G值(记住,这是开始点到当前点的方块数量)
H值(记住,这是当前点到目标点的方块估算数量)
Parent是它的上一步操作
F值,这是方块的和值(它是G+H的值)
打开CatSprite.cpp文件,添加以下代码:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
CatSprite::ShortestPathStep::ShortestPathStep() :
_position(Point::ZERO), _gScore( 0), _hScore( 0), _parent(nullptr) { } CatSprite::ShortestPathStep::~ShortestPathStep() { } CatSprite::ShortestPathStep *CatSprite::ShortestPathStep::createWithPosition( const Point &pos) { ShortestPathStep *pRet = new ShortestPathStep(); if (pRet && pRet->initWithPosition(pos)) { pRet->autorelease(); return pRet; } else { CC_SAFE_DELETE(pRet); return nullptr; } } bool CatSprite::ShortestPathStep::initWithPosition( const Point &pos) { bool bRet = false; do { this->setPosition(pos); bRet = true; } while ( 0); return bRet; } int CatSprite::ShortestPathStep::getFScore() const { return this->getGScore() + this->getHScore(); } bool CatSprite::ShortestPathStep::isEqual( const CatSprite::ShortestPathStep *other) const { return this->getPosition() == other->getPosition(); } std::string CatSprite::ShortestPathStep::getDescription() const { return StringUtils::format( "pos=[%.0f;%.0f] g=%d h=%d f=%d", this->getPosition().x, this->getPosition().y, this->getGScore(), this->getHScore(), this->getFScore()); } |
这里定义了getDescription方法,以方便调试。创建了isEquals方法,当且仅当两个ShortestPathSteps的方块坐标相同时,它们相等(例如它们代表着相同的方块)。
3.创建Open和Closed列表。打开CatSprite.h文件,添加如下代码:
1
2 |
cocos2d::Vector<ShortestPathStep*> _spOpenSteps;
cocos2d::Vector<ShortestPathStep*> _spClosedSteps; |
4.检查开始和结束点。重新实现moveToward方法,获取当前方块坐标和目标方块坐标,然后检查是否需要计算一条路径,最后测试目标方块坐标是否可行走的(在这里只有墙壁是不可行走的)。打开CatSprite.cpp文件,修改moveToward方法,为如下:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
void CatSprite::moveToward( const Point &target)
{ Point fromTileCoord = _layer->tileCoordForPosition( this->getPosition()); Point toTileCoord = _layer->tileCoordForPosition(target); if (fromTileCoord == toTileCoord) { CCLOG( "You're already there! :P"); return; } if (!_layer->isValidTileCoord(toTileCoord) || _layer->isWallAtTileCoord(toTileCoord)) { SimpleAudioEngine::getInstance()->playEffect( "hitWall.wav"); return; } CCLOG( "From: %f, %f", fromTileCoord.x, fromTileCoord.y); CCLOG( "To: %f, %f", toTileCoord.x, toTileCoord.y); } |
编译运行,在地图上进行点击,如果不是点击到墙壁的话,可以在控制台看到如下信息:
1
2 |
From: 24.000000, 0.000000
To: 22.000000, 3.000000 |
其中"From"就是猫的方块坐标,"To"就是所点击的方块坐标。
5.实现A星算法。根据算法,第一步是添加当前坐标到open列表。还需要三个辅助方法:
一个方法用来插入一个ShortestPathStep对象到适当的位置(有序的F值)
一个方法用来计算从一个方块到相邻方块的移动数值
一个方法是根据"曼哈顿距离"算法,计算方块的H值
打开CatSprite.cpp文件,添加如下方法:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
void CatSprite::insertInOpenSteps(CatSprite::ShortestPathStep *step)
{ int stepFScore = step->getFScore(); ssize_t count = _spOpenSteps.size(); ssize_t i = 0; for (; i < count; ++i) { if (stepFScore <= _spOpenSteps.at(i)->getFScore()) { break; } } _spOpenSteps.insert(i, step); } int CatSprite::computeHScoreFromCoordToCoord( const Point &fromCoord, const Point &toCoord) { // 这里使用曼哈顿方法,计算从当前步骤到达目标步骤,在水平和垂直方向总的步数 // 忽略了可能在路上的各种障碍 return abs(toCoord.x - fromCoord.x) + abs(toCoord.y - fromCoord.y); } int CatSprite::costToMoveFromStepToAdjacentStep( const ShortestPathStep *fromStep, const ShortestPathStep *toStep) { // 因为不能斜着走,而且由于地形就是可行走和不可行走的成本都是一样的 // 如果能够对角移动,或者有沼泽、山丘等等,那么它必须是不同的 return 1; } |
接下来,需要一个方法去获取给定方块的所有相邻可行走方块。因为在这个游戏中,HelloWorld管理着地图,所以在那里添加方法。打开HelloWorldScene.cpp文件,添加如下方法:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
PointArray *HelloWorld::walkableAdjacentTilesCoordForTileCoord( const Point &tileCoord) const
{ PointArray *tmp = PointArray::create( 4); // 上 Point p(tileCoord.x, tileCoord.y - 1); if ( this->isValidTileCoord(p) && ! this->isWallAtTileCoord(p)) { tmp->addControlPoint(p); } // 左 p.setPoint(tileCoord.x - 1, tileCoord.y); if ( this->isValidTileCoord(p) && ! this->isWallAtTileCoord(p)) { tmp->addControlPoint(p); } // 下 p.setPoint(tileCoord.x, tileCoord.y + 1); if ( this->isValidTileCoord(p) && ! this->isWallAtTileCoord(p)) { |