如何使用 xlib 创建游戏循环
How to create a game loop with xlib
我正在尝试为 xlib window 创建一个游戏循环,但我无法正确绘制 window。现在我正在使用 XCreateSimpleWindow(...) 创建一个 window,并使用 for 循环一次绘制所有像素。 (这些像素的颜色是从一个大整数数组中读取的,现在我将所有像素设置为蓝色。)现在实际的游戏循环如下:
void loop() {
while (true) {
// Clear the window (the background color is set to white)
XClearWindow(dsp, win);
// Loop through all pixels of the 800*600 window
for (int j = 0; j < 600; j++) {
for (int i = 0; i < 800; i++) {
// Read the color from the pixels array (always blue for now)
long int color = pixels[i + 800*j];
// Set the foreground color for drawing
XSetForeground(dsp, gc, color);
// Draw the pixel
XDrawPoint(dsp, win, gc, i, j);
}
}
// Flush the output buffer
XFlush();
}
}
全局定义变量dsp、win、pixels、gc。
现在我编译执行二进制文件时,y坐标低的行大多是蓝色的,y坐标高的行大多是白色的。在这两者之间,很容易看出一次绘制所有像素是如何花费太多时间的。我预计这种效果是因为首先绘制了最上面的行(低 y),这意味着这些像素的 XClearWindow() 和 XDrawPoint() 之间有一个短暂的延迟。 (我还测试了 fps,运行 while(true) 循环一次大约需要 7 毫秒。)
我做了一些研究,并了解了双缓冲如何解决这个问题。我确实遵循了有关使用 xlib (Xdbe) 双缓冲的指南,但它似乎并没有解决问题。有没有比循环遍历所有像素更快的使用 xlib 绘图的方法?双缓冲不是应该解决这个问题,还是我实施不正确?
直接与 Xlib 通信是 90 年代初期的事情(阅读:在过去的 20 年里,除非他是框架设计师,否则没有人这样做过!)。
你说得对,循环遍历像素以在屏幕上更新它们的速度令人难以置信。这就是为什么几乎所有现代 GUI 框架都使用它们自己的 Xbuffer,它们只是在上面绘制并指示 X 进行渲染。
作为对您方法的评论:
在原始 X 上进行游戏开发毫无意义,因为有更多可盈利、性能更好、更易于使用、小型且经过良好测试的库。以SDL为例
2021 年 3 月更新:正如 Pablo Ariel 在下面指出的那样,现在可能很重要的一点是要指出,花时间学习 X11 并不是明智的投资:X 正在消失;X 正在消失;它花了几十年,但最大的 Linux 发行版(在 Debian 10 之后,Ubuntu 21.04 也这样做了,因此像 Mint 这样的所有衍生产品默认使用 Wayland;Fedora 34 也这样做)正在使用默认为韦兰;在这些上仍然可以使用 X11,但是使用旧的东西是一个额外的步骤...
请注意,这并不意味着如果您想学习如何制作游戏,就应该学习 Wayland。或原始 OpenGL。或伏尔甘。 graphics/game 引擎是你的朋友,因为你 a) 不想自己处理所有低级的东西来抽象同一事物的不同版本之间的细微差别,并且 b) 你没有得到任何比写得好的引擎更快的东西,编写游戏引擎的人在这方面会比你更好。专注于相关部分,而不是处理最低级别的库!
您没有看到任何东西的原因是您忽略了 X 事件循环。您所做的只是向 X 服务器发送数据,但 X 服务器无法返回通信。
您必须设置一个循环,从队列中读取 XEvent 并分派 c.q。处理。类似于:
XEvent event;
while (XPending (m_display))
{
XNextEvent (m_display, &event);
if (XFilterEvent (&event, None))
{
continue;
}
switch (event.type)
{
case KeyPress:
..
case ButtonPress:
..
case Expose:
..
case MapNotify:
..
// etc
}
}
虽然这可以与无限循环相结合。
但是,是的,逐像素绘制非常慢。我什至不想计算协议开销...:P
我正在尝试为 xlib window 创建一个游戏循环,但我无法正确绘制 window。现在我正在使用 XCreateSimpleWindow(...) 创建一个 window,并使用 for 循环一次绘制所有像素。 (这些像素的颜色是从一个大整数数组中读取的,现在我将所有像素设置为蓝色。)现在实际的游戏循环如下:
void loop() {
while (true) {
// Clear the window (the background color is set to white)
XClearWindow(dsp, win);
// Loop through all pixels of the 800*600 window
for (int j = 0; j < 600; j++) {
for (int i = 0; i < 800; i++) {
// Read the color from the pixels array (always blue for now)
long int color = pixels[i + 800*j];
// Set the foreground color for drawing
XSetForeground(dsp, gc, color);
// Draw the pixel
XDrawPoint(dsp, win, gc, i, j);
}
}
// Flush the output buffer
XFlush();
}
}
全局定义变量dsp、win、pixels、gc。
现在我编译执行二进制文件时,y坐标低的行大多是蓝色的,y坐标高的行大多是白色的。在这两者之间,很容易看出一次绘制所有像素是如何花费太多时间的。我预计这种效果是因为首先绘制了最上面的行(低 y),这意味着这些像素的 XClearWindow() 和 XDrawPoint() 之间有一个短暂的延迟。 (我还测试了 fps,运行 while(true) 循环一次大约需要 7 毫秒。)
我做了一些研究,并了解了双缓冲如何解决这个问题。我确实遵循了有关使用 xlib (Xdbe) 双缓冲的指南,但它似乎并没有解决问题。有没有比循环遍历所有像素更快的使用 xlib 绘图的方法?双缓冲不是应该解决这个问题,还是我实施不正确?
直接与 Xlib 通信是 90 年代初期的事情(阅读:在过去的 20 年里,除非他是框架设计师,否则没有人这样做过!)。
你说得对,循环遍历像素以在屏幕上更新它们的速度令人难以置信。这就是为什么几乎所有现代 GUI 框架都使用它们自己的 Xbuffer,它们只是在上面绘制并指示 X 进行渲染。
作为对您方法的评论: 在原始 X 上进行游戏开发毫无意义,因为有更多可盈利、性能更好、更易于使用、小型且经过良好测试的库。以SDL为例
2021 年 3 月更新:正如 Pablo Ariel 在下面指出的那样,现在可能很重要的一点是要指出,花时间学习 X11 并不是明智的投资:X 正在消失;X 正在消失;它花了几十年,但最大的 Linux 发行版(在 Debian 10 之后,Ubuntu 21.04 也这样做了,因此像 Mint 这样的所有衍生产品默认使用 Wayland;Fedora 34 也这样做)正在使用默认为韦兰;在这些上仍然可以使用 X11,但是使用旧的东西是一个额外的步骤...
请注意,这并不意味着如果您想学习如何制作游戏,就应该学习 Wayland。或原始 OpenGL。或伏尔甘。 graphics/game 引擎是你的朋友,因为你 a) 不想自己处理所有低级的东西来抽象同一事物的不同版本之间的细微差别,并且 b) 你没有得到任何比写得好的引擎更快的东西,编写游戏引擎的人在这方面会比你更好。专注于相关部分,而不是处理最低级别的库!
您没有看到任何东西的原因是您忽略了 X 事件循环。您所做的只是向 X 服务器发送数据,但 X 服务器无法返回通信。
您必须设置一个循环,从队列中读取 XEvent 并分派 c.q。处理。类似于:
XEvent event;
while (XPending (m_display))
{
XNextEvent (m_display, &event);
if (XFilterEvent (&event, None))
{
continue;
}
switch (event.type)
{
case KeyPress:
..
case ButtonPress:
..
case Expose:
..
case MapNotify:
..
// etc
}
}
虽然这可以与无限循环相结合。
但是,是的,逐像素绘制非常慢。我什至不想计算协议开销...:P