1. F1保存为 DLC 输出为当前文件目录下。
2. 瓦片的边框利用绘制到 -1 -1 把边框绘制到外部
3. 利用 +1 实现网格保留
#include #include #include #include // 保存游戏地图贴图数据void savegamemap(int** map,int gamemapi,int gamemapj,IMAGE* pentable,int pixnum);// 使用关键字 inline 声明为内联函数,减少贴图函数频繁调用的开销导致的卡顿。// 缓冲区纹理映射函数:bkmesh 映射目标,map 映射总网格,pentable:纹理集,bkmeshmapi,bkmeshmapj:映射起始点,tilenum:横,纵映射的数量,pixnum:一个映射块的边长,单位:像素。inline void freshmesh(IMAGE* bkmesh, int** map, IMAGE* pentable, int bkmeshmapi, int bkmeshmapj, int tilenum, int pixnum){int pennumber = 0;// 暂存每一次循环的映射代号IMAGE pen = NULL;// 所找到的纹理int left = 0;// 这是每次循环所找到的纹理对应映射地址int top = 0;SetWorkingImage(bkmesh);// 设置绘图目标为游戏背景采样区,刷新采样区,刷新寄存区for (int i = bkmeshmapi; i < bkmeshmapi + tilenum; i++){left = 0;for (int j = bkmeshmapj; j < bkmeshmapj + tilenum; j++){pennumber = map[i][j];// 读取游戏大地图数组序号pen = pentable[pennumber];// 根据序号查找对应贴图putimage(left, top, &pen);// 把贴图画到采样区left += pixnum+1;// 往右移动,准备下一次绘制位置,+1 留出白色背景 }top += pixnum+1;// 往下移动,准备下一次绘制位置,+1 留出白色背景 }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 的参数,参数范围 3}}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/pixnumint bkmeshmapj;int bkdeskleft;// 规定在屏幕上显示游戏背景寄存区,此处记录其左上角在屏幕上的像素坐标int bkdesktop;int bkmeshdeskleft;// 规定在屏幕上显示游戏背景采样区,此处记录其左上角在屏幕上的像素坐标int bkmeshdesktop;pixnum = 30;// 进行初始化,规定各位置具体数字bkdeskleft = 200;// 游戏背景左上角将会在屏幕的(200,200) 处bkdesktop = 200;bkgameleft = 0;bkgametop = 0;bkmeshdeskleft = 700;// 游戏背景缓冲区左上角将会在屏幕的(700,0)处bkmeshdesktop = 0;bkmeshgameleft = 0;bkmeshgametop = 0;bkmeshmapi = bkmeshgametop / pixnum;bkmeshmapj = bkmeshgameleft / pixnum;map=initmap(1000,1000);// 初始化一个有1000*1000个贴图的地图int pentableleft;// 忘了初始化调色盘了,这里设置调色盘左上角在屏幕的坐标int pentabletop;IMAGE* pentable;// 调色板其实就是贴图数组pentableleft = 0;// 调色盘左上角将会在屏幕的(0,0)处pentabletop = 0;pentable = new IMAGE[10];for (int i = 0; i < 10; 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];// 读取游戏大地图数组序号IMAGE pen = pentable[pennumber];// 根据序号查找对应贴图putimage(left, top, &pen);// 把贴图画到采样区left += pixnum+1;// 往右移动,准备下一次绘制位置, 此处 +1 是增加白色边框,方便定位 }top += pixnum+1;// 往下移动,准备下一次绘制位置,此处 +1 是增加白色边框,方便定位 }getimage(bk, bkgameleft, bkgametop, 270, 270);// 从刚刚绘制好的采样区取样,刷新游戏背景寄存区。//开始往屏幕上绘图SetWorkingImage();// 设置电脑屏幕为绘制对象for (int j = 0; j bkdeskleft && m.y > bkdesktop && m.x < bkdeskleft + 300 && m.y 0 && m.y > 0 && m.x < 30 && m.y bkdeskleft && m.y > bkdesktop && m.x < bkdeskleft + 270 && m.y bkdeskleft && mousey > bkdesktop && mousex < bkdeskleft + 300 && mousey bkdeskleft && mousey > bkdesktop && mousex < bkdeskleft + 300 && mousey < bkdesktop + 300){moveflag=1;while(flag_x10)flag_x-=10;while(flag_y10)flag_y-=10;bkgameleft+=flag_x;// 更新游戏背景寄存区左上角坐标bkgametop+=flag_y;drawx = bkgameleft + (mousex - bkdeskleft);// drawx-bkgameleft=m.x-bkdeskleft横坐标方向移动距离相同drawy = bkgametop + (mousey - bkdesktop);// drawy-bkgametop=m.y-bkdesktop纵坐标方向移动距离相同if(olddrawi!=drawy/pixnum||olddrawj!=drawx/pixnum){drawsmallflag=1;olddrawi=drawy / pixnum;olddrawj=drawx / pixnum;map[olddrawi][olddrawj] = pentake;// 注意 map[y][x],而不是 map[x][y],因为判断第几行,是通过 y 来控制上下移动的,判断第几列,是通过 x 左右移动的。}else{drawsmallflag=0;// 检测到是上一次绘制的瓦片,则不再刷新贴图与缓冲区。}}else if(drawflag==0&&flag_x!=0||flag_y!=0){moveflag=1;while(flag_x10)flag_x-=10;while(flag_y10)flag_y-=10;bkgameleft+=flag_x;bkgametop+=flag_y;}else{// 既不绘制也不移动也不拖拽}// 根据计算出的坐标数据进行绘制,分多种情况分别绘制,减少函数重复调用与无效调用if(drawsmallflag==1&&moveflag==0){freshmesh(bkmesh, map, pentable, 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){if(bkgameleft<0)// 网格越界检测并调整bkgameleft=0;if(bkgametop30*300)bkgameleft=30*300;if(bkgametop>30*300)bkgametop=30*300;while (bkgameleft < bkmeshgameleft)// 更新游戏采样区坐标,一些简单换算bkmeshgameleft -=270;while (bkgametop bkmeshgameleft+270+270)bkmeshgameleft += 270;while(bkgametop>bkmeshgametop+270+270)bkmeshgametop += 270;bkmeshmapi = bkmeshgametop / pixnum;bkmeshmapj = bkmeshgameleft / pixnum;freshmesh(bkmesh, map, pentable, 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)// 分类渲染, drawflag==0 时,再选择性刷新缓冲区{if(bkgameleft<0)// 网格越界检测并调整bkgameleft=0;if(bkgametop30*300)bkgameleft=30*300;if(bkgametop>30*300)bkgametop=30*300;while (bkgameleft < bkmeshgameleft)// 更新游戏采样区坐标,一些简单换算,由于频繁调用函数在这里产生了明显的卡顿影响,所以这里就不再封装成函数bkmeshgameleft -=270;while (bkgametop bkmeshgameleft+270+270)bkmeshgameleft += 270;while(bkgametop>bkmeshgametop+270+270)bkmeshgametop += 270;bkmeshmapi = bkmeshgametop / pixnum;bkmeshmapj = bkmeshgameleft / pixnum;if(oldbkmeshgamex!=bkmeshgameleft||oldbkmeshgamey!=bkmeshgametop)// 判断是否更新采样区{freshmesh(bkmesh, map, pentable, 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);}else if(draftflag)// 分类渲染-只拖拽{if(bkgameleft<0)// 网格越界检测并调整bkgameleft=0;if(bkgametop30*300)bkgameleft=30*300;if(bkgametop>30*300)bkgametop=30*300;while (bkgameleft < bkmeshgameleft)// 更新游戏采样区坐标,一些简单换算bkmeshgameleft -=270;while (bkgametop bkmeshgameleft+270+270)bkmeshgameleft += 270;while(bkgametop>bkmeshgametop+270+270)bkmeshgametop += 270;bkmeshmapi = bkmeshgametop / pixnum;bkmeshmapj = bkmeshgameleft / pixnum;if(oldbkmeshgamex!=bkmeshgameleft||oldbkmeshgamey!=bkmeshgametop)// 判断是否更新采样区{freshmesh(bkmesh, map, pentable, 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);}Sleep(20);// 休眠 20 毫秒,减少 CPU 占用}return 0;}// 边绘制边移动:最大:边界 2.30GHz 3%占用,内部 2.25Ghz。仅绘制:1.8GHz, 拖拽:0.99GHz,旧瓦片绘制检测,又减少重复绘制,显著平稳,峰值降低void savegamemap(int** map,int gamemapi,int gamemapj,IMAGE* pentable,int pixnum){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",10);// 瓦片个数 char c[400]={'\0'};for(i=0;i<10;i++){sprintf(c,"tile_%d.png",i);fprintf(fp,"%s\n",c);} 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<10; i++){sprintf(imagename,"tile_%d.png",i);// 数字转字符串strcpy(imagepath,dirpath);// 函数直接从头开始粘贴,自动清除开头的字符 strcat(imagepath,next);strcat(imagepath,imagename);saveimage(imagepath,&pentable[i]);// 批量导出贴图}}