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

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

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

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

操作同上

导入步骤:

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

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

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

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

绘制完

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

// 程序:2D RPG 地图编辑器
// 作者:民用级脑的研发记录
// 邮箱:1309602336@qq.com
// 编译环境: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);
		}
	}
}
相关推荐
向宇it15 分钟前
【从零开始入门unity游戏开发之——unity篇02】unity6基础入门——软件下载安装、Unity Hub配置、安装unity编辑器、许可证管理
开发语言·unity·c#·编辑器·游戏引擎
徐小夕@趣谈前端15 分钟前
可视化大屏编辑器, 开源!
编辑器
天天进步201530 分钟前
TipTap编辑器:现代化的富文本编辑解决方案
编辑器
weixin_423196175 小时前
VSCode+WSL作为IDE开发和管理深度学习项目
ide·vscode·编辑器
乐闻x5 小时前
VSCode 插件开发实战(八):创建和管理任务 Task
ide·vscode·编辑器
神洛华7 小时前
Y3地图制作1:水果缤纷乐、密室逃脱
编辑器·游戏引擎·游戏程序
一棵开花的树,枝芽无限靠近你18 小时前
【PPTist】组件结构设计、主题切换
前端·笔记·学习·编辑器
0xdadream19 小时前
typora数学符号
编辑器
带电的小王1 天前
VSCode:VSCode安装 -- 最简洁的VSCode安装教程
ide·vscode·编辑器