C++ SDL教程翻译 Lesson 02 Getting an Image on the Screen

时间:2022-01-22 12:04:02

这是第二篇哦~像上一篇一样,末尾会有附加内容,方便你理解。

警告:转载此翻译文请注明来自CSDN博客,CSDN博客是此翻译唯一出处。

中文版:

课程2 在屏幕上获得图像

注意:从现在开始,教程只会涵盖源代码的关键部分。对于完整的程序,您将不得不下载完整的源代码。

//Starts up SDL and creates window
bool init();

//Loads media
bool loadMedia();

//Frees media and shuts down SDL
void close();

在第一个教程中,我们将所有内容都放在主函数中。
因为它是一个小程序,我们可以用它来解决,但是在真正的程序中(比如视频游戏),你想让你的代码尽可能的模块化。
这意味着您希望您的代码是整齐的,并且易于调试和重用。

意味着我们有处理初始化、加载媒体和关闭SDL应用程序的函数。
我们在源文件的顶部声明这些内容。

我收到很多电子邮件,关于如何调用这个函数,“关闭”会导致C中的冲突,因为它不支持函数重载。
这是我在本教程中使用C++的原因之一。
所以这不是bug,这个函数被称为“闭合”。

//The window we'll be rendering to
SDL_Window* gWindow = NULL;

//The surface contained by the window
SDL_Surface* gScreenSurface = NULL;

//The image we will load and show on the screen
SDL_Surface* gHelloWorld = NULL;

这里我们声明一些全局变量。
通常您希望避免在大型程序中使用全局变量。
我们之所以这么做是因为我们希望源代码尽可能的简单,但是在大型项目中,全局变量使事情变得更加复杂。
由于这是一个单独的源文件程序,所以我们不必太过担心。

这是一个称为SDL表面的新数据类型。
SDL表面只是一种图像数据类型,它包含图像的像素和呈现它所需的所有数据。
SDL表面使用软件渲染,这意味着它使用CPU来渲染。
渲染硬件图像是可能的,但这有点难,所以我们要先学习简单的方法。
在以后的教程中,我们将讨论如何渲染GPU加速图像。

我们将要处理的图像是屏幕图像(你在窗口中看到的)和我们将要从一个文件钟加载的图像。

注意,这些是指向SDL表面的指针。
原因是
1:我们将动态地分配内存来加载图像
2:最好是通过内存位置来引用一个图像。
想象一下,你玩的是一堵砖墙,它由同一块砖形的图像组成(就像超级马里奥兄弟一样)。
在内存中有几十个图像副本是很浪费的,您可以拥有一个图像副本并一次又一次地呈现它时。

另外,永远记得初始化指针。我们在声明时立即将它们设置为NULL。

bool init()
{
//Initialization flag
bool success = true;

//Initialize SDL
if( SDL_Init( SDL_INIT_VIDEO ) < 0 )
{
printf( "SDL could not initialize! SDL_Error: %s\n", SDL_GetError() );
success = false;
}
else
{
//Create window
gWindow = SDL_CreateWindow( "SDL Tutorial", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN );
if( gWindow == NULL )
{
printf( "Window could not be created! SDL_Error: %s\n", SDL_GetError() );
success = false;
}
else
{
//Get window surface
gScreenSurface = SDL_GetWindowSurface( gWindow );
}
}

return success;
}

正如您在这里看到的,我们已经使用了SDL初始化和窗口创建代码,并将其放入自己的函数中。
现在的新情况是,有一个东东叫做SDL_GetWindowSurface。

我们想要在窗口中显示图像,为了做到这一点,我们需要在窗口中获取图像。
所以我们叫SDL_GetWindowSurface来抓取窗口所包含的表面。

bool loadMedia()
{
//Loading success flag
bool success = true;

//Load splash image
gHelloWorld = SDL_LoadBMP( "02_getting_an_image_on_the_screen/hello_world.bmp" );
if( gHelloWorld == NULL )
{
printf( "Unable to load image %s! SDL Error: %s\n", "02_getting_an_image_on_the_screen/hello_world.bmp", SDL_GetError() );
success = false;
}

return success;
}

在loadMedia函数中,我们使用SDL_LoadBMP加载图像。
SDL_LoadBMP接受bmp文件的路径并返回已加载的表面。
如果函数返回NULL,那就意味着它失败了,所以我们用SDL_GetError恐惧打印到控制台的错误。

需要注意的一点是,这段代码假设您有一个名为“02_getting_an_image_on_the_screen”的目录,其中包含一个名为“hello_world.bmp”的图像。在你的工作目录中。
工作目录是您的应用程序认为它在运行的地方。
通常您的工作目录是您的可执行文件所在的目录,但是一些程序,如Visual Studio,将工作目录更改为vcxproj文件所在的位置。
因此,如果你的应用程序无法找到图像,请确保它在正确的位置。

void close()
{
//Deallocate surface
SDL_FreeSurface( gHelloWorld );
gHelloWorld = NULL;

//Destroy window
SDL_DestroyWindow( gWindow );
gWindow = NULL;

//Quit SDL subsystems
SDL_Quit();
}

在我们的清理代码中,我们破坏了窗口,并像以前一样退出了SDL,但是我们也必须处理我们加载的表面。
我们通过使用SDL_FreeSurface来实现这一点。
不要担心屏幕的表面,SDL_DestroyWindow会处理毁坏窗口的。

一定要养成在没有指向任何东西时让指针指向空的习惯。

int main( int argc, char* args[] )
{
//Start up SDL and create window
if( !init() )
{
printf( "Failed to initialize!\n" );
}
else
{
//Load media
if( !loadMedia() )
{
printf( "Failed to load media!\n" );
}
else
{
//Apply the image
SDL_BlitSurface( gHelloWorld, NULL, gScreenSurface, NULL );

在我们的主要函数中,我们初始化SDL并加载图像。
如果成功了,我们使用SDL_BlitSurface将加载的表面投射到屏幕上。

它的作用是获取一个源表面,并将其复制到目标表面。
SDL_BlitSurface的第一个参数是源映像。
第三个参数是目的地。
在以后的教程中,我们将讨论第2和第4个参数。

如果这是我们画的唯一的代码,我们仍然看不到我们在屏幕上加载的图像。还有一个步骤。

            //Update the surface
SDL_UpdateWindowSurface( gWindow );

在我们想要显示的屏幕上显示的所有内容之后,我们必须使用SDL_UpdateWindowSurface来更新屏幕。
当你画到屏幕上时,你通常不会在你看到的屏幕上画出图像。
默认情况下,大多数系统呈现都是双缓冲的。
这两个缓冲区是前面和后面的缓冲区。

当你做像SDL_BlitSurface这样的绘制调用时,你会渲染到后缓冲区。
你在屏幕上看到的是前缓冲。
我们这样做的原因是,大多数帧都需要将多个对象绘制到屏幕上。
如果我们只有一个前缓冲,我们就能看到物体被吸引到它的框架这意味着我们会看到未完成的框架。
所以我们要做的是先把所有的东西都画到后面的缓冲区中,一旦我们完成了,我们就交换了后退和前缓冲,这样用户就可以看到完成的帧了。

这也意味着,在所有的blit之后,您都不会调用SDL_UpdateWindowSurface,只是在当前框架的所有工作结束之后才会这样做。

            //Wait two seconds
SDL_Delay( 2000 );
}
}

//Free resources and close SDL
close();

return 0;
}

现在我们已经把所有的东西都渲染到窗口了,我们延迟了两秒钟,这样窗口就不会消失了。
在等待完成之后,我们关闭了我们的程序。

请下载本教程的媒体和源代码。


益文友_Allen说:此链接下载地址请见博文最下方,呵呵。


中英对照版:

Lesson 02 Getting an Image on the Screen
课程2 在屏幕上获得图像

Note: From now on the tutorials will only cover key parts of source code. For the full program, you will have to download the full source code.
注意:从现在开始,教程只会涵盖源代码的关键部分。对于完整的程序,您将不得不下载完整的源代码。

//Starts up SDL and creates window
bool init();

//Loads media
bool loadMedia();

//Frees media and shuts down SDL
void close();

In the first tutorial, we put everything in the main function.
在第一个教程中,我们将所有内容都放在主函数中。
Since it was a small program we can get away with that, but in real programs (like video games) you want to have your code as modular as possible.
因为它是一个小程序,我们可以用它来解决,但是在真正的程序中(比如视频游戏),你想让你的代码尽可能的模块化。
This means you want your code to be in neat chunks that are each easy to debug and reuse.
这意味着您希望您的代码是整齐的,并且易于调试和重用。

Here it means we have functions to handle initialization, loading media, and closing down the SDL application.
意味着我们有处理初始化、加载媒体和关闭SDL应用程序的函数。
We declare these near the top of our source file.
我们在源文件的顶部声明这些内容。

I get a lot of e-mails about how calling this function “close” causes conflicts in C because does not support function overloading.
我收到很多电子邮件,关于如何调用这个函数,“关闭”会导致C中的冲突,因为它不支持函数重载。
This is one of the reasons I am using C++ for this tutorial.
这是我在本教程中使用C++的原因之一。
So it is not bug that this is function is called “close”.
所以这不是bug,这个函数被称为“闭合”。

//The window we'll be rendering to
SDL_Window* gWindow = NULL;

//The surface contained by the window
SDL_Surface* gScreenSurface = NULL;

//The image we will load and show on the screen
SDL_Surface* gHelloWorld = NULL;

Here we declare some global variables.
这里我们声明一些全局变量。
Typically you want to avoid using global variables in large programs.
通常您希望避免在大型程序中使用全局变量。
The reason we’re doing it here is because we want the source code to be as simple as possible, but in large projects global variables make things more complicated.
我们之所以这么做是因为我们希望源代码尽可能的简单,但是在大型项目中,全局变量使事情变得更加复杂。
Since this is a single source file program we don’t have to worry about it too much.
由于这是一个单独的源文件程序,所以我们不必太过担心。

Here’s a new data type called an SDL Surface.
这是一个称为SDL表面的新数据类型。
An SDL surface is just an image data type that contains the pixels of an image along with all data needed to render it.
SDL表面只是一种图像数据类型,它包含图像的像素和呈现它所需的所有数据。
SDL surfaces use software rendering which means it uses the CPU to render.
SDL表面使用软件渲染,这意味着它使用CPU来渲染。
It is possible to render hardware images but it’s a bit more difficult so we’re going to learn it the easy way first.
渲染硬件图像是可能的,但这有点难,所以我们要先学习简单的方法。
In future tutorials we’ll cover how to render GPU accelerated images.
在以后的教程中,我们将讨论如何渲染GPU加速图像。

The images we’re going to be dealing with here are the screen image (what you see inside of the window) and the image we’ll be loading from a file.
我们将要处理的图像是屏幕图像(你在窗口中看到的)和我们将要从一个文件钟加载的图像。

Notice that these are pointers to SDL surfaces.
注意,这些是指向SDL表面的指针。
The reason is that 1) we’ll be dynamically allocating memory to load images and 2) it’s better to reference an image by memory location.
原因是
1:我们将动态地分配内存来加载图像
2:最好是通过内存位置来引用一个图像。
Imagine you had a game with a brick wall that consisted of the same brick image being rendered multiple times (like Super Mario Bros).
想象一下,你玩的是一堵砖墙,它由同一块砖形的图像组成(就像超级马里奥兄弟一样)。
It’s wasteful to have dozens of copies of the image in memory when you can have one copy of the image and render it over and over again.
在内存中有几十个图像副本是很浪费的,您可以拥有一个图像副本并一次又一次地呈现它时。

Also, always remember to initialize your pointers. We set them to NULL immediately when declaring them.
另外,永远记得初始化指针。我们在声明时立即将它们设置为NULL。

bool init()
{
//Initialization flag
bool success = true;

//Initialize SDL
if( SDL_Init( SDL_INIT_VIDEO ) < 0 )
{
printf( "SDL could not initialize! SDL_Error: %s\n", SDL_GetError() );
success = false;
}
else
{
//Create window
gWindow = SDL_CreateWindow( "SDL Tutorial", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN );
if( gWindow == NULL )
{
printf( "Window could not be created! SDL_Error: %s\n", SDL_GetError() );
success = false;
}
else
{
//Get window surface
gScreenSurface = SDL_GetWindowSurface( gWindow );
}
}

return success;
}

As you can see here, we’ve taken the SDL initialization and the window creation code and put it in its own function.
正如您在这里看到的,我们已经使用了SDL初始化和窗口创建代码,并将其放入自己的函数中。
What’s new is that there’s a call to SDL_GetWindowSurface.
现在的新情况是,有一个东东叫做SDL_GetWindowSurface。

We want to show images inside of the window and in order to do that we need to get the image inside of the window.
我们想要在窗口中显示图像,为了做到这一点,我们需要在窗口中获取图像。
So we call SDL_GetWindowSurface to grab the surface contained by the window.
所以我们叫SDL_GetWindowSurface来抓取窗口所包含的表面。

bool loadMedia()
{
//Loading success flag
bool success = true;

//Load splash image
gHelloWorld = SDL_LoadBMP( "02_getting_an_image_on_the_screen/hello_world.bmp" );
if( gHelloWorld == NULL )
{
printf( "Unable to load image %s! SDL Error: %s\n", "02_getting_an_image_on_the_screen/hello_world.bmp", SDL_GetError() );
success = false;
}

return success;
}

In the load media function we load our image using SDL_LoadBMP.
在loadMedia函数中,我们使用SDL_LoadBMP加载图像。
SDL_LoadBMP takes in the path of a bmp file and returns the loaded surface.
SDL_LoadBMP接受bmp文件的路径并返回已加载的表面。
If the function returns NULL, that means it failed so we print to the console an error using SDL_GetError.
如果函数返回NULL,那就意味着它失败了,所以我们用SDL_GetError恐惧打印到控制台的错误。

An important thing to note is that this piece of code assumes you have a directory called “02_getting_an_image_on_the_screen” that contains an image named “hello_world.bmp” in your working directory.
需要注意的一点是,这段代码假设您有一个名为“02_getting_an_image_on_the_screen”的目录,其中包含一个名为“hello_world.bmp”的图像。在你的工作目录中。
The working directory is where your application thinks it is operating.
工作目录是您的应用程序认为它在运行的地方。
Typically, your working directory is the directory where your executable is at but some programs like Visual Studio change the working directory to where the vcxproj file is located.
通常您的工作目录是您的可执行文件所在的目录,但是一些程序,如Visual Studio,将工作目录更改为vcxproj文件所在的位置。
So if your application can’t find the image, make sure it is in the right place.
因此,如果你的应用程序无法找到图像,请确保它在正确的位置。

void close()
{
//Deallocate surface
SDL_FreeSurface( gHelloWorld );
gHelloWorld = NULL;

//Destroy window
SDL_DestroyWindow( gWindow );
gWindow = NULL;

//Quit SDL subsystems
SDL_Quit();
}

In our clean up code, we destroy the window and quit SDL like before but we also have to take care of the surface we loaded.
在我们的清理代码中,我们破坏了窗口,并像以前一样退出了SDL,但是我们也必须处理我们加载的表面。
We do this by freeing it with SDL_FreeSurface.
我们通过使用SDL_FreeSurface来实现这一点。
Don’t worry about the screen surface, SDL_DestroyWindow will take care of it.
不要担心屏幕的表面,SDL_DestroyWindow会处理毁坏窗口的。

Make sure to get into the habit of having your pointers point to NULL when they’re not pointing to anything.
一定要养成在没有指向任何东西时让指针指向空的习惯。

int main( int argc, char* args[] )
{
//Start up SDL and create window
if( !init() )
{
printf( "Failed to initialize!\n" );
}
else
{
//Load media
if( !loadMedia() )
{
printf( "Failed to load media!\n" );
}
else
{
//Apply the image
SDL_BlitSurface( gHelloWorld, NULL, gScreenSurface, NULL );

In our main function we initialize SDL and load the image.
在我们的主要函数中,我们初始化SDL并加载图像。
If that succeeded we blit the loaded surface onto the screen surface using SDL_BlitSurface.
如果成功了,我们使用SDL_BlitSurface将加载的表面投射到屏幕上。

What blitting does is take a source surface and stamps a copy of it onto the destination surface.
它的作用是获取一个源表面,并将其复制到目标表面。
The first argument of SDL_BlitSurface is the source image.
SDL_BlitSurface的第一个参数是源映像。
The third argument is the destination.
第三个参数是目的地。
We’ll worry about the 2nd and 4th arguments in future tutorials.
在以后的教程中,我们将讨论第2和第4个参数。

Now if this was the only code for drawing we had, we still wouldn’t see the image we loaded on the screen. There’s one more step.
如果这是我们画的唯一的代码,我们仍然看不到我们在屏幕上加载的图像。还有一个步骤。

            //Update the surface
SDL_UpdateWindowSurface( gWindow );

After drawing everything on the screen that we want to show for this frame we have to update the screen using SDL_UpdateWindowSurface.
在我们想要显示的屏幕上显示的所有内容之后,我们必须使用SDL_UpdateWindowSurface来更新屏幕。
See when you draw to the screen, you are not typically drawing to the image on the screen you see.
当你画到屏幕上时,你通常不会在你看到的屏幕上画出图像。
By default, most rendering systems out there are double buffered.
默认情况下,大多数系统呈现都是双缓冲的。
These two buffers are the front and back buffer.
这两个缓冲区是前面和后面的缓冲区。

When you make draw calls like SDL_BlitSurface, you render to the back buffer.
当你做像SDL_BlitSurface这样的绘制调用时,你会渲染到后缓冲区。
What you see on the screen is the front buffer.
你在屏幕上看到的是前缓冲。
The reason we do this is because most frames require drawing multiple objects to the screen.
我们这样做的原因是,大多数帧都需要将多个对象绘制到屏幕上。
If we only had a front buffer, we would be able to see the frame as things are being drawn to it which means we would see unfinished frames.
如果我们只有一个前缓冲,我们就能看到物体被吸引到它的框架这意味着我们会看到未完成的框架。
So what we do is draw everything to the back buffer first and once we’re done we swap the back and front buffer so now the user can see the finished frame.
所以我们要做的是先把所有的东西都画到后面的缓冲区中,一旦我们完成了,我们就交换了后退和前缓冲,这样用户就可以看到完成的帧了。

This also means that you don’t call SDL_UpdateWindowSurface after every blit, only after all the blits for the current frame are done.
这也意味着,在所有的blit之后,您都不会调用SDL_UpdateWindowSurface,只是在当前框架的所有工作结束之后才会这样做。

            //Wait two seconds
SDL_Delay( 2000 );
}
}

//Free resources and close SDL
close();

return 0;
}

Now that we’ve rendered everything to the window, we delay for two seconds so the window doesn’t just disappear.
现在我们已经把所有的东西都渲染到窗口了,我们延迟了两秒钟,这样窗口就不会消失了。
After the wait is done, we close out our program.
在等待完成之后,我们关闭了我们的程序。

Download the media and source code for this tutorial here.
请下载本教程的媒体和源代码。


好了,这就是Lesson 02,现在请看下文:

Lesson 02中说了以下几个重要的点(不管是做SDL程序或是其他任何程序中):

避免在大型程序中使用全局变量,在大型项目中,全局变量使事情变得更加复杂。
永远记得初始化指针。我们在声明时立即将它们设置为NULL。

这两句话其实挺好的,说的也没错。

还有,阅读本文时注意一点,其中有一个翻译叫做“SDL表面”,说实话,我都不知道该怎么翻译这个东西了,你可以看看中英对照版,这个单词最佳翻译就是表面了···

好吧,这一篇文章讲了以下几个重点:

SDL_GetWindowSurface是用来抓取窗口所包含的表面的。
loadMedia函数可以使用SDL_LoadBMP加载图像。
SDL_DestroyWindow会处理毁坏窗口的。
使用SDL_UpdateWindowSurface来更新屏幕。

具体如何使用这些请看:

SDL学习之路 2nd

在Lesson 02中有一个源代码下载,这里我给一下地址:

国外教程原地址(可能需要*):

http://lazyfoo.net/tutorials/SDL/02_getting_an_image_on_the_screen/02_getting_an_image_on_the_screen.zip

益文友_Allen(本博主)提供的下载地址(无需*):

http://download.csdn.net/detail/yiwenyoucod/9875373