SDL_main没了!Well, okay, there is, and it now does what it was always meant to: be a small piece of code that hides the difference between main() and WinMain() on Windows. There's no initialization code in it, and it's completely optional. This means you can use SDL without it taking over your mainline, which is nice for plugins that use SDL, or scripting languages with an SDL module. All the stuff you'd want the 1.2 SDL_main for is now in SDL_Init() where it belongs.
There's no SDL parachute anymore. What 1.2 called SDL_INIT_NOPARACHUTE is a default and only state now. This would cause problems if something other than the main thread crashed, and it would interfere with apps setting up their own signal/exception handlers. On the downside, some platforms don't clean up fullscreen video well when crashing. You should install your own crash handler, or call SDL_Quit() in an atexit() function or whatnot if this is a concern. Note that on Unix platforms, SDL still catches SIGINT and maps it to an SDL_QUIT event.
SDL_Window *screen = SDL_CreateWindow("My Game Window",
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
640, 480,
SDL_WINDOW_FULLSCREEN | SDL_WINDOW_OPENGL);
复制代码
对比改动,改动后代码和1.2还是很像的,不同的是你想创建多少个窗口(SDL_Window)不受限了。为让每个窗口有自已标题,SDL_WM_SetCaption也不能要了,取而代之是使用SDL_SetWindowTitle去修改各窗口标题,除了标题,你还要为每个窗口指定位置(不关心放哪儿可用SDL_WINDOWNPOS_UNDEFINED,固定放中央的话可用SDL_WINDOWPOS_CENTERED)。
SDL2提供一个新功能:你可为每窗口挂接各自显示器。SDL2提供了管理多显示器功能。
为平滑显示,不论是SDL1.2还是SDL2.0,它们都使用双缓冲机制,准备好的窗口是先放在后台,需要时再调到前台去显示。SDL2依旧存在图面(SDL_Surface),但正如你可能猜到的,SDL2加了它的替代品,是的,这替代品叫纹理(SDL_Texture)。在2.0,图面将总是创建在系统内存,它的操作一定要通过CPU,为整系统效率考虑,程序应该尽可能使用GPU。SDL2 has a new rendering API. It's meant for use by simple 2D games, but most notably, it's meant to get all that software rendering into video RAM and onto the GPU. And even if you just want to use it to get your software renderer's work to the screen, it brings some very nice benefits: if possible, it will use OpenGL or Direct3D behind the scenes, which means you'll get faster blits, a working Steam Overlay, and scaling for free.
The setup looks like this.
之前有说到SDL_CreateWindow()要代替SDL_SetVideoMode(),那要如何处理分辨率。如果你的程序硬编码为640x480,但使用的显示器不是这分辨率,那就不能全屏只能是窗口模式,游戏看起来更像是贴邮票。为让强制全屏SDL2下提供了种更好办法。
SDL_ListModes已没了,作为它的替代实现,SDL2建议是使用一个循环,调用SDL_GetNumDisplayModes()次SDL_GetDisplayMode。要把640x480强制为全屏可使用新方法:全屏桌面。它告诉SDL“把整个窗口让我显示,而且不要改变我希望的分辨率”,对假设的640x480游戏,代码看去就是以下样子。
SDL_Window *sdlWindow = SDL_CreateWindow(title,
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
0, 0,
SDL_WINDOW_FULLSCREEN_DESKTOP);
复制代码
Notice how we didn't specify 640 or 480...fullscreen desktop gives you the whole display and ignores any dimensions you specify. The game window should come up immediately, without waiting for the monitor to click into a new resolution, and we'll be using the GPU to scale to the desktop size, which tends to be faster and cleaner-looking than if an LCD is faking a lower resolution. Added bonus: none of your background windows are resizing themselves right now.
渲染上下文隐藏了SDL是如何把窗口画到屏幕的具体细节。根据系统不同,SDL会使用下面方式的一种,Direct3D、OpenGL、OpenGL ES或软件模拟。选择哪种由SDL内部决定,你无法修改,regardless of what SDL chooses (although you are welcome to force one kind of renderer or another). If you want to attempt to force sync-to-vblank to reduce tearing, you can use SDL_RENDERER_PRESENTVSYNC instead of zero for the third parameter. You shouldn't create a window with the SDL_WINDOW_OPENGL flag here. If SDL_CreateRenderer() decides it wants to use OpenGL, it'll update the window appropriately for you.
One more general thing to set up here. Since we're using SDL_WINDOW_FULLSCREEN_DESKTOP, we don't actually know how much screen we've got to draw to. Fortunately, we don't have to know. One of the nice things about 1.2 is that you could say "I want a 640x480 window and I don't care how you get it done," even if getting it done meant centering the window in a larger resolution on behalf of your application.
For 2.0, the render API lets you do this...
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear"); // make the scaled rendering look smoother.
SDL_RenderSetLogicalSize(sdlRenderer, 640, 480);
复制代码
...and it will do the right thing for you. This is nice in that you can change the logical rendering size to achieve various effects, but the primary use is this: instead of trying to make the system work with your rendering size, we can now make your rendering size work with the system. On my 1920x1200 monitor, this app thinks it's talking to a 640x480 resolution now, but SDL is using the GPU to scale it up to use all those pixels. Note that 640x480 and 1920x1200 aren't the same aspect ratio: SDL takes care of that, too, scaling as much as possible and letterboxing the difference.
介绍了基础性的,接下让进入具体升级。
在逻辑上,你按通常方法生成图面,并处理图面,最后用这函数由图面生成纹理。一旦由图面生成了纹理,你就可释放图面占用的资源了。
在这种情形中,SDL 1.2是创建若干图面,然后把它们按要求一次又一次混合向主图面,最后由SDL_Flip()调到前台。到SDL 2.0后,要改为创建若干纹理,然后按要求用SDL_RenderCopy一次又一次混合向后备帧,最后用SDL_RenderPresennt()把后备帧调到前台,从而显示到屏幕。It's that simple. If these textures never need modification, you might find your framerate has just gone through the roof, too.
注意,创建纹理会较耗CPU,而且因为它是位在显存,往往也不能创建得太多,因而不要每次渲染时都调用一次SDL_CreateTextureFromSurface。只须创建一次纹理、图面,接下你只要复用它们就行了。
渲染API还有不少新加内容,它们有可能代替你现在代码,像缩放、画线、等等。If you are reading this section because you have simple needs beyond blitting surfaces, you might be able to stop poking individual pixels and move everything onto the GPU, which will give your program a significant speed boost and probably simplify your code greatly
2.3 OpenGL
If you were already using OpenGL directly, your migration is pretty simple. Change your SDL_SetVideoMode() call to SDL_CreateWindow() followed by SDL_GL_CreateContext(), and your SDL_GL_SwapBuffers() call to SDL_GL_SwapWindow(window). All the actual calls into the GL are exactly the same.
If you had used SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, x), this has changed. There is now an SDL_GL_SetSwapInterval(x) call, so you can change this on an existing GL context.
Note that SDL 2.0 can toggle windowed/fullscreen and back with OpenGL windows without losing the GL context (hooray!). Use SDL_SetWindowFullscreen() for this.
2.4 输入
好消息是SDL 2.0已支持Unicode输入,坏消息是你得对代码做些改动。
在SDL之前版本,为接收ASCII外字符,程序需调用SDL_EnableUNICODE(1)。即使这么做了,也不能很好处理字符输入。
事实证明,在那版本要做到国际化非常困难。
SDL 2.0对此做了重大改动,SDL_EnableUNICODE没有了,同时去掉的还有SDL_Keysym中的unicode字段。从SDL_KEYDOWN你有可能不能准确知道按下的是什么字符,更好的处理方式是把SDL_KEYDOWN视作101个按钮的游戏杆,得用其它方法去获知按下什么键。
新增SDL_TEXTIPUT事件。一旦玩家输入字符,就会触发该事件。输入或是来自一次按键输入,或是来自IME(由多次按键组合而成的输入,像通过中文输入法)。SDL_TEXTINPUT返回的是最后输出,这意味着在IME输入时,通过它得到的是中文输入法输出的最后形成的汉字(串)。另外,SDL_TEXTINPUT返回的字符都是UTF-8格式。
SDL_KEYDOWN可处理一次性按键输入,在这种情况下,SDK_KEYDOWN会形成两种码:键盘码、扫描码。
扫描码是和键盘布局无关。
键盘码和特定键盘布局相关。
举个例子,你在US QWERTY键盘按下CAPS Lock和S键,SDK_KEYDOWN会报出扫描码SDL_SCANCODE_S、键盘码SDLK_S。或改在Dvorak键盘按下CAPS Lock和S键,这时改为报出扫描码SDL_SCANCODE_S、键盘码SDLK_O。(译者注:为简单,可理解为SDL把US QWERTY认为是标准键盘,扫描码就是根据这键盘得出的。)
不论是键盘码还是扫描码,它们都是32位表示,这意味可表示2^32的范围值。There's no SDLK_LAST anymore. If your program had a lookup table of SDLK_LAST elements, to map between SDL keys and whatever your application wanted internally, that's no longer feasible. Use a hash table instead. A std::map will do. If you're mapping scancodes instead of keycodes, there's SDL_NUM_SCANCODES, which you can use for array bounds. It's 512 at the moment.
SDL_Keymode代替了SDLMode,“META”键(Windows键)已改叫“GUI”键。
SDL_GetKeyState改名叫SDL_GetKeyboardState,它返回以扫描码(SDL_SCANCODE_*)为索引的状态数组。
现在说说鼠标输入。
第一个改变,差不也就是全部了,滚动鼠标不再归入按下事件(SDL_MOUSEBUTTONDOWN)。把滚动归入按下是个历史遗留错误,SDL 2.0把它修正了。SDL 2.0新加一事件:SDL_MOUSEWHEEL,用它表示鼠标滚动,滚动包括水平、垂直两方向,在一些平台,还把两手指滚动也归入SDL_MOUSEWHEEL。玩家滚动鼠标时,程序将不会再收到SDL_MOUSEBUTTONDOWN。SDL 1.2为表示滚动用了“按钮值是4(水平)、5(垂直)的SDL_MOUSEBUTTONDOWN”,2.0开始这4、5也和1(左键)、2(中键)、3(右键)一样表示真实按钮。
If your game needed to roll the mouse in one direction forever, for example to let a player in an FPS to spin around without the mouse hitting the edge of the screen and stopping, you probably hid the mouse cursor and grabbed input:
SDL_ShowCursor(0);
SDL_WM_GrabInput(SDL_GRAB_ON);
复制代码
In SDL2, this works slightly differently. You call...