使用内联函数,降低函数调用开销,实现移动时绘制

2024-02-10 13:28

本文主要是介绍使用内联函数,降低函数调用开销,实现移动时绘制,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

 easyx devc++ 开发。

AWSD移动,移动时可以左键绘制

左键绘制

左上角画笔颜色

右键拖拽

#include <graphics.h>
// 使用关键字 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;									// 往右移动,准备下一次绘制位置}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] = j % 9;						// 初始化游戏大地图 map 的参数,参数范围 0-9}}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);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(0, 0, pixnum, pixnum);			// 在调色板上绘制颜色(纹理)}int left;												// 初始化绘制采样区所需的坐标,相对于采样区,(0,0)就是采样区左上角顶点坐标int top;left = 0;top = 0;//	往缓冲区刷入贴图SetWorkingImage(bkmesh);								// 设置绘图目标为游戏背景采样区,刷新采样区,刷新寄存区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;									// 往右移动,准备下一次绘制位置}top += pixnum;										// 往下移动,准备下一次绘制位置}getimage(bk, bkgameleft, bkgametop, 270, 270);			// 从刚刚绘制好的采样区取样,刷新游戏背景寄存区。//	开始往屏幕上绘图SetWorkingImage();															// 设置电脑屏幕为绘制对象for (int j = 0; j < 10; j++){putimage(pentableleft + 10, pentabletop + j * 30, &pentable[j]); 		// 绘制绘图板}putimage(bkdeskleft, bkdesktop, bk);										// 绘制游戏背景putimage(bkmeshdeskleft, bkmeshdesktop, bkmesh); 							// 显示游戏背景缓冲区// 此时绘制完成,以上 刷贴图,采样,粘贴就是实现 RPG 游戏大地图的压缩//	开始检测鼠标键盘功能int drawflag;															// 设置长按 flagint drawoldmx;															// 记录上一次绘制时的鼠标坐标int drawoldmy;int drawx;																// 画笔在游戏里的位置,单位像素int drawy;drawx = 0;drawy = 0;drawflag = 0;drawoldmx = 0;drawoldmy = 0;int pentake;															// 设置不绘制时贴图代号为 -1int draftoldmx;															// 记录刚刚拖拽时的鼠标的位置int draftoldmy;int draftoldgamex;														// 记录刚刚拖拽时的游戏地图位置int draftoldgamey;int draftflag;															// 设置拖拽 flagpentake = 0;															// 默认黑色笔刷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;															// 判断是否需要刷新int oldbkmeshgamey;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;																				// 才记录为正在绘制的状态}else if (drawflag==0&&draftflag==0&&m.x > 0 && m.y > 0 && m.x < 30 && m.y < 300){pentake = m.y / 30; 																		// 选择贴图对应的代号}break;case WM_LBUTTONUP:drawflag = 0;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;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)break;case WM_KEYDOWN:switch (m.vkcode)					// 键盘移动控制{case 0x41:						// Aflag_x-=speed;moveflag=1;break;case 0x57:						// Wflag_y-=speed;break;case 0x44:						// Dflag_x+=speed;break;case 0x53:						// Sflag_y+=speed;break;}break;case WM_KEYUP:switch(m.vkcode){case 0x41:						// Aflag_x=0;break;case 0x57:						// Wflag_y=0;break;case 0x44:						// Dflag_x=0;break;case 0x53:						// Sflag_y=0;break;}break;default:mousex=m.x;mousey=m.y;break;}}
//		开始根据指令运行坐标变化if (draftflag == 1){bkgameleft = draftoldgamex - (mousex - draftoldmx);											// bkgameleft-draftoldgamex=-(mousex-draftoldmx)bkgametop = draftoldgamey - (mousey - draftoldmy);											// bkgametop-draftoldgamey=-(mousey - draftoldmy)}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	纵坐标方向移动距离相同map[drawy / pixnum][drawx / pixnum] = pentake;									// 注意 map[y][x],而不是 map[x][y],因为判断第几行,是通过 y 来控制上下移动的,判断第几列,是通过 x 左右移动的。}	//	对绘制进行分类计算数据,剥离特殊情况的重复绘制,仅仅是 flag_x,或者flag_y不为零时取消重复绘制判断。else if(drawflag == 1 && mousex > bkdeskleft && mousey > bkdesktop && mousex < bkdeskleft + 300 && mousey < bkdesktop + 300){moveflag=1;while(flag_x<-10)																// 限制在合适速度范围flag_x+=10;while(flag_x>10)flag_x-=10;while(flag_y<-10)flag_y+=10;while(flag_y>10)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	纵坐标方向移动距离相同map[drawy / pixnum][drawx / pixnum] = pentake;									// 注意 map[y][x],而不是 map[x][y],因为判断第几行,是通过 y 来控制上下移动的,判断第几列,是通过 x 左右移动的。}else if(drawflag==0&&flag_x!=0||flag_y!=0){moveflag=1;while(flag_x<-10)																// 限制在合适速度范围flag_x+=10;while(flag_x>10)flag_x-=10;while(flag_y<-10)flag_y+=10;while(flag_y>10)flag_y-=10;bkgameleft+=flag_x;bkgametop+=flag_y;}else{// 既不绘制也不移动也不拖拽}// 根据计算出的坐标数据进行绘制,分多种情况分别绘制,减少函数重复调用与无效调用 if(drawflag==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(drawflag==1&&moveflag==1){if(bkgameleft<0)															// 网格越界检测并调整bkgameleft=0;if(bkgametop<0)bkgametop=0;if(bkgameleft>30*300)bkgameleft=30*300;if(bkgametop>30*300)bkgametop=30*300;while (bkgameleft < bkmeshgameleft)											// 更新游戏采样区坐标,一些简单换算bkmeshgameleft -=270;while (bkgametop <bkmeshgametop )bkmeshgametop -= 270;while(bkgameleft>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(bkgametop<0)bkgametop=0;if(bkgameleft>30*300)bkgameleft=30*300;if(bkgametop>30*300)bkgametop=30*300;while (bkgameleft < bkmeshgameleft)											// 更新游戏采样区坐标,一些简单换算,由于频繁调用函数在这里产生了明显的卡顿影响,所以这里就不再封装成函数 bkmeshgameleft -=270;while (bkgametop <bkmeshgametop )bkmeshgametop -= 270;while(bkgameleft>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(bkgametop<0)bkgametop=0;if(bkgameleft>30*300)bkgameleft=30*300;if(bkgametop>30*300)bkgametop=30*300;while (bkgameleft < bkmeshgameleft)											// 更新游戏采样区坐标,一些简单换算bkmeshgameleft -=270;while (bkgametop <bkmeshgametop )bkmeshgametop -= 270;while(bkgameleft>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.55GHz 3%占用,内部 2.45Ghz ,使用内联,显著平稳,峰值降低 

这篇关于使用内联函数,降低函数调用开销,实现移动时绘制的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/697211

相关文章

Linux换行符的使用方法详解

《Linux换行符的使用方法详解》本文介绍了Linux中常用的换行符LF及其在文件中的表示,展示了如何使用sed命令替换换行符,并列举了与换行符处理相关的Linux命令,通过代码讲解的非常详细,需要的... 目录简介检测文件中的换行符使用 cat -A 查看换行符使用 od -c 检查字符换行符格式转换将

SpringBoot实现数据库读写分离的3种方法小结

《SpringBoot实现数据库读写分离的3种方法小结》为了提高系统的读写性能和可用性,读写分离是一种经典的数据库架构模式,在SpringBoot应用中,有多种方式可以实现数据库读写分离,本文将介绍三... 目录一、数据库读写分离概述二、方案一:基于AbstractRoutingDataSource实现动态

Python FastAPI+Celery+RabbitMQ实现分布式图片水印处理系统

《PythonFastAPI+Celery+RabbitMQ实现分布式图片水印处理系统》这篇文章主要为大家详细介绍了PythonFastAPI如何结合Celery以及RabbitMQ实现简单的分布式... 实现思路FastAPI 服务器Celery 任务队列RabbitMQ 作为消息代理定时任务处理完整

使用Jackson进行JSON生成与解析的新手指南

《使用Jackson进行JSON生成与解析的新手指南》这篇文章主要为大家详细介绍了如何使用Jackson进行JSON生成与解析处理,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1. 核心依赖2. 基础用法2.1 对象转 jsON(序列化)2.2 JSON 转对象(反序列化)3.

Java枚举类实现Key-Value映射的多种实现方式

《Java枚举类实现Key-Value映射的多种实现方式》在Java开发中,枚举(Enum)是一种特殊的类,本文将详细介绍Java枚举类实现key-value映射的多种方式,有需要的小伙伴可以根据需要... 目录前言一、基础实现方式1.1 为枚举添加属性和构造方法二、http://www.cppcns.co

使用Python实现快速搭建本地HTTP服务器

《使用Python实现快速搭建本地HTTP服务器》:本文主要介绍如何使用Python快速搭建本地HTTP服务器,轻松实现一键HTTP文件共享,同时结合二维码技术,让访问更简单,感兴趣的小伙伴可以了... 目录1. 概述2. 快速搭建 HTTP 文件共享服务2.1 核心思路2.2 代码实现2.3 代码解读3.

Elasticsearch 在 Java 中的使用教程

《Elasticsearch在Java中的使用教程》Elasticsearch是一个分布式搜索和分析引擎,基于ApacheLucene构建,能够实现实时数据的存储、搜索、和分析,它广泛应用于全文... 目录1. Elasticsearch 简介2. 环境准备2.1 安装 Elasticsearch2.2 J

使用C#代码在PDF文档中添加、删除和替换图片

《使用C#代码在PDF文档中添加、删除和替换图片》在当今数字化文档处理场景中,动态操作PDF文档中的图像已成为企业级应用开发的核心需求之一,本文将介绍如何在.NET平台使用C#代码在PDF文档中添加、... 目录引言用C#添加图片到PDF文档用C#删除PDF文档中的图片用C#替换PDF文档中的图片引言在当

Kotlin 作用域函数apply、let、run、with、also使用指南

《Kotlin作用域函数apply、let、run、with、also使用指南》在Kotlin开发中,作用域函数(ScopeFunctions)是一组能让代码更简洁、更函数式的高阶函数,本文将... 目录一、引言:为什么需要作用域函数?二、作用域函China编程数详解1. apply:对象配置的 “流式构建器”最

Java中List的contains()方法的使用小结

《Java中List的contains()方法的使用小结》List的contains()方法用于检查列表中是否包含指定的元素,借助equals()方法进行判断,下面就来介绍Java中List的c... 目录详细展开1. 方法签名2. 工作原理3. 使用示例4. 注意事项总结结论:List 的 contain