本文展示了使用cocos2d-x来实现的一个迷宫探索演示程序,一种简单粗暴的寻路算法,算法来自经典的严蔚敏c语言数据结构里的迷宫探索算法,使用栈来实现。
效果图按钮功能:
Begin : 开始演示
reset: 重新生成随机地图,地图是可以编辑,点击每个掉块翻转状态(墙或者路)
State: 显示地图的数字状态,调试用。
Edit: 在进行过探索之后,可以通过Edit对地图进行微调,即可以不需要全部reset。Edit之后再点Begin,重新进行探索。
下面给出代码实现,算法和界面是分开的两部分,后面如果要集成新的寻路算法,界面部分是可以公用的,定义新的寻路算法仅需要继承类:,并实现寻路算法接口。
代码 算法部分1. 算法基类:MazeStrategy.h
#pragma once#include "cocos2d.h"class MazeStrategy {public: typedef cocos2d::Vec2 MPoint; typedef cocos2d::Size MSize; struct MazeCell { MPoint pos; int state; }; static const int kStateWall = 0; static const int kStatePass = 1; static const int kStateTryFail = 2; static const int kStateInStack = 3; typedef std::vector<MazeCell> CellArray; typedef std::vector<CellArray> Maze; int _rows; int _columns; float _cellWIDth; Maze _maze; MazeStrategy(int rows = 10,int columns = 10) : _rows(rows),_columns(columns) { srand(time(0)); } virtual ~MazeStrategy() { } virtual voID clearState() { for (auto &row : _maze) { for (auto &column: row) { column.state = (column.state > 0) ? kStatePass : kStateWall; } } } virtual voID resetMaze(MSize winSize,MPoint origin) { _cellWIDth = std::min(winSize.wIDth / _columns,winSize.height / _rows); MPoint offsetForAnchorPoint(_cellWIDth/2,_cellWIDth/2); _maze.resize(_rows); int i,j; i = j = 0; for (auto &row : _maze) { row.resize(_columns); j = 0; for (auto &column: row) { column.pos.x = origin.x + j++ * _cellWIDth + offsetForAnchorPoint.x; column.pos.y = origin.y + i * _cellWIDth + offsetForAnchorPoint.y; column.state = (rand()*1.0 > RAND_MAX / 7.0 * 5) ? kStateWall : kStatePass; } ++i; } _maze[0][0].state = kStatePass; _maze[_rows-1][_columns-1].state = kStatePass; } voID updateMazeCell(int i,int j,int val) { _maze[i][j].state = val; } Maze& getMaze() { return _maze; } struct TickResult { MPoint pos; int result; }; virtual TickResult tickStep() { return {MPoint(0,0),0}; }};
2. 基于栈的解法:StackMazeStrategy.h
#pragma once#include "MazeStrategy.h"#include <stack>#include <array>class StackMazeStrategy: public MazeStrategy {public: StackMazeStrategy(int row = 10,int col = 10) : MazeStrategy(row,col) { } struct Pos { int _row; int _col; Pos(int row = -1,int col = -1) : _row(row),_col(col) { } bool operator == (const Pos& rhs) const { return (rhs._row == _row && rhs._col == _col); } bool operator != (const Pos& rhs) const { return !(*this == rhs); } voID makeValID(int size) { _row = std::max(0,_row); _col = std::max(0,_col); _row = std::min(_row,size); _col = std::min(_col,size); } }; std::stack<Pos> _stack; Pos _currentPos; Pos getNextPos(const Pos& pos) { std::array<Pos,4> neigboors = { Pos(pos._row + 1,pos._col),Pos(pos._row,pos._col + 1),Pos(pos._row - 1,pos._col - 1),}; Pos Noway = {-1,-1}; for (auto& posi : neigboors) { posi.makeValID(_rows-1); if ( (posi != pos) && (posi != Noway) && (_maze[posi._row][posi._col].state == MazeStrategy::kStatePass) ) { return posi; } } return Noway; } voID markMaze(const Pos& pos,int t) { _maze[pos._row][pos._col].state = t; } voID clearState() overrIDe { MazeStrategy::clearState(); std::stack<Pos> stemp; stemp.swap(_stack); _stack.push({0,0}); _currentPos = _stack.top(); markMaze(_currentPos,MazeStrategy::kStateInStack); } voID resetMaze(MSize visibleSize,MPoint origin) overrIDe { MazeStrategy::resetMaze(visibleSize,origin); std::stack<Pos> stemp; stemp.swap(_stack); _stack.push({0,MazeStrategy::kStateInStack); } TickResult tickStep() overrIDe { int result(0); if (_stack.empty()) { return {_maze[_currentPos._row][_currentPos._col].pos,0}; //fail } Pos outlet = {_rows - 1,_columns - 1}; if (_currentPos == outlet) {// printSolution(st);// break; return {_maze[outlet._row][outlet._col].pos,1}; // ok } Pos nextPos = getNextPos(_currentPos); Pos Noway = {-1,-1}; if (nextPos == Noway) { _stack.pop(); markMaze(_currentPos,MazeStrategy::kStateTryFail); if (!_stack.empty()) { _currentPos = _stack.top(); result = 2; // need next step } } else { _stack.push(nextPos); markMaze(nextPos,MazeStrategy::kStateInStack); _currentPos = nextPos; result = 2; } return {_maze[_currentPos._row][_currentPos._col].pos,result}; }};界面展示部分: MatrixExplore.h
#pragma once#include "cocos2d.h"#include <vector>#include <CCDirector.h>class MazeStrategy;class MatrixExplore : public cocos2d::Layer{public: MatrixExplore() : _mazeStrategy(nullptr),_playing(false),_robot(nullptr),_door(nullptr),_showState(false){} ~MatrixExplore() { CC_SAFE_DELETE(_mazeStrategy); } voID onreset(cocos2d::Ref* target); voID onBegin(cocos2d::Ref* target); voID onShowState(cocos2d::Ref* target); voID onEdit(cocos2d::Ref* target); voID onMazeClicked(cocos2d::Ref* target); voID tick(float dt); static cocos2d::Scene* createScene(); virtual bool init(); voID reloadUI(); voID loadMazeMap(); voID reloadMazeStrategy(); voID refreshState(); CREATE_FUNC(MatrixExplore);private: MazeStrategy* _mazeStrategy; bool _playing; bool _showState; cocos2d::Sprite* _robot; cocos2d::Sprite* _door;};
MatrixExplore.cpp
#include "MatrixExplore.h"#include "StackMazeStrategy.h"USING_NS_CC;Scene* MatrixExplore::createScene(){ auto scene = Scene::create(); auto layer = MatrixExplore::create(); scene->addChild(layer); return scene;}voID MatrixExplore::loadMazeMap() { auto& maze = _mazeStrategy->getMaze(); int rows = _mazeStrategy->_rows; int columns = _mazeStrategy->_columns; Vector<MenuItem*> spriteBtns; for (size_t i=0; i<rows; ++i) { for (size_t j=0; j<columns; ++j) { std::string imgname = (maze[i][j].state == MazeStrategy::kStateWall) ? "stone.jpg" : "grass.jpg"; auto btnsprite = MenuItemImage::create(imgname,imgname,CC_CALLBACK_1(MatrixExplore::onMazeClicked,this)); btnsprite->setTag(i*100 + j); CCAssert(btnsprite,"fail to create bnt sprite"); Size cts = btnsprite->getContentSize(); btnsprite->setScaleX(_mazeStrategy->_cellWIDth / cts.wIDth); btnsprite->setScaleY(_mazeStrategy->_cellWIDth / cts.height); btnsprite->setposition(maze[i][j].pos); spriteBtns.pushBack(btnsprite); if (_showState) { std::string state = std::to_string(maze[i][j].state); auto label = Label::createWithTTF(state,"Fonts/arial.ttf",50); label->setcolor(color3B(255,0,0)); label->setposition(_mazeStrategy->_cellWIDth / 2,_mazeStrategy->_cellWIDth / 2); btnsprite->addChild(label); } } } auto mazeMenu = Menu::createWithArray(spriteBtns); addChild(mazeMenu); mazeMenu->setposition(Vec2::ZERO); if (!_robot) { _robot = Sprite::create("HelloWorld.png"); Size cts = _robot->getContentSize(); _robot->setScaleX(_mazeStrategy->_cellWIDth / cts.wIDth); _robot->setScaleY(_mazeStrategy->_cellWIDth / cts.height); _robot->setposition(maze[0][0].pos); addChild(_robot); _robot->setLocalZOrder(100); } if (!_door) { _door = Sprite::create("door.jpg"); Size cts = _door->getContentSize(); _door->setScaleX(_mazeStrategy->_cellWIDth / cts.wIDth); _door->setScaleY(_mazeStrategy->_cellWIDth / cts.height); _door->setposition(maze[_mazeStrategy->_rows - 1][_mazeStrategy->_columns - 1].pos); addChild(_door); _door->setLocalZOrder(100); }}voID MatrixExplore::refreshState() {}voID MatrixExplore::reloadUI() { removeAllChildrenWithCleanup(true); _robot = nullptr; _door = nullptr; auto resetBtn = MenuItemLabel::create(Label::createWithTTF("reset",24),CC_CALLBACK_1(MatrixExplore::onreset,this)); auto beginBtn = MenuItemLabel::create(Label::createWithTTF("Begin",CC_CALLBACK_1(MatrixExplore::onBegin,this)); auto showStateBtn = MenuItemLabel::create(Label::createWithTTF("State",CC_CALLBACK_1(MatrixExplore::onShowState,this)); auto editBtn = MenuItemLabel::create(Label::createWithTTF("Edit",CC_CALLBACK_1(MatrixExplore::onEdit,this)); beginBtn->setpositionY(resetBtn->getContentSize().height + 20); editBtn->setpositionY(beginBtn->getpositionY() + beginBtn->getContentSize().height + 20); showStateBtn->setpositionY(editBtn->getpositionY() + editBtn->getContentSize().height + 20); auto menu = Menu::create(resetBtn,beginBtn,editBtn,showStateBtn,NulL); menu->setpositionX(Director::getInstance()->getVisibleSize().wIDth - resetBtn->getContentSize().wIDth); menu->setpositionY(resetBtn->getContentSize().height + 20); addChild(menu); loadMazeMap();}voID MatrixExplore::reloadMazeStrategy() { if (!_mazeStrategy) { _mazeStrategy = new StackMazeStrategy(10,10); } _mazeStrategy->resetMaze(Director::getInstance()->getVisibleSize(),Director::getInstance()->getVisibleOrigin());}bool MatrixExplore::init(){ if ( !Layer::init() ) { return false; } reloadMazeStrategy(); reloadUI(); return true;}voID MatrixExplore::tick(float dt) { auto tickResult = _mazeStrategy->tickStep(); switch (tickResult.result) { case 0: // fail MessageBox("no way anymore","Maze Explore"); unschedule(CC_SCHEDulE_SELECTOR(MatrixExplore::tick)); break; case 1: // ok MessageBox("i'm out","Maze Explore"); unschedule(CC_SCHEDulE_SELECTOR(MatrixExplore::tick)); break; case 2: // keep on. cclOG("i'm going on...\n"); break; default: break; } _robot->setposition(tickResult.pos);}voID MatrixExplore::onreset(Ref* target) { unschedule(CC_SCHEDulE_SELECTOR(MatrixExplore::tick)); reloadMazeStrategy(); reloadUI(); _playing = false;}voID MatrixExplore::onBegin(Ref* target) { _playing = true; schedule(CC_SCHEDulE_SELECTOR(MatrixExplore::tick),0.3);}voID MatrixExplore::onShowState(Ref* target) { if (_playing) {// unschedule(CC_SCHEDulE_SELECTOR(MatrixExplore::tick)); } _showState = !_showState; reloadUI(); if (_playing) {// schedule(CC_SCHEDulE_SELECTOR(MatrixExplore::tick),0.3); }}voID MatrixExplore::onEdit(Ref* target) { unschedule(CC_SCHEDulE_SELECTOR(MatrixExplore::tick)); _mazeStrategy->clearState(); _playing = false; reloadUI();}voID MatrixExplore::onMazeClicked(Ref* target) { if (_playing) { MessageBox("use EIDt or reset menu","Maze Explore"); return; } auto menuItemImage = dynamic_cast<MenuItemImage*>(target); CCAssert(menuItemImage,"clicked on nil"); int tag = menuItemImage->getTag(); int i = tag / 100; int j = tag % 100; cclOG("edit (%d,%d)\n",i,j); auto& maze = _mazeStrategy->getMaze(); if (maze[i][j].state == _mazeStrategy->kStateWall) { _mazeStrategy->updateMazeCell(i,j,MazeStrategy::kStatePass); menuItemImage->setnormalimage(Sprite::create("grass.jpg")); } else { _mazeStrategy->updateMazeCell(i,MazeStrategy::kStateWall); menuItemImage->setnormalimage(Sprite::create("stone.jpg")); } Size cts = menuItemImage->getContentSize(); menuItemImage->setScaleX(_mazeStrategy->_cellWIDth / cts.wIDth); menuItemImage->setScaleY(_mazeStrategy->_cellWIDth / cts.height);}
作者水平有限,对相关知识的理解和总结难免有错误,还望给予指正,非常感谢!
在这里也能看到这篇文章:github博客,CSDN博客,欢迎访问
总结以上是内存溢出为你收集整理的基于Cocos2d-x的迷宫探索演示程序全部内容,希望文章能够帮你解决基于Cocos2d-x的迷宫探索演示程序所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)