Edting While Playing 瓦片地图编辑器开发整合导入自定义贴图 DEVC++ VS2022都可复制粘贴运行

多种类型图片模块读取-CSDN博客

Editing While Playing 使用 Easyx 开发的 RPG 地图编辑器 tilemap eaitor-CSDN博客

整合实现平面贴图纹理自定义

操作同上

导入步骤:

先运行程序,然后关闭,同目录下有四个文件夹,

把自己准备的 30*30 像素的 png 格式的图片粘贴进去。无需改名。

或者随便粘贴图片,不过加载的时候只会加载左上角 30*30 的部分

然后启动程序,发现会有自定义贴图加载出来。

绘制完

F1 还可以导出为 DLC,DLC 内自动复制粘贴并按程序内的顺序重命名了

复制代码
// 程序:2D RPG 地图编辑器
// 作者:民用级脑的研发记录
// 邮箱:[email protected]
// 编译环境:Devc++/VC 2010/Visual Studio 2022,EasyX_20220901/Easyx_2023 大暑版
// 编写日期:2024-2-22
//
#undef UNICODE
#undef _UNICODE
#pragma warning(disable : 4996)		// VS2022 对某些函数警告,但是为了方便移植,就无视这些警告 这样 Devc++ VC2010 VS2022 都能跑
#include <graphics.h>
#include <stdio.h>
#include <string.h>
#include <io.h>
#include <direct.h>
// 从默认文件夹中读取自定义图片
void loadfile(IMAGE*** bentablev2);
// 加载自定义图片
void loadfile_scanf(char* dirname, char* next, char* kind, IMAGE** pentablev2);
// 保存自定义文件
void savepersonalfile(char* DLC, IMAGE*** pentablev2);
// 保存自定义瓦片贴图
void savelocal(char* DLC, char* dirname, char* next, char* kind, char* sign, IMAGE** files);
// 实时渐变色小动画
inline void animation(int chararcterflag, int bkgameleft, int bkgametop, int bkmeshgameleft, int bkmeshgametop, int bkmeshdeskleft, int bkmeshdesktop);
// 边界检查,move 是平移大小,单位:像素,若使得寄存区在九宫格中心,move 的大小就是寄存区的边长,相应的 size 的大小是 move 的两倍。这样无论长宽都是 3 倍。size 是大小区域,代表不会触发缓冲区更新的范围,单位:像素
inline void checkboundary(int* bkgameleft, int* bkgametop, int gamelimitright, int gamelimitbuttom, int* bkmeshleft, int* bkmeshtop, int* meshlimit, int* meshlimitbutton, int move, int size);
// 保存游戏地图贴图数据
void savegamemap(int** map, int gamemapi, int gamemapj, IMAGE* pentable, IMAGE*** pentablev2, int pixnum, int imagenum);
// 导入游戏地图贴图数据
void loadgamemap(int** map, int* gamemapi, int* gamemapj, IMAGE* pentable, int* pixnum, int* imagenum);
// 使用关键字 inline 声明为内联函数,减少贴图函数频繁调用的开销导致的卡顿。
// 缓冲区纹理映射函数:bkmesh 映射目标,map 映射总网格,pentable:纹理集,bkmeshmapi,bkmeshmapj:映射起始点,tilenum:横,纵映射的数量,pixnum:一个映射块的边长,单位:像素。
inline void freshmesh(IMAGE* bkmesh, int** map, IMAGE* pentable, IMAGE*** pentablev2, int bkmeshmapi, int bkmeshmapj, int tilenum, int pixnum)
{
	int kind = 0;											// 存储代号第一位:类型
	int number = 0;											// 存储代号第二位:序列号
	int pennumber = -1;										// 暂存每一次循环的映射代号
	IMAGE* pen = NULL;										// 所找到的纹理
	int left = 0;											// 这是每次循环所找到的纹理对应映射地址
	int top = 0;
	SetWorkingImage(bkmesh);								// 设置绘图目标为游戏背景采样区,刷新采样区,刷新寄存区
	cleardevice();
	for (int i = bkmeshmapi; i < bkmeshmapi + tilenum; i++)
	{
		left = 0;
		for (int j = bkmeshmapj; j < bkmeshmapj + tilenum; j++)
		{
			pennumber = map[i][j];							// 读取游戏大地图数组序号
			if (pennumber == -1)
			{
				rectangle(left, top, left + pixnum, top + pixnum);
			}
			else
			{
				kind = pennumber / 10 - 6;						// 剥离第一位
				number = pennumber % 10;						// 剥离最后一位
				if (pennumber < 10)
					pen = &pentable[pennumber];
				else
					pen = pentablev2[kind][number];				// 根据序号查找对应贴图
				putimage(left, top, pen);						// 把贴图画到采样区
			}
			left += pixnum;										// 往右移动,准备下一次绘制位置,此处贴图就会覆盖白色边框。为保证坐标变换和网格对应,算上网格宽度,也在贴图矩形内
		}
		top += pixnum;											// 往下移动,准备下一次绘制位置,此处就会覆盖白色边框,方便定位
	}
	SetWorkingImage();
}
// 在纹理映射函数中产生的图片中截图,但此为演示参数作用,此处并未优化。
inline void freshbk(IMAGE* bk, IMAGE* bkmesh, int gamex, int gamey, int bkmeshmapi, int bkmeshmapj, int tilenum, int pixnum)
{
	SetWorkingImage(bkmesh);
	getimage(bk, gamex - bkmeshmapj * pixnum, gamey - bkmeshmapi * pixnum, tilenum * pixnum, tilenum * pixnum);
	SetWorkingImage();
}
// 在屏幕显示截图
inline void showbk(IMAGE* bk, int bkdeskx, int bkdesky)
{
	SetWorkingImage();
	putimage(bkdeskx, bkdesky, bk);
}
// 在屏幕上显示缓冲区
inline void showbkmesh(IMAGE* bkmesh, int bkmeshdeskx, int bkmeshdesky)
{
	SetWorkingImage();
	putimage(bkmeshdeskx, bkmeshdesky, bkmesh);
}
// 初始化游戏地图
int** initmap(int wide, int high)
{
	int** map = new int* [high];					// 二维数组动态初始化,先给二级指针挂上一个长度为 10 的指针数组
	for (int i = 0; i < high; i++)
	{
		map[i] = new int[wide];						// 然后数组里的每个指针都挂上一个长度为 10 的 int 类型数组
	}
	for (int i = 0; i < high; i++)
	{
		for (int j = 0; j < wide; j++)
		{
			map[i][j] = -1;							// 初始化游戏大地图 map 的参数,参数 1 默认黑色
		}
	}
	return map;
}
int main()
{
	initgraph(1640, 980, 1);
	setbkcolor(GREEN);
	cleardevice();
	IMAGE* bk;									// 背景图片寄存区
	bk = new IMAGE(270, 270);
	IMAGE* bkmesh;								// 背景图片采样区
	bkmesh = new IMAGE(270 * 3, 270 * 3);
	int** map;									// 游戏大地图数组,记录着整个游戏背景的贴图信息,而在运行过程中,选取部分区域的数字,对照序号与贴图,实现游戏背景绘制。其余没有选中的区域就是压缩的空间。
	int pixnum;									// 一个正方形瓦片的边长。单位:像素
	int bkgameleft;								// 背景图片寄存区左上角坐标,是在游戏里的像素坐标。(0,0)可以理解为游戏大地图的左上角顶点。
	int bkgametop;
	int bkmeshgameleft;							// 背景图片采样区左上角坐标,是在游戏里的像素坐标。
	int bkmeshgametop;
	int bkmeshmapi;								// 背景图片采样区左上角所对应的 map 数组序号。从 map[0][0]开始,按照 map[i][j],其中 bkmeshmapi=bkmeshtop/pixnum
	int bkmeshmapj;
	int bkdeskleft;								// 规定在屏幕上显示游戏背景寄存区,此处记录其左上角在屏幕上的像素坐标
	int bkdesktop;
	int bkmeshdeskleft;							// 规定在屏幕上显示游戏背景采样区,此处记录其左上角在屏幕上的像素坐标
	int bkmeshdesktop;
	pixnum = 30;								// 进行初始化,规定各位置具体数字
	bkdeskleft = 200;							// 游戏背景左上角将会在屏幕的(200,200) 处
	bkdesktop = 200;
	bkgameleft = 0;								// 由于 bkgame 控制 mesh 坐标移动,(0,0)则游戏背景完全在当前采样区移动
	bkgametop = 0;
	bkmeshdeskleft = 700;						// 游戏背景缓冲区左上角将会在屏幕的(700,0)处
	bkmeshdesktop = 0;
	bkmeshgameleft = 0;
	bkmeshgametop = 0;
	int meshlimitright;							// 使用变量暂存边界,不用每次来回计算边界
	int meshlimitbuttom;
	meshlimitright = 270;							// 和寄存区一样大,把寄存区限制在一个九宫格中心,如果越出九宫格,九宫格整体平移
	meshlimitbuttom = 270;
	bkmeshmapi = bkmeshgametop / pixnum;
	bkmeshmapj = bkmeshgameleft / pixnum;
	int mapi;										// 读取 DLC 文件,需要变量记录循环次数,单位 瓦片贴图个数
	int mapj;
	int safemapi;									// 一个安全的贴图范围,避免阅读超过 10000 导致的越界闪退问题。
	int safemapj;
	int gamelimitright;								// 用于减少比较时的计算,存储边界范围
	int gamelimitbuttom;
	safemapi = 70;									// 注意导出的是左上角开始的 100*100 部分,如果绘制出界,保存不了,需要改这一行和下一行的数字
	safemapj = 70;
	gamelimitright = pixnum * safemapi;
	gamelimitbuttom = pixnum * safemapj;
	mapi = 300;
	mapj = 300;
	map = initmap(mapi, mapj);						// 初始化一个有 10000*10000 个贴图的地图
	int pentableleft;								// 忘了初始化调色盘了,这里设置调色盘左上角在屏幕的坐标
	int pentabletop;
	int imagenum;									// 调色板数量上限,用于文件读取控制循环次数
	IMAGE* pentable;								// 调色板其实就是贴图数组
	pentableleft = 0;								// 调色盘左上角将会在屏幕的(0,0)处
	pentabletop = 0;
	imagenum = 10;
	int pentablev2left;								// 自定义贴图插槽
	int pentablev2top;
	int pentablev2high;								// 自定义贴图的容量大小
	int pentablev2wide;
	pentablev2left = 10;
	pentablev2top = 300;
	pentablev2high = 10;
	pentablev2wide = 4;
	IMAGE*** pentablev2;
	pentablev2 = new IMAGE * *[pentablev2wide];
	for (int i = 0; i < pentablev2wide; i++)
		pentablev2[i] = new IMAGE * [pentablev2high];
	for (int i = 0; i < pentablev2wide; i++)
		for (int j = 0; j < pentablev2high; j++)
			pentablev2[i][j] = NULL;
	loadfile(pentablev2);							// 加载本地图片


	pentable = new IMAGE[imagenum];
	for (int i = 0; i < imagenum; i++)
	{
		pentable[i] = IMAGE(30, 30);
		SetWorkingImage(&pentable[i]);					// 给调色板绘制颜色
		setfillcolor(RGB(i * 20, i * 20, i * 20));		// 这里初始化调色盘的颜色
		fillrectangle(-1, -1, pixnum, pixnum);			// 在调色板上绘制颜色(纹理)要从 -1,-1 开始绘制,把边框画到外部,不保留边框。
	}
	int left;												// 初始化绘制采样区所需的坐标,相对于采样区,(0,0)就是采样区左上角顶点坐标
	int top;
	left = 0;
	top = 0;
	//	往缓冲区刷入贴图
	SetWorkingImage(bkmesh);								// 设置绘图目标为游戏背景采样区,刷新采样区,刷新寄存区
	setbkcolor(RGB(200, 200, 200));							// 设置瓦片边线颜色,图片没有覆盖的背景就是边线
	cleardevice();
	for (int i = bkmeshmapi; i < bkmeshmapi + 30; i++)
	{
		left = 0;
		for (int j = bkmeshmapj; j < bkmeshmapj + 30; j++)
		{
			int pennumber = map[i][j];							// 读取游戏大地图数组序号
			if (pennumber == -1)
			{
				rectangle(left, top, left + pixnum, top + pixnum);
			}
			left += pixnum;										// 往右移动,准备下一次绘制位置,
		}
		top += pixnum;											// 往下移动,准备下一次绘制位置
	}
	getimage(bk, bkgameleft, bkgametop, 270, 270);				// 从刚刚绘制好的采样区取样,刷新游戏背景寄存区。
	//	开始往屏幕上绘图
	SetWorkingImage();															// 设置电脑屏幕为绘制对象
	for (int j = 0; j < 10; j++)
	{
		putimage(pentableleft + 10, pentabletop + j * 30, &pentable[j]); 		// 绘制绘图板
	}
	for (int i = 0; i < pentablev2wide; i++)									// 绘制自定义贴图,印自定义笔刷
		for (int j = 0; j < pentablev2high; j++)
			if (pentablev2[i][j] != NULL)
				putimage(pentablev2left + i * 30, pentablev2top + j * 30, pentablev2[i][j]);
	putimage(bkdeskleft, bkdesktop, bk);										// 绘制游戏背景
	putimage(bkmeshdeskleft, bkmeshdesktop, bkmesh); 							// 显示游戏背景缓冲区
	// 此时绘制完成,以上 刷贴图,采样,粘贴就是实现 RPG 游戏大地图的压缩
	//	开始检测鼠标键盘功能
	int drawflag;															// 设置长按 flag
	int drawoldmx;															// 记录上一次绘制时的鼠标坐标,用于检测是否重复点击相同像素,来减少重复绘制
	int drawoldmy;
	int drawx;																// 画笔在游戏里的位置,单位像素
	int drawy;
	int olddrawi;															// 记录上一次绘制的瓦片,判断是否需要重新绘制
	int olddrawj;
	int drawsmallflag;														// 在 drawflag=1 时,检测是否刷新
	drawsmallflag = 0;
	olddrawi = 0;
	olddrawj = 0;
	drawx = 0;
	drawy = 0;
	drawflag = 0;
	drawoldmx = 0;
	drawoldmy = 0;
	int pentake;															// 设置不绘制时贴图代号为 -1
	pentake = 9;															// 默认白色笔刷,对应黑色背景
	int draftoldmx;															// 记录刚刚拖拽时的鼠标的位置,用于坐标变换计算位移
	int draftoldmy;
	int draftoldgamex;														// 记录刚刚拖拽时的游戏地图位置,用于坐标变换计算新的游戏坐标
	int draftoldgamey;
	int draftflag;															// 设置拖拽 flag
	draftoldmx = 0;
	draftoldmy = 0;
	draftoldgamex = 0;
	draftoldgamey = 0;
	draftflag = 0;
	int moveflag;															// 是否键盘控制移动
	int flag_x;																// 记录位移
	int flag_y;
	int speed;																// 键盘控制视口的移动速度
	speed = 5;
	flag_x = 0;
	flag_y = 0;
	moveflag = 0;
	int mousex;																// 记录鼠标位置
	int mousey;
	mousex = 0;
	mousey = 0;
	int oldbkmeshgamex;														// 判断拖拽时是否需要刷新 bkmesh 网格
	int oldbkmeshgamey;
	oldbkmeshgamex = -1;
	oldbkmeshgamey = -1;
	int i = -1;																// 用于暂存 mesh 网格坐标
	int j = -1;
	int characterflag;														// 用于按键时游戏小人的颜色变化
	characterflag = 5;
	ExMessage m;
	while (1)
	{
		while (peekmessage(&m, EX_KEY | EX_MOUSE))		// 一次性处理完鼠标消息,参考自 https://codebus.cn/zhaoh/handle-mouse-messages-correctly
		{
			switch (m.message)
			{
			case WM_LBUTTONDOWN:																				// 鼠标左键按下,有两种情况,一是选择贴图,另外就是绘制贴图
				if (drawflag == 0 && m.x > bkdeskleft && m.y > bkdesktop && m.x < bkdeskleft + 300 && m.y < bkdesktop + 300)		// 如果之前不是长按状态	且按下左键时,鼠标在游戏背景区域内
				{
					drawflag = 1;																				// 记录为正在绘制的状态
					mousex = m.x;																				// 记录坐标用于绘制
					mousey = m.y;
				}
				else if (drawflag == 0 && draftflag == 0 && m.x > 0 && m.y > 10 && m.x < 40 && m.y < 300)
					pentake = m.y / 30; 																		// 选择贴图对应的代号
				else if (drawflag == 0 && draftflag == 0 && m.x > 10 && m.y > 300 && m.x < 10 + pentablev2wide * 30 && m.y < 300 + pentablev2high * 30)
				{
					if (pentablev2[(m.x - pentablev2left) / pixnum][(m.y - pentablev2top) / pixnum] != NULL)
						pentake = ((m.x - pentablev2left) / pixnum + 6) * 10 + (m.y - pentablev2top) / pixnum;	// 计算 map 的二位数代号
					printf("pentake2: %d\n", pentake);
				}
				break;
			case WM_LBUTTONUP:
				drawflag = 0;
				drawsmallflag = 0;
				olddrawi = -1;
				olddrawj = -1;
				drawoldmx = -1;																					// 清除坐标记录,保证下次按键一定绘制
				drawoldmy = -1;
				break;
			case WM_RBUTTONDOWN:																				// 鼠标右键拖动
				if (draftflag == 0 && m.x > bkdeskleft && m.y > bkdesktop && m.x < bkdeskleft + 270 && m.y < bkdesktop + 270)
				{
					draftflag = 1;
					draftoldmx = m.x;																			// 记录鼠标坐标
					draftoldmy = m.y;
					mousex = m.x;
					mousey = m.y;
					draftoldgamex = bkgameleft;																	// 记录游戏背景寄存区左上角坐标
					draftoldgamey = bkgametop;
				}
				break;
			case WM_RBUTTONUP:
				draftflag = 0;
				bkgameleft = draftoldgamex - (m.x - draftoldmx);												// bkgameleft - draftoldgamex =- (m.x - draftoldmx)
				bkgametop = draftoldgamey - (m.y - draftoldmy);													// bkgametop - draftoldgamey =- (m.y - draftoldmy)
				mousex = -1;																					// draft 和 draw 共用 mousex mousey
				mousey = -1;
				break;
			case WM_KEYDOWN:
				switch (m.vkcode)				// 键盘移动控制
				{
				case 0x41:						// A
					if (flag_x - speed > -10)	// 限制范围,减少内存读写
						flag_x -= speed;
					characterflag = 1;
					break;
				case 0x57:						// W
					if (flag_y - speed > -10)
						flag_y -= speed;
					characterflag = 2;
					break;
				case 0x44:						// D
					if (flag_x + speed < 10)
						flag_x += speed;
					characterflag = 3;
					break;
				case 0x53:						// S
					moveflag = 1;
					if (flag_y + speed < 10)
						flag_y += speed;
					characterflag = 4;
					break;
				case VK_F1:
					savegamemap(map, mapi, mapj, pentable, pentablev2, pixnum, imagenum);
					break;
				case VK_F2:
					loadgamemap(map, &mapi, &mapj, pentable, &pixnum, &imagenum);
					gamelimitright = pixnum * safemapi;
					gamelimitbuttom = pixnum * safemapj;
					for (int i = 0; i < imagenum; i++)					// 刷新绘图板颜色
					{
						pentable[i] = IMAGE(30, 30);
						SetWorkingImage(&pentable[i]);					// 给调色板绘制颜色
						setfillcolor(RGB(i * 20, i * 20, i * 20));		// 这里初始化调色盘的颜色
						fillrectangle(-1, -1, pixnum, pixnum);			// 在调色板上绘制颜色(纹理)要从 -1,-1 开始绘制,把边框画到外部,不保留边框。
					}
					SetWorkingImage();
					freshmesh(bkmesh, map, pentable, pentablev2, bkmeshmapi, bkmeshmapj, 27, pixnum);			// 刷新,重新映射,其实就是开头初始化的代码,这里是给了一个封装示例,但未进行性能优化
					freshbk(bk, bkmesh, bkgameleft, bkgametop, bkmeshmapi, bkmeshmapj, 9, pixnum);
					showbk(bk, bkdeskleft, bkdesktop);
					showbkmesh(bkmesh, bkmeshdeskleft, bkmeshdesktop);
					break;
				}
				break;
			case WM_KEYUP:
				switch (m.vkcode)
				{
				case 0x41:						// A
					flag_x = 0;
					break;
				case 0x57:						// W
					flag_y = 0;
					break;
				case 0x44:						// D
					flag_x = 0;
					break;
				case 0x53:						// S
					flag_y = 0;
					break;
				}
				if (flag_x == 0 && flag_y == 0)
					characterflag = 5;
				break;
			case WM_MOUSEMOVE:
				if (mousex != m.x || mousey != m.y)
				{
					mousex = m.x;
					mousey = m.y;
				}
				break;
			}
		}
		//		开始根据指令运行坐标变化
		if (draftflag == 1)
		{
			if (flag_x != 0)																	// 实现拖拽时键盘也能控制移动
				draftoldgamex += flag_x;
			if (flag_y != 0)
				draftoldgamey += flag_y;
			bkgameleft = draftoldgamex - (mousex - draftoldmx);									// bkgameleft-draftoldgamex=-(mousex-draftoldmx)
			bkgametop = draftoldgamey - (mousey - draftoldmy);									// bkgametop-draftoldgamey=-(mousey - draftoldmy)
			if (drawflag == 1 && mousex > bkdeskleft && mousey > bkdesktop && mousex < bkdeskleft + 300 && mousey < bkdesktop + 300)		// 实现边拖拽边移动边绘制
			{
				drawx = bkgameleft + (mousex - bkdeskleft);										// drawx-bkgameleft=m.x-bkdeskleft	横坐标方向移动距离相同
				drawy = bkgametop + (mousey - bkdesktop);										// drawy-bkgametop=m.y-bkdesktop	纵坐标方向移动距离相同
				i = drawy / pixnum;
				j = drawx / pixnum;
				if (olddrawi != i || olddrawj != j)
				{
					if (i > safemapi)															// map 数组越界检测
						i = safemapi;
					else if (i < 0)
						i = 0;
					if (j > safemapj)
						j = safemapj;
					else if (j < 0)
						j = 0;
					drawsmallflag = 1;
					map[i][j] = pentake;											// 注意 map[y][x],而不是 map[x][y],因为判断第几行,是通过 y 来控制上下移动的,判断第几列,是通过 x 左右移动的。
					olddrawi = i;
					olddrawj = j;
				}
				else
				{
					drawsmallflag = 0;												// 检测到是上一次绘制的瓦片,则不再刷新贴图与缓冲区。
				}
			}
		}
		else if (drawflag == 1 && flag_x == 0 && flag_y == 0 && (drawoldmx != mousex || drawoldmy != mousey) && mousex > bkdeskleft && mousey > bkdesktop && mousex < bkdeskleft + 300 && mousey < bkdesktop + 300)
		{
			// 注意不要越界,否则 gamex 为负数,导致数组越界闪退。
			// 通过实现坐标变换与赋值达到修改游戏大地图(数组)
			moveflag = 0;
			drawoldmx = mousex;
			drawoldmy = mousey;
			drawx = bkgameleft + (mousex - bkdeskleft);										// drawx-bkgameleft=mousex-bkdeskleft	横坐标方向移动距离相同
			drawy = bkgametop + (mousey - bkdesktop);										// drawy-bkgametop=mousey-bkdesktop	纵坐标方向移动距离相同
			i = drawy / pixnum;
			j = drawx / pixnum;
			if (olddrawi != i || olddrawj != j)
			{
				drawsmallflag = 1;
				map[i][j] = pentake;														// 注意 map[y][x],而不是 map[x][y],因为判断第几行,是通过 y 来控制上下移动的,判断第几列,是通过 x 左右移动的。
				olddrawi = i;
				olddrawj = j;
			}
			else
			{
				drawsmallflag = 0;															// 检测到是上一次绘制的瓦片,则不再刷新贴图与缓冲区。
			}
		}	//	对绘制进行分类计算数据,剥离特殊情况的重复绘制,仅仅是 flag_x,或者 flag_y 不为零时取消重复绘制判断
		else if (drawflag == 1 && mousex > bkdeskleft && mousey > bkdesktop && mousex < bkdeskleft + 300 && mousey < bkdesktop + 300)
		{
			moveflag = 1;
			bkgameleft += flag_x;															// 更新游戏背景寄存区左上角坐标
			bkgametop += flag_y;
			drawx = bkgameleft + (mousex - bkdeskleft);										// drawx-bkgameleft=m.x-bkdeskleft	横坐标方向移动距离相同
			drawy = bkgametop + (mousey - bkdesktop);										// drawy-bkgametop=m.y-bkdesktop	纵坐标方向移动距离相同
			i = drawy / pixnum;
			j = drawx / pixnum;
			if (olddrawi != i || olddrawj != j)
			{
				drawsmallflag = 1;
				map[i][j] = pentake;											// 注意 map[y][x],而不是 map[x][y],因为判断第几行,是通过 y 来控制上下移动的,判断第几列,是通过 x 左右移动的。
				olddrawi = i;
				olddrawj = j;
			}
			else
			{
				drawsmallflag = 0;												// 检测到是上一次绘制的瓦片,则不再刷新贴图与缓冲区。
			}
		}
		else if (drawflag == 0 && flag_x != 0 || flag_y != 0)
		{
			moveflag = 1;
			bkgameleft += flag_x;
			bkgametop += flag_y;
		}
		else
		{
			// 既不绘制也不移动也不拖拽
		}
		// 根据计算出的坐标数据进行绘制,分多种情况分别绘制,减少函数重复调用与无效调用
		if (drawsmallflag == 1 && moveflag == 0)
		{
			freshmesh(bkmesh, map, pentable, pentablev2, bkmeshmapi, bkmeshmapj, 27, 30);			// 刷新,重新映射,其实就是开头初始化的代码,这里是给了一个封装示例,但未进行性能优化
			freshbk(bk, bkmesh, bkgameleft, bkgametop, bkmeshmapi, bkmeshmapj, 9, 30);
			showbk(bk, bkdeskleft, bkdesktop);
			showbkmesh(bkmesh, bkmeshdeskleft, bkmeshdesktop);
		}
		else if (moveflag == 1 && drawsmallflag == 1)
		{
			checkboundary(&bkgameleft, &bkgametop, gamelimitright, gamelimitbuttom, &bkmeshgameleft, &bkmeshgametop, &meshlimitright, &meshlimitbuttom, 270, 540);
			bkmeshmapi = bkmeshgametop / pixnum;
			bkmeshmapj = bkmeshgameleft / pixnum;
			freshmesh(bkmesh, map, pentable, pentablev2, bkmeshmapi, bkmeshmapj, 27, 30);
			freshbk(bk, bkmesh, bkgameleft, bkgametop, bkmeshmapi, bkmeshmapj, 9, 30);
			showbkmesh(bkmesh, bkmeshdeskleft, bkmeshdesktop);
			showbk(bk, bkdeskleft, bkdesktop);
		}
		else if (moveflag == 1 || draftflag)															// 分类渲染, drawflag==0 时,再选择性刷新缓冲区
		{
			checkboundary(&bkgameleft, &bkgametop, gamelimitright, gamelimitbuttom, &bkmeshgameleft, &bkmeshgametop, &meshlimitright, &meshlimitbuttom, 270, 540);
			bkmeshmapi = bkmeshgametop / pixnum;
			bkmeshmapj = bkmeshgameleft / pixnum;
			if (oldbkmeshgamex != bkmeshgameleft || oldbkmeshgamey != bkmeshgametop)				// 判断是否更新采样区
			{
				freshmesh(bkmesh, map, pentable, pentablev2, bkmeshmapi, bkmeshmapj, 27, 30);
				oldbkmeshgamex = bkmeshgameleft;
				oldbkmeshgamey = bkmeshgametop;
				showbkmesh(bkmesh, bkmeshdeskleft, bkmeshdesktop);
			}
			freshbk(bk, bkmesh, bkgameleft, bkgametop, bkmeshmapi, bkmeshmapj, 9, 30);
			showbk(bk, bkdeskleft, bkdesktop);
		}

		animation(characterflag, bkgameleft, bkgametop, bkmeshgameleft, bkmeshgametop, bkmeshdeskleft, bkmeshdesktop);
		Sleep(2);																		// 休眠 20 毫秒,减少 CPU 占用
	}
	return 0;
}
// 保存游戏地图的全部数据
void savegamemap(int** map, int gamemapi, int gamemapj, IMAGE* pentable, IMAGE*** pentablev2, int pixnum, int imagenum)
{
	FILE* fp;
	int i = 0;
	int j = 0;
	char dirpath[400] = { '\0' };
	char filepath[400] = { '\0' };
	for (i = 0; i < 100; i++)
	{
		sprintf(dirpath, "DLC%d", i);
		if (access(dirpath, 0) == -1) 									// 检查 DLC 是否存在,不存在为 -1
		{
			break;
		}
	}
	mkdir(dirpath);														// 创建文件夹
	char filename[400] = "gamemap.txt";
	const char* next = "./";
	strcat(filepath, dirpath);
	strcat(filepath, next);
	strcat(filepath, filename);
	fp = fopen(filepath, "w+");											// 创建 .txt 文件
	fprintf(fp, "注意此导出的游戏文件为按键 F1 后自动生成 修改汉语字符或者英文字符 或导致该 .txt 文件不可读取\n");
	fprintf(fp, "warning this saved gamefile is automatically create after F1 pressed change Chinesse character or English character lead to read failed");
	fprintf(fp, "pixnum %d\n", pixnum);									// 正方形瓦片贴图边长信息:单位:像素
	fprintf(fp, "imagenum %d\n", imagenum);								// 瓦片个数
	char imageindix[400] = { '\0' };
	for (i = 0; i < imagenum; i++)
	{
		sprintf(imageindix, "tile_%d.png", i);
		fprintf(fp, "%s\n", imageindix);
	}
	fprintf(fp, "gamemapi %d gamemapj %d\n", gamemapi, gamemapj);
	for (i = 0; i < gamemapi; i++)
	{
		fprintf(fp, "%d", map[i][j]);
		for (j = 0; j < gamemapj; j++)
		{
			fprintf(fp, " %d", map[i][j]);
		}
		fprintf(fp, "\n");
	}
	fclose(fp);
	char imagename[400] = { '\0' };
	char imagepath[400] = { '\0' };
	for (i = 0; i < imagenum; i++)
	{
		sprintf(imagename, "tile_%d.png", i);							// 数字转字符串
		strcpy(imagepath, dirpath);										// 函数直接从头开始粘贴,自动清除开头的字符
		strcat(imagepath, next);
		strcat(imagepath, imagename);
		saveimage(imagepath, &pentable[i]);								// 批量导出贴图
	}
	savepersonalfile(dirpath, pentablev2);								// 创建默认文件夹并保存自定义贴图
}
// 读取 DLC 继续开发
void loadgamemap(int** map, int* gamemapi, int* gamemapj, IMAGE* pentable, int* pixnum, int* imagenum)
{
	FILE* fp;
	int i = 0;
	int j = 0;
	char dirpath[400] = { '\0' };
	char filepath[400] = { '\0' };
	for (i = 0; i < 100; i++)
	{
		sprintf(dirpath, "DLC%d", i);
		if (access(dirpath, 0) == 0) 										// 检查 DLC 是否存在,存在为 0
		{
			break;
		}
	}
	if (i == 100)															// 100 次查找失败,则返回,不再读取
		return;
	char filename[400] = "gamemap.txt";
	const char* next = "./";
	strcat(filepath, dirpath);
	strcat(filepath, next);
	strcat(filepath, filename);
	fp = fopen(filepath, "r");											// 读取 .txt 文件
	fscanf(fp, "注意此导出的游戏文件为按键 F1 后自动生成 修改汉语字符或者英文字符 或导致该 .txt 文件不可读取\n");
	fscanf(fp, "warning this saved gamefile is automatically create after F1 pressed change Chinesse character or English character lead to read failed");
	fscanf(fp, "pixnum %d\n", pixnum);									// 正方形瓦片贴图边长信息:单位:像素
	fscanf(fp, "imagenum %d\n", imagenum);								// 瓦片个数
	char imageindix[400] = { '\0' };
	char imagename[400] = { '\0' };
	char imagepath[400] = { '\0' };
	for (i = 0; i < *imagenum; i++)
	{
		fscanf(fp, "%s\n", imageindix);
		strcpy(imagepath, dirpath);
		strcat(imagepath, next);
		strcat(imagepath, imageindix);
		loadimage(&pentable[i], imagepath, *pixnum, *pixnum, false);	// 批量导入贴图
	}
	fscanf(fp, "gamemapi %d gamemapj %d\n", gamemapi, gamemapj);
	for (i = 0; i < *gamemapi; i++)
	{
		fscanf(fp, "%d", &map[i][j]);
		for (j = 0; j < *gamemapj; j++)
		{
			fscanf(fp, " %d", &map[i][j]);
		}
		fscanf(fp, "\n");
	}
	fclose(fp);
}
// 检查边界
inline void checkboundary(int* bkgameleft, int* bkgametop, int gamelimitright, int gamelimitbuttom, int* bkmeshgameleft, int* bkmeshgametop, int* meshlimitright, int* meshlimitbuttom, int move, int size)
{
	if (*bkgameleft < 0)												// 网格越界检测并调整
		*bkgameleft = 0;
	else if (*bkgameleft > gamelimitright)
		*bkgameleft = gamelimitright;
	if (*bkgametop < 0)
		*bkgametop = 0;
	else if (*bkgametop > gamelimitbuttom)
		*bkgametop = gamelimitbuttom;
	if (*bkgameleft < *bkmeshgameleft)									// 更新游戏采样区坐标,一些简单换算,由于频繁调用函数在这里产生了明显的卡顿影响,所以这里就不再封装成函数
	{
		*bkmeshgameleft -= move;
		*meshlimitright = *bkmeshgameleft + size;
	}
	else if (*bkgameleft > *meshlimitright)
	{
		*meshlimitright += move;
		*bkmeshgameleft = *meshlimitright - size;
	}
	if (*bkgametop < *bkmeshgametop)
	{
		*bkmeshgametop -= move;
		*meshlimitbuttom = *bkmeshgametop + size;
	}
	else if (*bkgametop > *meshlimitbuttom)
	{
		*meshlimitbuttom += move;
		*bkmeshgametop = *meshlimitbuttom - size;
	}
}
// 性能:CPU 2% 占有率,峰值 1.20GHz,核显 GPU 10% 使用率
inline void animation(int characterflag, int bkgameleft, int bkgametop, int bkmeshgameleft, int bkmeshgametop, int bkmeshdeskleft, int bkmeshdesktop)
{
	static int i = 1;
	static int x = 10;
	static int y = 10;
	if (i % 3 == 0)
	{
		x = bkmeshdeskleft + bkgameleft - bkmeshgameleft;
		y = bkmeshdesktop + bkgametop - bkmeshgametop;
		i %= 125;														// i 的上限 * 放大倍数 不超过 255
		switch (characterflag)											// 选取颜色
		{
		case 1:
			setfillcolor(RGB(i * 2, i * 2, 0));
			break;
		case 2:
			setfillcolor(RGB(i * 2, 0, i * 2));
			break;
		case 3:
			setfillcolor(RGB(i * 2, 0, 0));
			break;
		case 4:
			setfillcolor(RGB(0, i * 2, 0));
			break;
		case 5:														// 如果没有按键,则使用默认颜色
			setfillcolor(RGB(0, 0, i * 2));
			break;
		}
		fillrectangle(x, y, x + 30, y + 30);
	}
	i++;
}
// 从默认文件夹中读取自定义图片
void loadfile(IMAGE*** pentablev2)
{
	char brick[100] = "aa_birck_6_family";
	char ground[100] = "ab_ground_7_family";
	char change[100] = "ac_change_8_family";
	char trick[100] = "ad_trick_9_family";
	char next[100] = "./";
	char png[100] = "*.png";
	loadfile_scanf(brick, next, png, pentablev2[0]);								// 加载用于当砖头类型的图片
	loadfile_scanf(ground, next, png, pentablev2[1]);
	loadfile_scanf(change, next, png, pentablev2[2]);
	loadfile_scanf(trick, next, png, pentablev2[3]);
}
// 加载自定义图片
void loadfile_scanf(char* dirname, char* next, char* kind, IMAGE** pentablev2)
{
	// 文件存储信息结构体
	struct _finddata_t fileinfo;
	// 保存文件句柄
	long fHandle;
	// 文件数记录器
	char dirpath[100];
	strcpy(dirpath, dirname);
	if (access(dirpath, 0) == -1)													// 检查文件夹是否存在,不存在为 -1
	{
		mkdir(dirpath);
		return;
	}
	else
	{
		int i = -1;																	// 记录文件数量
		char kindpath[100];
		strcpy(kindpath, dirpath);
		strcat(kindpath, next);
		strcat(kindpath, kind);
		if ((fHandle = _findfirst(kindpath, &fileinfo)) == -1L) 					// *是通配符,默认在当前文件夹内查找文件,这里查找 .png 文件
		{
			printf("当前目录: %s 下没有所需文件\n", dirname);
			return;
		}
		else
		{
			char path[100];
			do
			{
				i++;
				printf("在%s 下找到文件:%s,文件大小:%d bit\n", dirname, fileinfo.name, fileinfo.size);
				strcpy(path, dirname);
				strcat(path, next);
				strcat(path, fileinfo.name);
				while (pentablev2[i] != NULL)
				{
					i++;
				}
				pentablev2[i] = new IMAGE(30, 30);
				loadimage(pentablev2[i], path, 30, 30);								// 根据名称读取文件
			} while (_findnext(fHandle, &fileinfo) == 0);
		}
		// 关闭文件
		_findclose(fHandle);

		printf("文件数量:%d\n", i + 1);
	}
}
// 在目标 DLC 里创建四个文件夹并保存
void savepersonalfile(char* DLC, IMAGE*** pentablev2)
{
	char brick[100] = "aa_birck_6_family";
	char ground[100] = "ab_ground_7_family";
	char change[100] = "ac_change_8_family";
	char trick[100] = "ad_trick_9_family";
	char next[100] = "./";
	char png[100] = ".png";
	if (access(DLC, 0) == -1)														// 检查文件夹是否存在,不存在为 -1
	{
		mkdir(DLC);
	}
	char sign[10] = {};
	strcpy(sign, "6");
	savelocal(DLC, brick, next, png, sign, pentablev2[0]);
	strcpy(sign, "7");
	savelocal(DLC, ground, next, png, sign, pentablev2[1]);
	strcpy(sign, "8");
	savelocal(DLC, change, next, png, sign, pentablev2[2]);
	strcpy(sign, "9");
	savelocal(DLC, trick, next, png, sign, pentablev2[3]);
}
// 保存自定义文件
void savelocal(char* DLC, char* dirname, char* next, char* kind, char* sign, IMAGE** files)
{
	char dirpath[100] = "";
	strcpy(dirpath, DLC);															// 字符串拼接出相对路径
	strcat(dirpath, next);
	strcat(dirpath, dirname);
	if (access(dirpath, 0) == -1)													// 检查 DLC 内的指定文件夹是否存在,不存在为 -1
	{
		mkdir(dirpath);
	}
	char filepath[100];
	char filepa[100];
	char num[10];
	strcpy(filepath, dirpath);														// 字符串拼接出文件名前缀
	strcat(filepath, next);
	strcat(filepath, sign);
	for (int i = 0; i < 10; i++)
	{
		sprintf(num, "%d", i);
		strcpy(filepa, filepath);													// 从头粘贴,可以覆盖之前的信息,不受上一次保存的影响
		strcat(filepa, num);
		strcat(filepa, kind);
		if (files[i] != NULL)
		{
			saveimage(filepa, files[i]);
			printf("file save %s\n", filepa);
		}
	}
}
相关推荐
左 岸 ღ °6 小时前
window11 通过cmd命令行安装 oh my zsh 的教程
编辑器
MonkeyKing_sunyuhua7 小时前
Visual Studio Code 进行汉化
ide·vscode·编辑器
空中湖7 小时前
墨笔 在线Markdown 编辑器
编辑器
有趣的我10 小时前
vim的操作
编辑器·vim·excel
『六哥』15 小时前
Windows 10 如何设置右击鼠标快速进行“关机”
windows·经验分享·编辑器
1alisa1 天前
Sublime Text for Mac v4【注册汉化版】代码编辑器
macos·编辑器·sublime text
神码编程1 天前
【Unity】 HTFramework框架(六十四)SaveDataRuntime运行时保存组件参数、预制体
unity·编辑器·游戏引擎
Tiger Z1 天前
R 语言科研绘图 --- 韦恩图-汇总
开发语言·程序人生·r语言·贴图
mzak1 天前
vscode集成deepseek实现辅助编程(银河麒麟系统)【详细自用版】
linux·vscode·编辑器·银河麒麟·deepseek
一个程序员(●—●)1 天前
编辑器检视器面板深度扩展2+编辑器菜单栏扩展
编辑器