概述
最近在研究cocos2dx的地图,最开始使用的是Tiled,这个编辑器做比较小的地图还是比较强大的,不过做大地图的时候,有一些功能不太方便并且有缺陷(包括刷图繁琐以及坐标体系过于复杂,导致寻路比较看起来很不平滑)。于是就酝酿着自己写一个斜45度的地图编辑器。
现在的自己老是不能集中注意力,经常出现思考着思考着就想睡觉的情况,所以导致这个工具写了2周,汗颜啊!使用MFC+GDI实现,目前已经完成的功能有:
1、tile坐标系设计和转换,右下X轴,右上Y轴。这是实现45度地图的关键呀。
2、拆分合并图片功能,我的这个地编需要在编辑时传入一张大图(贴小图的方式没有实现,自己太懒),在导出的时候在拆成小图。
3、刷阻挡位,刷透明位,批量刷阻挡以及清除阻挡和透明等。
4、一些显示和隐藏等状态设置的小功能。
目前基本能用了,后期准备加入配置NPC和热区的功能,暂时没有时间来实现。还有就是当前的合并图片和拆分图片使用的是CImage的功能,感觉拆出来的图比原图质量差一些,以后有时间再优化完善吧。
先上一个图吧,看看成果:
再上关键算法:
tile编码的算法:
enum OCC_FLAG { F_BLOCK = 0x01, F_ALPHA = 0x02, }; struct TileData { CPoint tile; BYTE flag; TileData() : flag(0) {} }
for(int j = 0; j < m_yTile * 2 - 1; j++) { for(int i = 0; i < m_xTile * 2 - 1; i++) { if(i % 2 == j % 2) { CPoint ct = CPoint((i+1) * m_iGridWidth, m_iGridHeigh * (j + 1)); TileData td; td.tile = Space2Tile(ct); int idx = td.tile.x * 1000 + td.tile.y; m_tiles.insert(std::make_pair(idx, td)); } } }
CPoint CMy45MapEditorDoc::Space2Tile(CPoint& screen) { CPoint center; GetCursorDiamond(screen, center); int x=(center.x/(m_iGridWidth * 2)+(center.y+m_iGridHeigh)/(m_iGridHeigh*2)); int y=center.x/(m_iGridWidth * 2)-center.y/(m_iGridHeigh*2)+m_yTile; return CPoint(x, y); }
//参数: // POINT p 指定的某个点 // LPPOINT ptPolygon 多边形的各个顶点坐标(首末点可以不一致) // int nCount 多边形定点的个数 BOOL PtInPolygon(POINT p, LPPOINT ptPolygon, int nCount) { int nCross = 0; for (int i = 0; i < nCount; i++) { POINT p1 = ptPolygon[i]; POINT p2 = ptPolygon[(i + 1) % nCount]; // 求解 y=p.y 与 p1p2 的交点 if ( p1.y == p2.y ) // p1p2 与 y=p0.y平行 continue; if ( p.y < min(p1.y, p2.y) ) // 交点在p1p2延长线上 continue; if ( p.y >= max(p1.y, p2.y) ) // 交点在p1p2延长线上 continue; // 求交点的 X 坐标 -------------------------------------------------------------- double x = (double)(p.y - p1.y) * (double)(p2.x - p1.x) / (double)(p2.y - p1.y) + p1.x; if ( x > p.x ) nCross++; // 只统计单边交点 } // 单边交点为偶数,点在多边形之外 --- return (nCross % 2 == 1); } //判断点在不在菱形中 //pt-点指针 //x--菱形中心点横坐标 //y--菱形中心点纵坐标 BOOL CMy45MapEditorDoc::IsPtInDiamond(CPoint& pt, int x, int y) { POINT point4[4]; point4[0].x = x - m_iGridWidth; point4[0].y = y; point4[1].x = x; point4[1].y = y - m_iGridHeigh; point4[2].x = x + m_iGridWidth; point4[2].y = y; point4[3].x = x; point4[3].y = y + m_iGridHeigh; return PtInPolygon(pt, point4, 4); } //获取鼠标点中的那个菱形的中心点 //pt-------鼠标位置 //pCenter--返回中心点坐标 BOOL CMy45MapEditorDoc::GetCursorDiamond(CPoint& pt, CPoint& pCenter) { RECT Rect; CRgn diamond; Rect.left = pt.x / m_iGridWidth * m_iGridWidth; Rect.top = pt.y / m_iGridHeigh * m_iGridHeigh; Rect.right = Rect.left + m_iGridWidth; Rect.bottom = Rect.top + m_iGridHeigh; if((Rect.left / m_iGridWidth % 2) == (Rect.top / m_iGridHeigh % 2)) { pCenter.x = Rect.left, pCenter.y = Rect.top; if(IsPtInDiamond(pt, Rect.left, Rect.top) == TRUE) return TRUE; } if((Rect.right / m_iGridWidth % 2) == (Rect.top / m_iGridHeigh % 2)) { pCenter.x = Rect.right, pCenter.y = Rect.top; if(IsPtInDiamond(pt, Rect.right, Rect.top) == TRUE) return TRUE; } if((Rect.left / m_iGridWidth % 2) == (Rect.bottom / m_iGridHeigh % 2)) { pCenter.x = Rect.left, pCenter.y = Rect.bottom; if(IsPtInDiamond(pt, Rect.left, Rect.bottom) == TRUE) return TRUE; } if((Rect.right / m_iGridWidth % 2) == (Rect.bottom / m_iGridHeigh % 2)) { pCenter.x = Rect.right, pCenter.y = Rect.bottom; if(IsPtInDiamond(pt, Rect.right, Rect.bottom)==TRUE) return TRUE; } return FALSE; }
上面的代码就是生产tile的规则,下面是保存地图的代码:BOOL CMy45MapEditorDoc::OnSaveDocument(LPCTSTR lpszPathName) { try { Blaze::ByteBuffer buff; buff << (int)m_mapSize.cx << (int)m_mapSize.cy; //地图尺寸 buff << m_iGridWidth << m_iGridHeigh; //tile大小 buff << m_xTile << m_yTile; //tile尺寸 buff << m_xSplit << m_ySplit; //图元尺寸 std::string fileNameDif = groundPath; fileNameDif = fileNameDif.substr(fileNameDif.rfind(".") + 1, fileNameDif.size()); buff << fileNameDif; buff << (int)(m_tiles.size()); //tile数量 for (TileMap::iterator it = m_tiles.begin(), ite = m_tiles.end(); it != ite; ++it) { int idx = it->first; TileData* tileD = &it->second; buff << idx << tileD->flag; } FILE* file = fopen(lpszPathName, "wb"); if (file) { fwrite(buff.contents(), 1, buff.size(), file); fclose(file); theApp.SpliteImage(groundPath, m_xSplit, m_ySplit, lpszPathName); } } catch (CException* e) { AfxMessageBox("保存地图文件失败!"); return FALSE; } return TRUE; }
最后是读取地图的代码:BOOL CMy45MapEditorDoc::OnOpenDocument(LPCTSTR lpszPathName) { if (!CDocument::OnOpenDocument(lpszPathName)) return FALSE; try { Blaze::ByteBuffer buff; FILE* file = fopen(lpszPathName, "rb"); if (file) { while (!feof(file)) { char buf[1024]; int size = fread(buf, 1, 1024, file); buff.append((unsigned char*)buf, size); } int sx, sy; buff >> sx >> sy; //地图尺寸 buff >> m_iGridWidth >> m_iGridHeigh; //tile大小 buff >> m_xTile >> m_yTile; //tile尺寸 buff >> m_xSplit >> m_ySplit; //图元尺寸 m_mapSize = CSize(sx, sy); std::string fileNameDif; buff >> fileNameDif; std::string fileName(lpszPathName); fileName = fileName.substr(0, fileName.rfind(".")); groundPath.Format("%s.%s", fileName.c_str(), fileNameDif.c_str()); if(m_pict.Load(groundPath)) { int tileSize; buff >> tileSize; for (int i = 0; i < tileSize; ++i) { TileData td; int tileIdx; buff >> tileIdx >> td.flag; td.tile.y = tileIdx % 1000; td.tile.x = tileIdx / 1000; m_tiles.insert(std::make_pair(tileIdx, td)); } } return TRUE; } } catch (CException* e) { AfxMessageBox("打开地图文件失败!"); } return FALSE; }
配上原来实现的A*算法寻路效果相当好,在安卓上运行内存占用比用Tiled少了一半多,后面还可以加上底图视野管理,每次只加载9张底图,内存占用会更少。
下面给个测试版给大家试用一下,大家有什么意见可以反馈给我,暂时不开源,等完善之后再考虑开源吧!
45度地图编辑器DEMO
最后
以上就是无语黄豆为你收集整理的编写简易斜45度地图编辑器的全部内容,希望文章能够帮你解决编写简易斜45度地图编辑器所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复