EGE答疑与补充

EGE专栏:EGE专栏

EGE答疑与补充

文章最后修改时间:2020年9月13日 16:44

一、EGE问题答疑

  更详细的EGE相关介绍请查看 EGE专栏 中的 EGE基础教程

EGE吧答疑帖

  这个答疑帖回答了新手常问的一些问题

http://tieba.baidu.com/p/5219031936

EGE程序的大致结构

(1) 最简单的程序

  • 包含头文件
  • 初始化图形窗口
  • 关闭图形界面
#include <graphics.h>

int main()
{
	//初始化为640*480大小
	 initgraph(640, 480);
	
	 //等待用户按键
	 getch();
	
	 //关闭图形界面
	 closegraph();
	 return 0;
}

(2) 常用EGE程序结构

  • 常用的EGE程序则包含了常用的设置和一个循环。
#include <graphics.h>

int main()
{
	//窗口相关初始化设置
	//设置窗口大小,设置手动渲染(减少闪烁,提高绘图效率)
	initgraph(640, 480, INIT_RENDERMANUAL);

	//绘图相关设置设置
	setbkcolor(WHITE);		//设置背景颜色

	//基本的循环,控制帧率为每秒60帧,循环直到窗口关闭
	for ( ; is_run(); delay_fps(60)) {
		//绘画, 交互
		
	}
	
	//绘图结束,关闭窗口
	closegraph();

	return 0;
}
  • 这里则对相关设置进行了扩展,包含常用的参数设置。
#include <graphics.h>

int main()
{
	//设置为普通窗口(去除动画,有边框),设置窗口的位置
	setinitmode(0, 100, 100);

	//设置为手动模式,只有在调用延时功能的函数时才更新绘制,减少闪烁
	setrendermode(RENDER_MANUAL);	//设置为手动模式
	
	int screenWidth = 640, screenHeight = 480;
	initgraph(screenWidth, screenHeight);			//初始化窗口
	setcaption("EGE窗口标题");		//设置窗口标题

	//颜色设置
	setbkcolor(WHITE);		//设置背景颜色
	setcolor(RED);			//设置画笔颜色
	setfillcolor(YELLOW);	//设置填充颜色
	
	//设置文字背景颜色为透明(如果不设置文字会自带色块,当然,如果不清屏,直接覆盖,就不用设置)
	//setbkmode(TRANSPARENT);

	//基本的循环,控制帧率为每秒60帧,这是大多显示器默认的刷新频率
	//is_run() 判断窗口是否被关闭,窗口关闭则退出循环
	for ( ; is_run(); delay_fps(60)) {
		//绘画, 交互
		
	}
	
	//绘图结束,关闭窗口
	closegraph();

	return 0;
}

窗口

(1) EGE窗口打开与关闭

  • 窗口打开使用 initgraph(), 关闭窗口使用 closegraph()
initgraph(640, 480, 0);
closegraph();

调用 closegraph(), 关闭窗口之后,窗口也仅仅是不显示,程序仍在运行,不是真正的关闭 ,再次 initgraph()打开也不会重新初始化窗口,只会清空窗口内容,不能调整窗口模式。

(2) 窗口开始动画关闭与打开

  • 在使用默认设置的情况下,是Debug模式下不带窗口开始动画,Release模式下带窗口开始动画
  • 可以在 initgraph() 中添加 窗口初始化模式 参数,参数为0, 去掉动画
initgraph(screenWidth, screenHeight, 0);
  • 如果想打开窗口开始动画, 则可以设置为 INIT_WITHLOGO.
initgraph(screenWidth, screenHeight, INIT_WITHLOGO);
  • 函数 setinitmode(mode, x, y) 中第一个参数也是设置窗口初始化模式,所以也可以在initgraph()之前调用进行设置。在setinitmode()中设置了模式之后,在initgraph()中的模式设置就无效了。
setinitmode(INIT_WITHLOGO, 100, 100);
initgraph(640, 480);

(3) 窗口调整大小

  • EGE的窗口不能直接拖动边框调整大小
  • 可以使用 resizewindow() 调整窗口大小,窗口左上角位置不变,并且窗口内容会被清空。
  • 也可以再次调用 initgraph(), 传入不同的窗口大小参数,改变窗口大小。
  • 此时,窗口大小改变,之前的窗口设置仍然保留, 并且左上角位置和原来的窗口一致,。此时 initgraph() 仅仅是作为调整窗口大小,而不是初始化窗口,所以在 initgraph() 中改变窗口模式无效, 使用 setinitmode() 无效,
  • 即使你调用 closegraph(), 它也只是不显示,重新打开也不会重新初始化窗口,改变不了初始化模式。
resizewindow(width, height);

(4) 设置窗口标题

initgraph() 之前之后都可以调用

	setcaption("EGE窗口标题");

(5) 去除窗口边框

initgraph() 中设置初始化模式为 INIT_NOBORDER(无边框)

initgraph(640, 480, INIT_NOBORDER);

(6) EGE图形窗口只有一个

  • 不能同时打开多个窗口
  • 想要打开多个窗口可以使用 windowsAPI 实现。

(7) is_run()

  • is_run() 是用来判断EGE窗口环境是否存在的,这个是否存在只是一个EGE内部的一个标记
  • 使用 closegraph() 关闭窗口后,EGE窗口环境标记依然是 true,因为 closegraph() 实际上只是隐藏EGE窗口不显示。

  那什么时候 is_run() 会返回 false 呢? 就是窗口初始化模式先设置INIT_NOFORCEEXIT,然后点击EGE窗口右上角的关闭按钮,或者使用快捷键 Alt + F4 ,这是EGE窗口并不会关闭,但是EGE窗口环境标记 会变成 false, is_run() 就会返回 false
  如果不设置的话,点击EGE窗口右上角的关闭按钮将窗口关闭后,程序就会被强制退出,不会再运行下去
  所以这时下面的两个程序是一样的,循环外面的那句都不会被运行。

for ( ; is_run(); delay_fps(60)) {

}

//下面不会执行
printf("窗口环境已不存在\n");
closegraph();
for ( ; true; delay_fps(60))

	delay_fps(60)) {
}

//下面不会执行
printf("窗口环境已不存在\n");
closegraph();

  当 窗口初始化模式 添加 INIT_NOFORCEEXIT 后,此时点击EGE窗口右上方关闭按钮并不会关闭EGE窗口,但是此is_run() 会返回 false,从而退出循环,往下执行程序。
  如下,需要先调出控制台

#define SHOW_CONSOLE
#include <graphics.h>
#include <stdio.h>
#include <stdlib.h>

int main()
{
	initgraph(640, 480, INIT_NOFORCEEXIT);
	setbkcolor(WHITE);
	setcolor(BLACK);
	setfont(30, 0, "黑体");

	xyprintf(40, 40, "请关闭EGE窗口,然后看控制台");

	for (; is_run(); delay_fps(60)) {

	}

	printf("已经退出循环\n");

	system("pause");

	closegraph();

	return 0;
}

  需要注意,当点击EGE窗口关闭按钮后,getch(), getkey() 和 getmouse() 将不会阻塞程序

(8) 控制台(黑窗口)

  • VS2017 默认不显示控制台,想显示控制台,可以在 #include <graphics.h> 之前 #define SHOW_CONSOLE
#define SHOW_CONSOLE
#include <graphics.h>
  • CodeBlocks和DevC++ 如果有控制台,可以在项目属性中进行设置,设置项目类型为GUI程序,而不是控制台程序

getch() 相关问题

原因

  1. EGE中的getch()conio.h中的函数定义冲突
  2. EGE中的getch() 只能用于图形窗口,不能用于控制台
详细分析
  • getch() 的功能是阻塞程序,等待按键按下,返回按下按键的键码,常用来做暂停。
  • EGE中定义有 getch(), conio.h 头文件中也有getch(),
  • 如果你在同一个 .cpp源文件 中,在graphics.hege.h 之前包含 conio.h ,那么将会编译报错, 放在后面虽然不报错,但是 getch() 在控制台中并没有起到作用。

  需要注意的是,EGE中的 getch() 是不能用在控制台上的,也就是说,如果你设置了显示控制台,那么焦点在控制台上时,按任意键是没有效果的。
  如果你在程序中包含了 graphics.hege.h 头文件,但是又没有调用创建图形窗口,只使用控制台。这时调用EGE 中的 getch() 是会出问题的, 因为EGE中的getch()对控制台不起作用

解决方案

那如果我想使用控制台,想要 getch(),怎么办呢?

方法一:
使用 stdlib.h 中的 system() 替代

#include <stdlib.h>

system("pause");

效果是起到暂停作用,按下任意键继续,并且显示 “ 按下任意键继续…

方法二:
在不同的源文件中包含,并且自己另外定义一个命名不冲突的函数。

graphics.hconio.h 分开包含在不同的.cpp文件中

可以另创建一个.cpp文件, 包含 <conio.h>, 然后另写一个 getch_console(), 里面调用 conio.h 中的 getch() 就行了
如果你是使用VS的话,编译时还可能说使用了被遗弃的 getch() 。这时把 getch() 换成用 _getch() 就好了

包含了 ege 的头文件后,_getch()是不可以用的

#inlcude <conio.h>

int getch_console() {
	return getch();
}

然后就在使用到的源文件中,加个函数声明,然后就可以用了

#define SHOW_CONSOLE
#include <graphics.h>
#include <stdio.h>

int getch_console();

int main()
{
	int a;
	printf("输入数字:");
	scanf("%d", &a);

	printf("输入的数字为:%d\n按任意键继续", a);
	getch_console();

	initgraph(640, 480, 0);
	xyprintf(100, 220, "按任意键退出");
	getch();

	closegraph();

	return 0;
}

不要在帧循环中重复地从文件中获取图像

比如下面这个有什么问题?

for ( ; is_run(); delay_fps(60)) {
	PIMAGE pimg = newimage();
	getimage(pimg, "image.jpg");
	
	//绘图
	putimage(0, 0, pimg);
}

1. 创建图像后没有使用 delimage() 销毁图像,出现了 内存泄漏 。由于每秒创建几十个图像对象,如果你图片很大的话,运行久了,你会发现内存占用极高。

2. 从文件中读取图像是很慢的,很耗时的过程,如果你在里面每次都读取数目极多的图像,会卡到你怀疑人生。

3. 图片文件中的图像都是不变的内容,没必要重复获取。

4. 除非你要获取的图像是不断变化的,否则不要在循环中重复地读取。重复地读取相同的图像,除了能让你的程序卡以外没啥用

要把从PIMAGE 放在帧循环外读取,退出后,如果不用,记得使用 delimage()

PIMAGE pimg = newimage();
getimage(pimg, "image.jpg");

for ( ; is_run(); delay_fps(60)) {
	//操作
		
	//绘图
	putimage(0, 0, pimg);
}

//记得销毁
delimage(pimg);

如何保存游戏进度等数据

这就需要使用文件, 将程序中的一些数据写入文件来保存。在每次程序开始运行时,就从文件中读取数据。这样就可以做到读取上次的进度的。

窗口绘图频繁出现闪烁怎么办

  • 如果绘图是出现窗口频繁闪烁, 可以设置初始化模式为 INIT_RENDERMANUAL(或者是INIT_ANIMATION, 这个INIT_ANIMATION是几个模式的组合,其中包括有INIT_RENDERMANUAL模式,是专门用于动画的模式)
  • 窗口初始化模式在setinitmode()中的第一个参数中设置,或者在 initgraph() 中第三个参数中设置
//设置为INIT_RENDERMANUAL模式,以及窗口位置
setinitmode(INIT_RENDERMANUAL, 100, 100);
initgraph(640, 480);
或者
initgraph(640, 480, INIT_RENDERMANUAL);
  • 或者在 initgraph() 之后使用 setrendermode(RENDER_MANUAL);
initgraph(640, 480);
setrendermode(RENDER_MANUAL);

快速画点方式

EGE中画点是用 putpixel() 函数
putpixel 做了两个工作:

  • 判断点的位置是否在有效范围内
  • 帧缓存中对应像素的颜色赋值。

由此,有了更高效的 putpixel_f(), 它并不会检测点的位置是否在有效范围内,少了一些运算,能更快,但缺点是需要调用者确保位置的合法性。不然将会越界访问。

频繁的调用函数依然是有些费时,所以可以获取EGE窗口帧缓存的首地址,然后由我们来给对应像素改变颜色。

  • 获取窗口帧缓存或图像的首地址(pimg 若为 NULL 则表示窗口帧缓存)
color_t* buff = getbuffer(pimg);
  • 获取图像大小(pimg 若为 NULL 则表示窗口帧缓存)
int width = getwidth(pimg), height = getheight(pimg);
  • 直接修改缓存区的数据
buff[x + y * width] = color;

坐标 (x, y) 的位置对应缓存区中的 x + y * width
这省去了调用函数的开销,但是自己要保证不要越界访问
即要确保0 <= x < width, 0 <= y < height;

图像

(1) 清屏

  • 清屏使用 cleardevice( ), 原理是将窗口或图像用背景色填充
cleardevice(PIMAGE pimg = NULL);

参数是一个PIMAGE, 默认是NULL, 即将窗口清屏

cleardevice();

如果是传入一个图像,则是对图像清屏

cleardevice(pimg);
cleardevice() 的位置
  • 一般我们绘画是这么一个循环, 每次进入循环后,绘图,这是很快的。然后延时一段时间,这段时间里图像是保持不变的。所以我们要把cleardevice()放在绘画部分的前面,而不能放在后面,否则绘图完成后被清空,你将会看不到你的绘图
for ( ; is_run(); delay_fps(60)) {
	cleardevice();
	
   //绘图
   draw();
}

(2) PIMAGE 与 IMAGE 类

PIMAGE 是 指向IMAGE对象的指针类型,定义如下

typedef IMAGE* PIMAGE;

IMAGE 是EGE中的图像类,它的创建必须在窗口初始化之后。
使用 newimage() 可以创建一个IMAGE对象,并返回指向该对象的指针。因为PIMAGE仅仅是指针类型,所以可以先定义之后再用 newimage() 赋值。

PIMAGE pimg;
pimg = newimage();

(3) newimage() 与delimage()

  • newimage() 创建一个IMAGE 类对象,并返回指向该对象的指针。
  • newimage() 必须在 initgraph() 之后调用,不然程序会崩溃。
  • 所以如果一个类初始化时调用有 newimage() , 那么不能实例化为全局变量, 因为全局变量是在main()函数之前初始化完成的,此时 initgraph() 还没有调用。
  • newimage() 有两个重载,没有参数的那个将创建大小为 1x1 的图像, 有参数的将创建指定宽高的图像。
PIMAGE	newimage(); // 创建PIMAGE
PIMAGE	newimage(int width, int height); // 创建PIMAGE
  • 使用newimage() 创建的图像是动态内存分配的图像,不使用时需要使用 delimage(pimg) 进行销毁。

(4) 函数中的PIMAGE参数

  • 很多函数中都带有PIMAGE参数,这些参数,经常写成
函数名(参数, ...,  PIMAGE pimg = NULL)

其中 PIMAGE pimg = NULL 的表示接受一个 PIMAGE参数,但是这个参数可以省略,省略时相当于参数为 NULL。而对于EGE中的PIMAGE 类型, 很大一部分如果是NULL, 则指的是窗口, 如果是一个指向图像的指针,则操作的是图像。

(5) 从文件中加载图片

PIMAGE pimg = newimage();
getimage(pimg, "文件名");
  • 加载后得到的是原图大小的图像,并且 getimage()会改变pimg的大小所以newimage()不需要参数。
  • getimage()并没有缩放的功能,想要缩放可以先加载到一个临时图像上,再使用putimage()缩放绘制到我们的图像上。详细可以参考EGE专栏中的EGE基础教程中篇
  • 如果你的图像什么都没有,一片漆黑,说明按照所在路径,并没有对应的图片。那么请检查一下你的文件名是否正确文件名需要带路径和文件扩展名, 如果你不知道什么是路径和文件扩展名,那么请看本文后面的 "文件名 绝对路径与相对路径"
  • 图像不使用时,记得调用 delimage(pimg);

(6) 图像数组,加载多张图片

  • 经常需要从加载很多图像用作动画,所以定义可以PIMAGE 数组,然后从文件中加载。
    动画中很多图像是一组动作,是有编号的一组图像,假设有很多图像, 名字是 “image1.png” ~ “image20.png” , 一共二十张,序号1 ~ 20.那么可以先用 sprintf() 生成文件名,再进行加载, sprintf 在头文件 stdio.h 中。
    因为这里图片序号是从1到20,数组序号是0~19, 所以要根据实际调整一下下标。pimgs[i] 对应的是图片 i +1.
PIMAGE pimgs[20];
char fileName[30];

for (int i = 0; i < 20; i++) {
	sprintf(fileName, "image%d.png", i + 1);
	pimgs[i] = newimage();
	getimage(pimgs[i], fileName);
}
  • 当图像不使用时,请使用delimage() 来销毁图像。
for (int i = 0; i < 20; i++) {
	delimage(pimgs[i]);
	pimgs[i] = NULL;
}

(7) 截屏

  • 使用 getimage( ), 在 src 中截取, 不设置src 则表示在窗口中截取
    截取区域左上角坐标为(x, y), 宽为 width , 高为 height, 截图后保存在 pimg 中。
	getimage(PIMAGE pimg, int x, int y, int width, int height, PIMAGE src = NULL);
  • 窗口宽高可以分别使用 getwidth(), getheight() 获取, 如果你想截图整个窗口,可以使用
getimage(pimg, 0, 0, getwidth(), getheight());
  • 下面是截取整个窗口保存为图像的方法。
PIMAGE pimg = newimage();
getimage(pimg, 0, 0, getwidth(), getheight());
  • 如果从另一张图像截取
PIMAGE pimg = newimage();
getimage(pimg, 0, 0, width, heigth, anotherPimg);

坐标系

(1) 屏幕坐标系

  • 左上角的像素坐标为(0, 0), 往右是x增大方向,往下是y增大方向
  • 如果窗口大小是640 x 480, 那么左上角的像素是(0, 0), 右下角的像素是(639, 479).

(2) 纹理坐标系

  • 纹理坐标是归一化的,用纹理图片的宽和高为基准,无论实际的纹理图片的尺寸是多大,宽的长度作为x轴的1.0, 高的长度作为y轴的1.0 , 所以纹理右下角的位置是(1.0, 1.0)
    在这里插入图片描述
  • 纹理坐标可以有正有负, 根据坐标系,在纹理图片左边的位置,X坐标就是负的,在纹理图的上方,Y坐标就是负的。在纹理图片右边的位置就是X坐标大于1的。

动画

(1) 动画原理

  • 动画,顾名思义,就是能“动”的画。
    人的眼睛对图像有短暂的记忆效应,所以当眼睛看到多张图片连续快速的切换时,就会被认为是一段连续播放的动画了。

  • 比如,中国古代的“走马灯”,就是用的这个原理。
    在这里插入图片描述
    在这里插入图片描述

在这里插入图片描述

  • 有些人还会在一个本子每页上手绘一些漫画,当快速翻页的时候,也会看到动画的效果。

  • 动画是由一张张图片组成的,在计算机中,我们称每一张图片为 一帧画面 。

如果我们想实现这么一个动画:一个水杯放在桌子的左边,移动到右边,那么我们实际操作的,只是水杯。
所以动画的实现,只是对运动变化了的部分的处理

(2) 逐帧 与 关键帧

  • 类似于上面提到的手绘翻页方式,我们可以将这个水杯在每帧画面中的位置一一找出来,这样实现动画的方式就叫作 逐帧动画,我们需要处理动画中的每一帧

  • 我们一般在计算机上用 FPS ( Frames Per Second) ,即 每秒的帧数 来表示动画的刷新速度,基于屏幕的刷新率等其他原因,在计算机上一般采用 60 FPS
    如果运动变化幅度较缓,减半到 30 FPS 时,我们肉眼也是可接受的。
    较低的 FPS 会让我们有“卡顿”的感觉。

逐帧动画是最直接的,但要处理的帧数太多,所以实现过程是会麻烦。

计算机的工作就是来完成重复单调的工作的,所以,有些工作是可以考虑让计算机来完成的。

上面的例子,可以变成一个涉及数学和物理的问题:一个杯子初始位置在左边,n秒后匀速运动到右边,那么在每 1/60 秒的时候,这个杯子的位置显然是可以计算出来的了。
所以,我们其实只需要指定一些 关键 信息就能让计算机自己计算出每一帧杯子的位置了:

起始位置,比如一个坐标 (0,0)
结束位置,再比如一个坐标 (100,0)
动画总时间,比如 0.25 秒
匀速运动
这种方式就称之为 关键帧动画。即我们只需要给定几个关键帧的画面信息,关键帧与关键帧之间的过渡帧都将由计算机自动生成

(3) EGE动画的实现

  • 可以按照绘制,变动,清屏,重新绘制的步骤进行,从而能得到动画的效果
  • 其中清屏的步骤看情况,如果图片绘制的地方刚好把之前的绘制的图遮住,那就不需要清屏,如果没能遮住,能看到原来的图,就需要进行清屏。
  • 下面是官网的一个简单平移示例,试试看把cleardevice() 去掉会是什么效果。
//基础动画二:简单平移动画
#include <graphics.h>

void mainloop()
{
	// 动画控制变量,控制横坐标,初始值为0
	int x = 0;

	setcolor(EGERGB(0, 0xFF, 0));
	setbkcolor(WHITE);
	setfillcolor(EGERGB(0, 0, 0xFF));

	for (; is_run(); delay_fps(60))
	{
		// todo: 逻辑更新
		//计算新坐标,右移一个像素,如果等于440则重新移回x=0,达到动画循环
		x = (x + 2) % 440;

		// todo: 图形更新
		//清屏,重新在新的位置绘图图像
		cleardevice();
		//以x为圆的左边界绘画,为什么是左边界?因为圆心坐标是 (x + 半径) 了
		fillellipse(x + 100, 200, 100, 100);
	}
}

int main(void)
{
	//INIT_ANIMATION相当于INIT_NOFORCEEXIT|INIT_DEFAULT|INIT_RENDERMANUAL
	//下面就不需要再多一步setrendermode
	setinitmode(INIT_ANIMATION);
	// 图形初始化,窗口尺寸640x480
	initgraph(640, 480);
	// 随机数初始化,如果需要使用随机数的话
	randomize();
	// 程序主循环
	mainloop();
	// 关闭绘图设备
	closegraph();
	return 0;
}

(4) 逐帧动画

如果一些动画是有一些连续的图组成,可以使用PIMAGE数组 保存,然后根据帧数循环绘图。
如果按帧循环帧率是A FPS, 帧动画的帧率是每秒B帧, 那计算公式应该是
((FramCount * B) / A) % B

PIMAGE pimgs[8] = {NULL};

获取图像

unsigned int FrameCount = 0;
for ( ; is_run(); delay_fps(60)) {
	//绘图
	putimage(0, 0, pimgs[FrameCount * 8 / 60 % 8 ]);
	FrameCount++;
}

销毁图像

EGE播放音乐

  • EGE可以播放音乐,使用MUSIC
    具体如何使用可以查看EGE专栏之EGE基础教程下篇
  • 可以同时播放多个音乐定义多个MUSIC对象分别打开不同的音乐文件进行播放。
  • 一个音乐文件不能被多个的MUSIC对象同时打开

视频播放

  • EGE没有视频播放的类,但EGE是基于windowsAPI的,可以使用windowsAPI的函数来实现视频播放。

  • 主要是使用mciSendString() 函数发送命令,来播放视频。

  • 由于需要把视频嵌入到EGE窗口,所以需要获取EGE窗口句柄

HWND egeHWnd = getHWnd();
  • 然后将文件名, 窗口句柄, 播放区域传入下面编写的PlayVideoWindow()就行了
  • 通过mciSendString发送命令,控制视频打开,播放,暂停,关闭等。
void PlayVideoInWindow(char *pszFileName, HWND hWnd, int x, int y, int width, int height)
{
	char openCommand[MAX_PATH] = { 0 };
	char sizeCommand[MAX_PATH] = { 0 };

	/*构建命令字符串*/
	// 构造mci打开视频命令, 设置视频播放的窗口
	wsprintf(openCommand, "open \"%s\" type mpegvideo alias myvideo parent %u style %u", pszFileName, hWnd, WS_CHILD);
	// 构造mci视频播放位置大小命令, 设置视频播放的窗口
	wsprintf(sizeCommand, "put myvideo window at %d %d %d %d", x, y, width, height);


	// 打开视频, 指定窗口
	mciSendString(openCommand, NULL, 0, NULL);
	// 设置视频播放位置及画面大小
	mciSendString(sizeCommand, NULL, 0, NULL);
	// 播放视频
	mciSendString("play myvideo", NULL, 0, NULL);
}
  • 下面做一个播放的视频的示例
  • 这个程序有很大的问题,就是播放视频时不能移动窗口,也不能最小化窗口,否则窗口会卡死,视频播放完后也会卡死
  • 所以只是做个播放视频的说明,其它问题解决方法请找相关资料(我不会)。
#include <graphics.h>
#include <cstdio>

void PlayVideoInWindow(char *pszFileName, HWND hWnd, int x, int y, int iWidth, int iHeight);

int main(void)
{
	setinitmode(0, 100, 100);
	int screenWidth = 640, screenHeight = 480;
	initgraph(screenWidth, screenHeight);

	setbkcolor(WHITE);

	char fileName[] = "秦时明月之君临天下.avi";
	HWND hWnd = getHWnd();


	PlayVideoInWindow(fileName, hWnd, 0, 0, screenWidth, screenHeight);

	getch();

	mciSendString("close myvideo", NULL, 0, NULL);
	
	closegraph();

	return 0;
}
void PlayVideoInWindow(char *pszFileName, HWND hWnd, int x, int y, int width, int height)
{
	char openCommand[MAX_PATH] = { 0 };
	char sizeCommand[MAX_PATH] = { 0 };

	/*构建命令字符串*/
	// 构造mci打开视频命令, 设置视频播放的窗口
	wsprintf(openCommand, "open \"%s\" type mpegvideo alias myvideo parent %u style %u", pszFileName, hWnd, WS_CHILD);
	// 构造mci视频播放位置大小命令, 设置视频播放的窗口
	wsprintf(sizeCommand, "put myvideo window at %d %d %d %d", x, y, width, height);


	// 打开视频, 指定窗口
	mciSendString(openCommand, NULL, 0, NULL);
	// 设置视频播放位置及画面大小
	mciSendString(sizeCommand, NULL, 0, NULL);
	// 播放视频
	mciSendString("play myvideo", NULL, 0, NULL);
}



二、程序问题

1. 无法打开.exe进行写入

  • 这种情况是程序还在运行,无法进行写入。如果你没看到有EGE的窗口,那可能是EGE窗口关闭了,但是程序没有结束。可以按 Ctrl + Alt + Del 进入任务管理器,找到运行的EGE程序,右键选择结束程序。
    在这里插入图片描述
    在这里插入图片描述
  • 为什么会有EGE窗口退出,但是程序没有结束的情况。来看看这个代码
#include <graphics.h>

int main() {

	initgraph(640, 480, 0);
	
	closegraph();

	getch();

	return 0;
}

  关闭了窗口之后,有一个暂停接受键盘输入的 getch(), 因为窗口已经关闭,没有了窗口,已经无法对程序进行键盘输入了,程序仍在运行,这时需要在任务管理器结束程序。

2. 运行时出现 Fatal error: At function ‘newimage’, construct PIMAGE before ‘initgraph’

在这里插入图片描述

  原因是图像的创建需要在窗口初始化之后才能进行
图像的创建使用 newimage(), 窗口初始化在第一次调用 initgraph() 是进行,所以调用 newimage() 必须是在 第一次调用 initgraph() 之后。
  如果你定义的类的构造函数里面有创建图像操作,那么就不能在窗口初始化之前创建该类的对象。
  需要注意的是全局变量,全局变量是在main()之前初始化完成的。所以如果一个类有全局对象,那么就不能在对应的构造函数中创建图像。

3. 使用了未初始化的局部变量 ‘pimg’

在这里插入图片描述
对应代码如下:

PIMAGE pimg;
getimage(pimg, "image.jpg");

原因是使用的 pimg 并没有初始化,需要先创建图像才能调用 getimage()

PIMAGE pimg = newimage();
getimage(pimg, "image.jpg");

EGE专栏:EGE专栏

  • 32
    点赞
  • 87
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

依稀_yixy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值