游戏必要的清屏实现以及闪烁原因的详细解释 您所在的位置:网站首页 清屏函数的使用方法 游戏必要的清屏实现以及闪烁原因的详细解释

游戏必要的清屏实现以及闪烁原因的详细解释

2024-04-05 01:21| 来源: 网络整理| 查看: 265

目录

system("cls")

system("cls")闪烁的原因解释:

解决方案

1.使用sleep/Sleep函数

2.再次引入一种新的具有清屏效果的工具

gotoxy函数

双缓冲技术

文前声明:

最近在做很多简单代码的小游戏,所以需要从开发小游戏专栏的开头就介绍清屏效果的有效实现方法,途径与原理。本人被困惑了一段时间,找遍了绝大多数c站以及其他网站的资料以此来解释清屏效果的实现。c站目前没有解释清屏太过详细的文章,可能是由于内容易懂而本人过于笨拙吧?但是由于我在黑暗中踩到过水洼,所以我要在黑暗中引燃火把。

在文章开头,我们需要知道:我们所谓的清屏只是一个手段,而我们真正想要实现的理想效果是通过用户操作来实现画面的更新

system("cls")

system函数有很多功能,system("cls")函数是实现清屏功能最直接,也是从真正意义上对“清屏”两字做出实质性践行的。举个栗子:

#include int main(){ int a,b,c; c=a+b; printf("%d",c); system("cls"); return 0; }

对于这段代码中,大家可以很明确的看到system("cls")函数被放在最后一行,我们在计算c并输出c的结果后对当前屏幕上显示的内容进行了清屏处理;

但实际上,c已经被赋值为a+b。

也就是说,system("cls")只是对当前显示器上的内容进行清空处理,对于已经做出计算或赋值的变量等保存方式不会进行清除,我们后来的几个清屏函数也是这样单纯字面意义上的清除,并不会对其他变量的赋值或定义情况做出更改。

对于system("cls")函数的使用,我们不需要引入其他头文件,它是一种较为简单的使用,它的实现原理也很简单:

清空当前显示屏上的所有内容,并将清空后将需要打印在显示屏上的内容与缓冲区同步打印。

以我自己更为口头阐述的表达:就是在清空屏幕后,计算机会将需要打印的内容打印在缓冲区中,在缓冲区打印的同时,显示屏与缓冲区联通,显示屏直播缓冲区正在加载的内容。

但我们都知道凡事都不太好做出事半功倍的效果,也正是因此我们的system("cls")函数有了一定的限制:

1.只可以进行小部分内容的瞬间清屏

2.倘若输出内容较大,譬如一般以c语言做游戏都需要存放在循环体中,那时每当进行一次画面输出,都需要在循环体头部存放一个system("cls")函数,在编译后的运行阶段,你会看到画面不断的闪烁,这就体现出了这个函数的弊端!

system("cls")闪烁的原因解释:

在输出内容较为庞大的时候,由于这个函数的原理是同时清屏并从头开始打印,所以会存在“第一个”内容和“最后一个”内容输出的时间差,也就是在最后一个内容开始进行打印结束后循环体执行了一轮下面程序的编译,再次开始了新一轮循环,即再次使用了清屏函数,由于内容的输出和清空不断更替,所以会出现屏幕内容闪烁的问题!

那我们也能从原因中找到两个解决方法:

解决方案 1.使用sleep/Sleep函数

【在本人的实践过程中(可能还是因为不细心观察s大小写的区别吧),发现s大写和小写的刷新频率是不一样的,Sleep的单位是ms,sleep的单位是s,而且经过测试,自我感觉用这种方法最舒服的是Sleep(100)。只在这里对S和s做出区分,在后文中统一用sleep来作为标识符表达这个函数】

首先介绍下sleep函数,sleep函数的作用效果其实就是将屏幕短暂的暂停,暂停的秒数是你在括号中输入的时间,例如:

Sleep(100);

它的意义就是在程序编译到此处时,程序的编译时间暂停0.1s。

当然,这种解决方法同样也是不适合使用的,因为sleep虽然解决了屏幕多次闪烁的问题,可是这仍然没有解决根本上的原因,并且阻断了与玩家的实时交互,会在一定程度上遏制玩家继续进行游戏的兴趣,这种卡顿感是游戏制作中所避讳的,我们也只是在这里提供一种解决方法,培养思维。

顺便介绍下前面所缺少的知识,因为部分游戏的制作对于短暂的视觉暂停效果是不可或缺的。

2.再次引入一种新的具有清屏效果的工具

这种方法肯定是直接解决的最佳方案了,也是我们要继续介绍的下面两种从不同层面上所实现的清屏效果。

gotoxy函数

第一种实现方式,我们是通过再次引入一种新的函数以近似同样的效果来实现对屏幕的二次输出。

需要引入头文件

#include

首先,需要介绍的是gotoxy函数是需要自己定义的,一般的编译器比如我第一篇文章所推荐的Dev c++中没有函数库包含gotoxy函数的功能,所以我们需要在主函数前进行如下输入:

void gotoxy(int x,int y) { HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); COORD pos; pos.X = x; pos.Y = y; SetConsoleCursorPosition(handle,pos); }

(*以下内容对新手不做详细要求,我在这里声明只是有助于深度理解:

HANDLE是一种句柄类型,句柄是一种获得关键信息的类似于c语言程序设计中标识符的东西,可以从计算机内部获取信息作为每个操控的专属识别传递到索取的地方。

STD_OUTPUT_HANDLE是nstdHandle的一种输出设备的取值,所取的值还有另外两种我们不会在这里展开讲。

COORD是windows API中定义的结构,它表示了字符在控制台屏幕上的坐标,(x,y)与我们gotoxy(x,y)函数中输入的值相匹配。)

它的作用效果并不难,就是将光标移动到所输入的位置(x,y)中。

它的作用效果并不是清屏,很奇怪,对吗?为什么它可以进行清屏效果的实现?

主观上的,我将有着清屏作用的这两种函数分为了整体清屏和局部清屏。

字面意思来看,整体清屏也就是全部一次清除后再次输出,即前文介绍的system("cls")函数;而局部清屏就是我们所要讲到的如何利用gotoxy函数实现清屏效果: 在我们已经执行了一次循环体的前提下,我们的显示屏中已经有了输出界面,而这时我们再次执行循环体,gotoxy函数会将光标移动到(x,y)的位置,而我们会在循环体头部写入

gotoxy (0,0);

这样,光标会移动到(0,0)的位置,然后进行二次绘图,类似于“重画”的效果,重新进行打印。

那为什么可以防止闪烁呢?

我们在进行光标移动的时候,只是在单纯的进行循环体中内容的输出,而我们是在上一次循环体已输出内容的基础上进行的二次输出,即在一个已经绘画的模板上再次进行一次相似的创作。

在这里,注意我们提到的例子中用词是“相似的创作”,这例子也不是空穴来风,因为我们只是重绘,所以我们不可以有太大地方的改动(假如我们可以实现动画的制作,在每一帧的情况下迟早都会有大幅度的画面改动,那么这时我们就会看到一个画面中上半部分出现这一帧的内容,下半部分出现前一帧的情况)。因此,这种解决方案也被称为“不完美的解决闪烁的方案”。

当然,在运行的时候,会出现光标在屏幕中“随处移动”的现象,所以我们需要再次应用另一种函数,如下:

void HideCursor() { CONSOLE_CURSOR_INFO cursor_info={1,0}; SetConsorInfo(GetStdHandle(STD_OUTPUT_HANDLE),&cursor_info); } int main(){ HideCursor(); return 0; }

这个函数当然也是需要自己写入,它的作用是隐藏光标。只是为了防止光标乱窜,辅佐gotoxy函数更好的实现清屏功能,并无直接起到清除内容的作用。

那么我们到底有没有完全完美的解决方案呢?

双缓冲技术

这是最最最完美的解决方案,对于普通的接触小型游戏或其他方向的部分人可能不会接触到双缓冲的实现。在图形处理编程过程中,双缓冲是基本技术之一。正因为能十分“无痕”的处理图像问题,所以它可以在编译游戏领域解决闪烁问题,也由此使得它得到了广泛的应用。

(由于本人学识有限,对双缓冲技术做出较为口头的阐述中倘若出现错误还请在评论区指出错误,我会及时进行更正)

首先,我需要给出我们计算机较为基层的执行次序:

 为了我们解释更加方便,所以我们引入上面这张流程图,显示缓冲区有标准输入输出流的支持,这样我们的显示屏会将显示缓冲区的输出内容实时获取。正因为如此,所以我们无法完全意义上的阻止屏幕闪烁。

因为我们没有时间去存储先前的内容就清空了屏幕,打印了更新后的内容。

而双进程是这样实现的:

#include #include int main(){ //获取默认标准显示缓冲区句柄 HANDLE hOutput; COORD coord={0,0}; hOutput=GetStdHandle(STD_OUTPUT_HANDLE); //创建新的缓冲区 HANDLE hOutBuf = CreateConsoleScreenBuffer( GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CONSOLE_TEXTMODE_BUFFER, NULL ); //设置新的缓冲区为活动显示缓冲 SetConsoleActiveScreenBuffer(hOutBuf); //隐藏两个缓冲区的光标 CONSOLE_CURSOR_INFO cci; cci.bVisible=0; cci.dwSize=1; SetConsoleCursorInfo(hOutput, &cci); SetConsoleCursorInfo(hOutBuf, &cci); //双缓冲处理显示 DWORD bytes=0; char data[800]; while (1) { for (char c='a'; c


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有