zoukankan      html  css  js  c++  java
  • cocos2dx A* + tiledMap

    上一章告诉cocos2dx 正在使用A星算法

    这一章讲 A*结合tiledmap

    先看下效果图


    图有点丑,忍受下

    绿色的块 表示人物的行走的路线(A*算法的结果)

    红色部分 表示A*算法搜寻过的点(越少,速度越快)

    黑色的部分(事实上是无色块,由于背景是黑色的) 表示障碍物


    这张图是用tiledmap做出来的, 看看里面的内容


    能够看到 我把不能通过的地区的图块给删了

    tiledmap中有2个层 一个是background, 一个是road. 为了方便, 我把road也用相同的图片, 最好的方法是用一种相同的瓦片拼接出来一条能走的路, 让后把background图层加到road图层上就ok了.

    以下直接上源代码, 用的时cocos2.2.3, 复制到项目中就能用了.当然别忘了自己做个像样的tiledMap . 

     假设你认为好用, 就在文章底下顶一个吧 , enjoy it !

    #ifndef __HELLOWORLD_SCENE_H__
    #define __HELLOWORLD_SCENE_H__
    
    #include "cocos2d.h"
    #include "vector"
    using namespace std;
    USING_NS_CC;
    #define MAP_WIDTH 200//要比tmx中的map大
    #define MAP_HEIGHT 200
    class PathSprite 
    {
    public:
        PathSprite(CCSprite* sprite)
        {
            m_parent = NULL;
            m_child = NULL;
            m_costToSource = 0;
            m_FValue = 0;
            m_sprite = sprite;
        };
    public:
        CCSprite* m_sprite;//包括的瓦片精灵
        PathSprite* m_parent;//父节点
        PathSprite* m_child;//子节点
        float m_costToSource;//到起始点的距离
        int m_x;//地图坐标
        int m_y;
        float m_FValue;
    };
    class PathSearchInfo//寻路类(主要负责寻路的參数和逻辑)
    {
    public:
        
        static int m_startX;//開始点
        static int m_startY;
        
        static int m_endX;//结束点
        static int m_endY;
     
        static CCSize m_mapSize;//地图大小
        static CCSize m_tileSize;//地图的块大小
        static vector<PathSprite*> m_openList;//开放列表(里面存放相邻节点)
        static PathSprite* m_inspectArray[MAP_WIDTH][MAP_HEIGHT];//所有须要检測的点
        static vector<PathSprite*> m_pathList;//路径列表
        static vector<PathSprite*> m_haveInspectList;//检測过的列表
    
        static float calculateTwoObjDistance(PathSprite* obj1, PathSprite* obj2)//计算两个物体间的距离
        {
            //        float _offsetX = obj1->m_x - obj2->m_x;
            //        float _offsetY = obj1->m_y - obj2->m_y;
            //        return sqrt( _offsetX * _offsetX + _offsetY * _offsetY);
            
            float _x = abs(obj2->m_x - obj1->m_x);
            float _y = abs(obj2->m_y - obj1->m_y);
            
            return _x + _y;
        }
        static void inspectTheAdjacentNodes(PathSprite* node, PathSprite* adjacent, PathSprite* endNode)//把相邻的节点放入开放节点中
        {
            if (adjacent)
            {
                float _x = abs(endNode->m_x - adjacent->m_x);
                float _y = abs(endNode->m_y - adjacent->m_y);
                
                float F , G, H1, H2, H3;
                adjacent->m_costToSource = node->m_costToSource + calculateTwoObjDistance(node, adjacent);//获得累计的路程
                G = adjacent->m_costToSource;
                
                //三种算法, 感觉H2不错
                H1 = _x + _y;
                H2 = hypot(_x, _y);
                H3 = max(_x, _y);
                
    #if 1 //A*算法 = Dijkstra算法 + 最佳优先搜索
                F = G + H2;
    #endif
    #if 0//Dijkstra算法
                F = G;
    #endif
    #if 0//最佳优先搜索
                F = H2;
    #endif
                adjacent->m_FValue = F;
                
                adjacent->m_parent = node;//设置父节点
                adjacent->m_sprite->setColor(ccORANGE);//搜寻过的节点设为橘色(測试用)
                m_haveInspectList.push_back(adjacent);
                node->m_child = adjacent;//设置子节点
    
                PathSearchInfo::m_inspectArray[adjacent->m_x][adjacent->m_y] = NULL;//把检測过的点从检測列表中删除
                PathSearchInfo::m_openList.push_back(adjacent);//增加开放列表
            }
        }
        static PathSprite* getMinPathFormOpenList()//从开放节点中获取F值最小值的点
        {
            if (m_openList.size()>0) {
                PathSprite* _sp =* m_openList.begin();
                for (vector<PathSprite*>::iterator iter = m_openList.begin(); iter !=  m_openList.end(); iter++)
                {
                    if ((*iter)->m_FValue < _sp->m_FValue)
                    {
                        _sp = *iter;
                    }
                }
                return _sp;
            }
            else
            {
                return NULL;
            }
            
        }
        static PathSprite* getObjFromInspectArray(int x, int y)//依据横纵坐标从检測数组中获取点
        {
            if (x >=0 && y >=0 && x < m_mapSize.width && y < m_mapSize.height) {
                return m_inspectArray[x][y];
            }
            return  NULL;
        }
        static bool removeObjFromOpenList( PathSprite* sprite)//从开放列表中移除对象
        {
            if (!sprite) {
                return  false;
            }
            for (vector<PathSprite*>::iterator iter = m_openList.begin(); iter !=  m_openList.end(); iter++)
            {
                if (*iter == sprite)
                {
                    m_openList.erase(iter);
                    return true;
                }
            }
            return false;
            
        }  
    };
    class HelloWorld : public cocos2d::CCLayer
    {
    public:
        // Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone
        virtual bool init();  
    
        // there's no 'id' in cpp, so we recommend returning the class instance pointer
        static cocos2d::CCScene* scene();
        
        // a selector callback
        void menuCloseCallback(CCObject* pSender);
        
        // implement the "static node()" method manually
        CREATE_FUNC(HelloWorld);
        void onEnter();
        virtual bool ccTouchBegan(CCTouch* touch, CCEvent* event);
        virtual void ccTouchMoved(CCTouch* touch, CCEvent* event);
        virtual void ccTouchEnded(CCTouch* touch, CCEvent* event);
        
        void calculatePath();//计算路径
        
        void drawPath();//绘制路径(測试用)
        
        void clearPath();//清理路径
    
        void playerMove();//人物走动
    
        void update(float dt);//跟新大地图(行走时, 人不动, 地图跟着人动);
        
    public:
        CCPoint m_orignPoint;//人物的起始点
        
        PathSprite* m_player;//人物点
        
        int m_playerMoveStep;//人物当前的行程的索引
        
    };
    
    #endif // __HELLOWORLD_SCENE_H__
    
    #include "HelloWorldScene.h"
    
    USING_NS_CC;
    vector<PathSprite*> PathSearchInfo::m_openList;
    
    PathSprite* PathSearchInfo::m_inspectArray[MAP_WIDTH][MAP_HEIGHT] = {NULL};
    
    vector<PathSprite*> PathSearchInfo::m_pathList;
    
    vector<PathSprite*> PathSearchInfo::m_haveInspectList;
    
    CCSize PathSearchInfo::m_mapSize;
    
    CCSize PathSearchInfo::m_tileSize;
    
    int PathSearchInfo::m_startX;
    
    int PathSearchInfo::m_startY;
    
    int PathSearchInfo::m_endX;
    
    int PathSearchInfo::m_endY;
    
    CCScene* HelloWorld::scene()
    {
        // 'scene' is an autorelease object
        CCScene *scene = CCScene::create();
        
        // 'layer' is an autorelease object
        HelloWorld *layer = HelloWorld::create();
    
        // add layer as a child to scene
        scene->addChild(layer);
    
        // return the scene
        return scene;
    }
    
    // on "init" you need to initialize your instance
    void HelloWorld::onEnter()
    {
        CCDirector* pDirector = CCDirector::sharedDirector();
        pDirector->getTouchDispatcher()->addTargetedDelegate(this, 0, true);
        CCLayer::onEnter();
    
    }
    
    bool HelloWorld::init()
    {
        //////////////////////////////
        // 1. super init first
        if ( !CCLayer::init() )
        {
            return false;
        }
        
        CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize();
        CCPoint origin = CCDirector::sharedDirector()->getVisibleOrigin();
    
        /////////////////////////////
        // 2. add a menu item with "X" image, which is clicked to quit the program
        //    you may modify it.
    
    
        
        CCLabelTTF* pLabel = CCLabelTTF::create("A* + tiledMap", "Arial", 24);
        
        // position the label on the center of the screen
        pLabel->setPosition(ccp(origin.x + visibleSize.width/2,
                                origin.y + visibleSize.height - pLabel->getContentSize().height));
    
        // add the label as a child to this layer
        this->addChild(pLabel, 1);
    
        this->scheduleUpdate();
    
       CCTMXTiledMap* map = CCTMXTiledMap::create("gameMap.tmx");
        this->addChild(map);
        map->setPosition(CCPoint());
        CCTMXLayer* _road = map->layerNamed("road");//行走路径的地图
        CCSize _mapSize = map->getMapSize();
        for (int j = 0;  j < _mapSize.height; j++) {
            for (int i = 0;  i < _mapSize.width; i++) {
                CCSprite* _sp = _road->tileAt(CCPoint(i, j));
                if (_sp) {
                    PathSprite* _pathSprite = new PathSprite(_sp);
                    _pathSprite->m_x = i;
                    _pathSprite->m_y = j;
                    PathSearchInfo::m_inspectArray[i][j] = _pathSprite;//把地图中所有的点一一相应放入检測列表中
                }
            }
        }
        PathSearchInfo::m_mapSize = _mapSize;//获取地图的尺寸
        PathSearchInfo::m_tileSize = map->getTileSize();//获取瓦片的尺寸
        
        //设置起始和终点
        PathSearchInfo::m_startX =30;
        PathSearchInfo::m_startY = 75;
    
        //创建一个人物
        m_player = new PathSprite(CCSprite::create("10001.png"));
        m_player->m_sprite->setAnchorPoint(CCPoint(0.5,0));
        this->addChild(m_player->m_sprite);
        
        m_player->m_x = PathSearchInfo::m_startX;//设置人物的起始的地图坐标
        m_player->m_y = PathSearchInfo::m_startY;
        
        m_orignPoint = PathSearchInfo::m_inspectArray[PathSearchInfo::m_startX][PathSearchInfo::m_startY]->m_sprite->getPosition();
        m_player->m_sprite->setPosition(m_orignPoint);//设置人物的起始的世界坐标
    
        
        
        return true;
    }
    void HelloWorld::calculatePath()
    {
        
        //得到開始点的节点
        PathSprite* _startNode = PathSearchInfo::m_inspectArray[PathSearchInfo::m_startX][PathSearchInfo::m_startY];
        //得到结束点的节点
        PathSprite* _endNode = PathSearchInfo::m_inspectArray[PathSearchInfo::m_endX][PathSearchInfo::m_endY];
        
        //由于是開始点 把到起始点的距离设为0, F值也为0
        _startNode->m_costToSource = 0;
        _startNode->m_FValue = 0;
        
        //把已经检測过的点从检測列表中删除
        PathSearchInfo::m_inspectArray[PathSearchInfo::m_startX][PathSearchInfo::m_startY] = NULL;
        //把该点放入已经检測过点的列表中
        PathSearchInfo::m_haveInspectList.push_back(_startNode);
        //然后增加开放列表
        PathSearchInfo::m_openList.push_back(_startNode);
        
        PathSprite* _node = NULL;
        while (true)
        {
            //得到离起始点近期的点(假设是第一次运行, 得到的是起点)
            _node = PathSearchInfo::getMinPathFormOpenList();
            if (!_node)
            {
                //找不到路径
                break;
            }
            //把计算过的点从开放列表中删除
            PathSearchInfo::removeObjFromOpenList( _node);
            int _x = _node->m_x;
            int _y = _node->m_y;
            
            //
            if (_x ==PathSearchInfo::m_endX && _y == PathSearchInfo::m_endY)
            {
                break;
            }
            
            //检測8个方向的相邻节点能否够放入开放列表中
            CCLog("%d, %d",_x, _y);
            PathSprite* _adjacent = PathSearchInfo::getObjFromInspectArray( _x + 1, _y + 1);
            PathSearchInfo::inspectTheAdjacentNodes(_node, _adjacent, _endNode);
            
            _adjacent = PathSearchInfo::getObjFromInspectArray(  _x +1, _y);
            PathSearchInfo::inspectTheAdjacentNodes(_node, _adjacent, _endNode);
            
            _adjacent = PathSearchInfo::getObjFromInspectArray(  _x +1, _y-1);
            PathSearchInfo::inspectTheAdjacentNodes(_node, _adjacent, _endNode);
            
            _adjacent = PathSearchInfo::getObjFromInspectArray(  _x , _y -1);
            PathSearchInfo::inspectTheAdjacentNodes(_node, _adjacent, _endNode);
            
            _adjacent = PathSearchInfo::getObjFromInspectArray(  _x -1, _y - 1);
            PathSearchInfo::inspectTheAdjacentNodes(_node, _adjacent, _endNode);
            
            _adjacent = PathSearchInfo::getObjFromInspectArray(  _x -1, _y);
            PathSearchInfo::inspectTheAdjacentNodes(_node, _adjacent, _endNode);
            
            _adjacent = PathSearchInfo::getObjFromInspectArray(  _x -1, _y+1);
            PathSearchInfo::inspectTheAdjacentNodes(_node, _adjacent, _endNode);
            
            _adjacent = PathSearchInfo::getObjFromInspectArray(  _x , _y+1);
            PathSearchInfo::inspectTheAdjacentNodes(_node, _adjacent, _endNode);
            
        }
        
        while (_node)
        {
            //把路径点增加到路径列表中
            PathSearchInfo::m_pathList.insert(PathSearchInfo::m_pathList.begin(), _node);
            _node = _node->m_parent;
        }
    }
    
    
    void HelloWorld::drawPath(  )
    {
        for (vector<PathSprite*>::iterator iter = PathSearchInfo::m_pathList.begin(); iter !=  PathSearchInfo::m_pathList.end(); iter++)
        {
            (*iter)->m_sprite->setColor(ccGREEN);
        }
        
    }
    CCRect getBoundingBox(float x, float y, float width, float height)
    {
        return CCRect(x - width/2, y - height/2, width, height);
    }
    bool HelloWorld::ccTouchBegan(CCTouch* touch, CCEvent* event)
    {
    
        //清除之前的路径
        clearPath();
        
        auto nodePosition = convertToNodeSpace( touch->getLocation() );
        CCLog("%f, %f", nodePosition.x, nodePosition.y);
    //    for (int i = 0; i < PathSearchInfo::m_inspectList.size(); i++)
    //    {
    //        PathSprite* _sp = PathSearchInfo::m_inspectList[i];
    //        
    //        CCRect _rect = getBoundingBox( _sp->m_sprite->getPositionX(), _sp->m_sprite->getPositionY(), _sp->m_sprite->getContentSize().width, _sp->m_sprite->getContentSize().height);
    //        
    //        if (_rect.containsPoint(nodePosition))
    //        {
        PathSprite* _sp = PathSearchInfo::m_inspectArray[(int)(nodePosition.x/PathSearchInfo::m_tileSize.width)][(int)(PathSearchInfo::m_mapSize.height - nodePosition.y/PathSearchInfo::m_tileSize.height)];
        if (_sp) {
            CCLog("%f, %f", _sp->m_sprite->getPositionX(), _sp->m_sprite->getPositionY());
            //获取触摸点, 设置为终点
            PathSearchInfo::m_endX = _sp->m_x;
            PathSearchInfo::m_endY = _sp->m_y;
            //计算路径
            calculatePath();
            //绘制路径
            drawPath(  );
            //移动物体
            playerMove();
        }
        
        
    //        }
    //        
    //    }
    
        return true;
    }
    
    void HelloWorld::ccTouchMoved(CCTouch* touch, CCEvent* event)
    {
    
    }
    
    
    
    void HelloWorld::ccTouchEnded(CCTouch* touch, CCEvent* event)
    {
    
    }
    
    void HelloWorld::menuCloseCallback(CCObject* pSender)
    {
    #if (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT) || (CC_TARGET_PLATFORM == CC_PLATFORM_WP8)
    	CCMessageBox("You pressed the close button. Windows Store Apps do not implement a close button.","Alert");
    #else
        CCDirector::sharedDirector()->end();
    #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
        exit(0);
    #endif
    #endif
    }
    
    void HelloWorld::clearPath()
    {
        for (vector<PathSprite*>::iterator iter = PathSearchInfo::m_haveInspectList.begin(); iter !=  PathSearchInfo::m_haveInspectList.end(); iter++)
        {
            (*iter)->m_sprite->setColor(ccWHITE);
            (*iter)->m_costToSource = 0;
            (*iter)->m_FValue = 0;
            (*iter)->m_parent = NULL;
            (*iter)->m_child = NULL;
            
            PathSearchInfo::m_inspectArray[(*iter)->m_x][(*iter)->m_y] = (*iter);
        }
        
        //把移除了障碍物的地图放入检測列表中
        //PathSearchInfo::m_inspectList = PathSearchInfo::m_mapList;
        PathSearchInfo::m_openList.clear();
        PathSearchInfo::m_pathList.clear();
        PathSearchInfo::m_haveInspectList.clear();
        PathSearchInfo::m_startX = m_player->m_x;
        PathSearchInfo::m_startY = m_player->m_y;
        m_player->m_sprite->stopAllActions();
        
        m_playerMoveStep = 0;
    }
    
    void HelloWorld::playerMove()
    {
        m_playerMoveStep++;
        
        if (m_playerMoveStep >= PathSearchInfo::m_pathList.size()) {
            return;
        }
        
        m_player->m_x = PathSearchInfo::m_pathList[m_playerMoveStep]->m_x;
        m_player->m_y = PathSearchInfo::m_pathList[m_playerMoveStep]->m_y;
        
        //依据路径列表移动人物
        m_player->m_sprite->runAction(CCSequence::create(CCMoveTo::create(1/24.0, PathSearchInfo::m_pathList[m_playerMoveStep]->m_sprite->getPosition()), CCCallFunc::create(this, SEL_CallFunc(&HelloWorld::playerMove)) , NULL));
        
    }
    void HelloWorld::update(float dt)
    {
        this->setPosition(m_orignPoint - m_player->m_sprite->getPosition());
    }
    



    版权声明:本文博客原创文章,博客,未经同意,不得转载。

  • 相关阅读:
    Windows API Reference for C#, VB.NET and VB6
    工程建设项目综合信息管理系统
    GridView,Repeater分页控件:WebPager 开源了
    asp.net服务器控件开发学习之路(一)
    ajaxToolkit:TabContainer 背景色的设置
    TreeView 树结构的断层处理
    C# 集合类(二):Queue
    AutoCAD.net(三)用VS.NET2005开发ObjectARX程序 调试方法
    AutoCAD.net(一):更改AutoCAD窗口的标题和图标
    C# 集合类(四):Hashtable
  • 原文地址:https://www.cnblogs.com/mengfanrong/p/4723868.html
Copyright © 2011-2022 走看看