原文地址:http://twinklebeardev.blogspot.com/2012/07/lesson-2-dont-put-everything-in-main.html
这一节中,我们将通过编写一些非常有用的函数来将上一节我们所写的代码组织起来,在这之中呢,我们会讨论一下图片是怎样在SDL window中确定位置和缩放的。
惯例,我们需要在程序开头包含SDL。这一节我们还需要string类,所以我们在开头也包含它。
我们还声明了一些表示窗口宽度和高度的常量,连同window还有renderer的全局变量,以便于所有的函数都能够访问到它们。这里有一次为了安全我们把指针初始化为nullptr.如果你没有使用C++11,那就把它们初始化为NULL吧。
注意:你应该尽可能地避免适用非常量的全局数或者全局变量,这即是说,你从来都不应该声明全局的SDL_Window和SDL_Renderer。尽管这样,鉴于这是一套非常简单的教程,我们还是不要纠结这个问题了。但是如果你觉得这样做恶心的话,这也没关系。在以后几节课程中,我们将提到一个解决方案。
还记得在第一节中我们加载了一个texture么。把加载图像的代码放到main里并不是太坏,但是如果我们需要加载很多图像呢?我们不得不每次都要写一遍那些代码!我们可以做得更好——定义一个可以通过文件名来加载texture的函数。
这里的这个函数看起来应该非常眼熟才对,因为它就是我们在第一节写的代码。但是这一次,我们把它们用一个美妙的函数包裹了起来。有了这个函数,我们就可以传给它一个文件名的字符串,然后得到一个SDL_Texture的指针。注意,如果图片加载失败了,这个指针会是nullptr,因为我们为了错误检测,把所有的指针都初始化为了nullptr。
下一步,我们想要写一个可以简化我们绘制调用,并且允许我们指定图像在屏幕上绘制的位置的函数。我们需要它能够取得x, y坐标位置还有一个texture指针,以及一个renderer指针,然后把那个texture画在那个位置。
为了指定Texture绘制的位置,我们需要创建一个SDL_Rect,这样我们就可以把它的地址传给SDL_RenderCopy的目标rect参数。这样做是因为SDL_RenderCopy的最后两个参数是SDL_Rect类型的指针,所以需要传入一个地址。
为了创建这个矩形(rectangle),我们将传入的x与y赋给rectangle的x和y.另外,我们还必须指定需要texture绘制的宽度和高度,因为SDL2.0在这里赋予了我们缩放texture的能力。在本教程结束后,你可以试着更改高度和宽度并看看会发生什么情况。
但是现在我们只想把texture原本的宽度和高度传入,以便于以1:1的比例来绘制它。我们可以通过SDL_QueryTexure来获取这些值。这个函数需要我们传入texture的指针,后面两个参数我们传入NULL,它们分别是texture格式(? 待考证函数说明)和访问级别,现在我们可以无视它们。最后我们将需要填texture宽高的变量的地址传入。
现在我们已经有了SDL_Rect,我们可以把它还有renderer,以及之前的texture传给SDL_RenderCopy ,这样texture就会以它原始的尺寸绘制在我们制定的位置。剩下的那个NULL的参数是为了裁剪原来的texture之用的,这点我们会在后面提及。
现在我们实际看一下我们的函数。首先我们和以前一样开启SDL,创建窗口还有renderer。这里还有个新东西,SDL_WINDOWPOS_CENTERED.这是个可以用来在创建窗口的时候告诉SDL把窗口设到指定坐标轴中央的选项,这里我们指定的是x和y。
现在我们加载图片。本节我们将绘制一个平铺的背景图还有位于它上面的一张居中的图。张是我们的背景图:
然后这张是前景图:
我们用刚才写的LoadImage 函数来加载它。
注意你也许需要更改文件路径以便与文件实际运行的路径匹配。
在绘制图片之前,我们需要知道我们要把它们放在那里,特别是我们应该如何平铺背图,还有如何把前景图绘制在屏幕中央。首先,我们必须弄明白SDL的坐标系统是怎样工作的。SDL的坐标系统看起来是这样的:
坐标0,0处从屏幕左上角开始。Y坐标从上到下增加,X坐标从左到右增加。另外还有一个需要注意的问题,SDL的坐标系统是,指定的图片绘制的x,y坐标,将以这个坐标为图片左上角的坐标来绘制——而不是像其他一些库一样把这个坐标当作图片的中心。
在绘制之前,还有一个需要注意的问题:SDL的绘制顺序。我们绘制时的顺序就是图像叠加的顺序,所以我们首先绘制的东西将位于最底层,最后绘制的将位于所有图像的最上层。
如果你看了那张背景图,你会发现这张图长320宽240,如果是640x480的屏幕的话,我们需要把它画四次以覆盖整个窗口,每次都要依据图片长宽来移动图片。
在绘制之前,我们需要清屏,然后创建好需要画到窗口中的元素。我们可以通过QueryTexture函数来获取背景图的长和宽,它为我们提供了相较之用一个for循环来迭代图像的长宽的一个更便捷的方法,如果我们需要绘制很多小的tile(方块?就是2D游戏中常见的一张图储存了很多种方块,方块的不同排列组成场景的不同部分。。。的那种方块。= =||)在本例中我们一共只需要画四次,所以我们用笨方法直接敲四次代码好了。
现在,我们想要把前景图画在背景图上方并在窗口中居中显示。我们可以很容易地计算出中间点的坐标,但是因为传入ApplySurface函数的图片坐标应该在图片左上角,我们必须给这个点应用一个基于原图像宽度和高度的偏移,以把图片正确地放到屏幕中央。
为了看到我们绘制的结果,我们需要把renderer呈现(present)出来,并且让SDL等待一两秒钟以便于我们能看到显示的图像。
最后,我们为了圆满地结束这个程序,需要释放掉texture、renderer还有window所占用的内存,退出SDL并返回。
当你编译并运行了这个程序,你的窗口看起来应该这样的:
第二节的Extra Challenge!
找到一个把我们之前平铺背景的笨方法改成一个聪明一点的for循环的方法吧!也许对四个背景图块来说,这样做并不是特别有效率,但是如果图块数量很多的话,使用for循环就很有必要了。
提示:
试想一下,当我们一行一行平铺的时候,y坐标是怎样增加的,当一列一列平铺的时候,x坐标是怎样增加的。
End of Lesson 2: Don’t Put Everything in Main If you have trouble compiling or running the program make sure you’ve set up the includes, include directories, linker settings and linker directories correctly, along with setting the correct path to the images and placing the SDL.dll in your executable folder. For Linux users there is no SDL.dll but instead make sure you have the runtime libraries in the correct place in your system.
I’ll see you again soon in Lesson 3: SDL Extension Libraries!