首页 > 其他分享 >编写简易斜45度地图编辑器

编写简易斜45度地图编辑器

时间:2024-01-23 14:56:34浏览次数:34  
标签:return iGridWidth int 45 简易 编辑器 iGridHeigh Rect buff

最近在研究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张底图,内存占用会更少。

       下面给个测试版给大家试用一下,大家有什么意见可以反馈给我,暂时不开源,等完善之后再考虑开源吧!

 

Demo下载地址:

https://download.csdn.net/download/ycg514230/6829523

标签:return,iGridWidth,int,45,简易,编辑器,iGridHeigh,Rect,buff
From: https://www.cnblogs.com/kt007/p/17982446

相关文章

  • 斜45度瓦片地图(Staggered Tiled Map)里的简单数学
    转载至:ShuaiYUAN斜45度瓦片地图(StaggeredTiledMap)里的简单数学前段时间在做游戏的地图编辑功能,我们是在一个斜45度视角的场景上,对地图上的建筑或装饰物进行添加、移动、移除等基本操作,而且位置的改变是以网格作为最小操作单位的。本渣是用StaggeredTiledMap实现的,与垂直视......
  • P4588 [TJOI2018] 数学计算
    题目描述小豆现在有一个数x,初始值为1。小豆有Q次操作,操作有两种类型:1m:将x变为×*m,并输出xmodM。2pos:将x变为x除以第pos次操作所乘的数(保证第pos次操作一定为类型1,对于每一个类型1的操作至多会被除一次),并输出xmodM。输入格式一共有t组输入。对于每一......
  • CMU15445 Concurrency Control
    LockManagerlock检查事务的隔离级别是否符合锁的要求REPEATABLE_READ:Thetransactionisrequiredtotakealllocks.AlllocksareallowedintheGROWINGstateNolocksareallowedintheSHRINKINGstateREAD_COMMITTED:Thetransactionisrequi......
  • Linux基础45 firewalld防火墙, 参数, 区域配置, 放行策略, 端口转发, 富规则, 防火墙
    firewalld防火墙一、防火墙安全概述在Centos7系统中继承了多款防火墙管理工具,默认启动的是firewalld(动态防火墙管理器)防火墙管理工具,Firewalld支持CLI(命令行)以及(图形)的两种管理方式。对于接触Linux较早的人员对Iptables比较熟悉,但由于Iptables的规则比较的麻烦,并且对网络有......
  • CF455D Serega and Fun 题解
    题目链接:CF或者洛谷本题是可以用平衡树去做的,具体的为每个\(k\)开一棵平衡树去维护相对位置,而这种移动操作用平衡树维护又是很容易做到的,这种做法是双\(log\)。在\(1e5\)的数据下,我们来说说好写的分块该如何去写。黑色的代表一个块,考虑暴力修改情况,假如原来的数字为\([1......
  • Go语言核心36讲 45 | 使用os包中的API (下)
    你好,我是郝林,今天我们继续分享使用os包中的API。我们在上一篇文章中。从“os.File类型都实现了哪些io包中的接口”这一问题出发,介绍了一系列的相关内容。今天我们继续围绕这一知识点进行扩展。知识扩展问题1:可应用于File值的操作模式都有哪些?针对File值的操作模式主要有只读......
  • python3使用socket模块实现简易syslog服务器
    废话不多说直接上代码1importsocket2importtime345defsocket_bind(server_socket,host,port=514,max_retries=5):6retries=07whileretries<max_retries:8try:9server_socket.bind((host,port))10......
  • 45个经典Linux面试题!赶紧收藏!
    问题一:绝对路径用什么符号表示?当前目录、上层目录用什么表示?主目录用什么表示?切换目录用什么命令?答案:绝对路径:如/etc/init.d当前目录和上层目录:./../主目录:~/切换目录:cd问题二:怎么查看当前进程?怎么执行退出?怎么查看当前路径?答案:查看当前进程:ps执行退出:exit查看当前路径:pwd问题三......
  • 正确理解springboot国际化简易运行流程
    看源码可以看出–》大致原理localeResolver国际化视图(默认的就是根据请求头带来的区域信息获取Locale进行国际化)返回的本地解析是根据响应头来决定的)接着按住ctrl点localeresolver可知localeresolver是一个接口于是有了这些我们只需通过继承LocaleResolver来自定义我们自己的Loca......
  • LG8459
    这题一看到要判断\(a\timesb=c\)是否成立,立马想到了用FFT/NTT。但看到数据范围\(a,b\le10^n\),\(c\le10^{2n}\),\(n\le1\times10^6+50\),再加上时限很紧(\(1\)秒),因此\(O(Tn\logn)\)的FFT/NTT会超时。既然暴力求解不行,我们不妨从数学的角度思考这个问题。还......