-
Notifications
You must be signed in to change notification settings - Fork 109
SDLHelloWord
英文原文地址:http://www.willusher.io/sdl2%20tutorials/2013/08/17/lesson-1-hello-world
这一节我们将学习简单的将一张图片绘制到屏幕上的方法。具体来讲是绘制下面这张图片。
你可以通过右键另存为下载这张图片。如果你丢了资源或者想要偷看一下我的代码,就从这里抓好了。但切忌复制粘贴!
首先在delphi当中File->New->Other,创建一个Console Application,保存为Lesson1,添加一个新的单元,保存为uMain.pas。
使用SDL,我们必须先初始化我们想要使用的SDL的各个子系统。将一组“或”关系的标志作为参数传入到SDL_Init可以完成我们要使用的子系统的初始化。现在我们只需要使用视频子系统,但是将来我们会增加更多的标志,因为我们需要更多的特性。注意使用视频子系统的时候,事件处理子系统会被自动初始化,文件I/O和线程系统也会被默认启动。如果所有的初始化都正确SDL_Init会返回0,如果不是我们将会打印错误并退出。
if (SDL_Init(SDL_INIT_VIDEO) <> 0) then
begin
Writeln('SDL_Init Error: ', SDL_GetError);
Exit(1);
end;
我们需要一个窗口来显示我们的渲染,我们可以使用SDL_CreateWindow创建一个窗口,参数包括窗口标题,X和Y坐标,窗口的宽度和高度以及一些标志来设置窗口的属性,返回一个PSDL_Window指针。如果创建窗口时出错,该指针将为nil。如果发生错误,我们需要在退出程序之前清理SDL。
win := SDL_CreateWindow('Hello World!', 100, 100, 640, 480, SDL_WINDOW_SHOWN);
// Make sure creating our window went ok
if (win = nil) then
begin
Writeln('SDL_CreateWindow Error: ', SDL_GetError);
Exit(1)
end;
现在我们可以创建一个渲染器并使用SDL_CreateRenderer在窗口中进行绘制。这个函数将渲染器与窗口进行关联,需要传入渲染器的驱动索引(或-1选择第一个符合需求的驱动)和渲染器所需的各种标志。在这里,我们请求带有垂直同步硬件加速的渲染器。我们得到一个PSDL_Renderer指针,如果该指针为nil则表示发生了错误。如果发生错误,我们需要清理我们以前创建的任何东西,并在退出程序之前退出SDL。
ren := SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED or SDL_RENDERER_PRESENTVSYNC);
if (ren = nil) then
begin
SDL_DestroyWindow(win);
Writeln('SDL_CreateRenderer Error: ', SDL_GetError);
SDL_Quit();
Exit(1);
end;
要渲染BMP图像,我们需要将其加载到内存中,然后加载到我们正在使用的渲染平台(在这种情况下是GPU)。我们可以使用SDL_LoadBMP加载图像,该函数返回PSDL_Surface指针,我们可以上传到一个SDL_Texture供渲染器使用。
SDL_LoadBMP需要传入一个filepath参数指定图像的路径,你应该按照你的应用目录结构进行相应的调整,该函数返回一个PSDL_Surface指针,如果发生错误则返回nil。
SDL_LoadBMP takes the filepath of our image, which you should change to match your project structure, and gives us back an SDL_Surface* or NULL if something went wrong.
imagePath := getResourcePath('Lesson1') + 'hello.bmp';
bmp := SDL_LoadBMP(PAnsiChar(AnsiString(imagePath)));
if (bmp = nil) then
begin
SDL_DestroyRenderer(ren);
SDL_DestroyWindow(win);
Writeln('SDL_LoadBMP Error: ', SDL_GetError);
SDL_Quit();
Exit(1);
end;
图像加载后可以通过SDL_CreateTextureFromSurface上传到渲染器使用。我们传入要上传的渲染器上下文和内存中的图像(PSDL_Surface指针)并得到加载的纹理,如果发生错误,我们会得到nil。我们在这里已经得到了窗口和渲染器,所以现在需要释放它们。
tex := SDL_CreateTextureFromSurface(ren, bmp);
SDL_FreeSurface(bmp);
if (tex = nil) then
begin
SDL_DestroyRenderer(ren);
SDL_DestroyWindow(win);
Writeln('SDL_CreateTextureFromSurface Error: ', SDL_GetError);
SDL_Quit();
Exit(1);
end;
剩下要做的就是让我们的纹理显示在屏幕上!首先我们要清除渲染器,然后渲染我们的纹理并更新屏幕显示结果。因为我们想图片进行拉伸铺满整个屏幕,我已我们将nil作为源和目标矩形传给SDL_RenderCopy。我们希望窗口在程序推出之前保留一会,以便我方看到显示结果,所以我们添加一个SDL_Delay调用。
把所有这些渲染代码放在程序的主循环中,使用一个简单的for循环。每次循环我们会休眠一秒钟,增加或减少计数器,可以使程序运行更长或更短的时间。当我们讲到事件处理时,我们将跟踪一个布尔值,确定用户是否要退出程序(如单击窗口上的X),并在这种情况下退出循环。
for i := 0 to 2 do
begin
// First clear the renderer
SDL_RenderClear(ren);
// Draw the texture
SDL_RenderCopy(ren, tex, nil, nil);
// Update the screen
SDL_RenderPresent(ren);
// Take a quick break after all that hard work
SDL_Delay(1000);
end;
在退出之前我们要通过各种SDL_DestroyX函数销毁所有创建的对象并且退出SDL。错误处理说明:在之前的程序中,我们可能遇到了一个错误并提前退出,在这种情况下,我们必须销毁我们创建的任何SDL对象,并退出SDL以在退出之前正确地清理。错误处理的这一部分从课程中省略了,因为它们是如此小的示例,它有助于使代码缩短一些,但在实际使用的程序中,适当的错误处理和清理是绝对必要的。
SDL_DestroyTexture(tex);
SDL_DestroyRenderer(ren);
SDL_DestroyWindow(win);
SDL_Quit();
如果一切顺利的话,你应该看到你加载的图像渲染整个窗口,等待2秒,然后退出。如果您有任何问题,请确保按照第0课:SDL配置,安装了SDL,或者在下面留下您的问题。
完整代码如下:
SDLHelloWorld.dpr:
program Lesson1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils,
sdl2 in '..\..\Pascal-SDL-2-Headers-master\sdl2.pas',
uMain in 'uMain.pas',
res_path in '..\include\res_path.pas';
begin
Main;
end.
App.pas如下:
unit uMain;
interface
uses
sdl2,
res_path;
function Main: Integer;
implementation
function Main: Integer;
var
win: PSDL_Window;
ren: PSDL_Renderer;
bmp: PSDL_Surface;
tex: PSDL_Texture;
imagePath: string;
i: Integer;
begin
// First we need to start up SDL, and make sure it went ok
if (SDL_Init(SDL_INIT_VIDEO) <> 0) then
begin
Writeln('SDL_Init Error: ', SDL_GetError);
Exit(1);
end;
// Now create a window with title "Hello World" at 100, 100 on the screen with w:640 h:480 and show it
win := SDL_CreateWindow('Hello World!', 100, 100, 640, 480, SDL_WINDOW_SHOWN);
// Make sure creating our window went ok
if (win = nil) then
begin
Writeln('SDL_CreateWindow Error: ', SDL_GetError);
Exit(1)
end;
// Create a renderer that will draw to the window, -1 specifies that we want to load whichever
// video driver supports the flags we're passing
// Flags: SDL_RENDERER_ACCELERATED: We want to use hardware accelerated rendering
// SDL_RENDERER_PRESENTVSYNC: We want the renderer's present function (update screen) to be
// synchronized with the monitor's refresh rate
ren := SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED or SDL_RENDERER_PRESENTVSYNC);
if (ren = nil) then
begin
SDL_DestroyWindow(win);
Writeln('SDL_CreateRenderer Error: ', SDL_GetError);
SDL_Quit();
Exit(1);
end;
// SDL 2.0 now uses textures to draw things but SDL_LoadBMP returns a surface
// this lets us choose when to upload or remove textures from the GPU
imagePath := getResourcePath('Lesson1') + 'hello.bmp';
bmp := SDL_LoadBMP(PAnsiChar(AnsiString(imagePath)));
if (bmp = nil) then
begin
SDL_DestroyRenderer(ren);
SDL_DestroyWindow(win);
Writeln('SDL_LoadBMP Error: ', SDL_GetError);
SDL_Quit();
Exit(1);
end;
// To use a hardware accelerated texture for rendering we can create one from
// the surface we loaded
tex := SDL_CreateTextureFromSurface(ren, bmp);
// We no longer need the surface
SDL_FreeSurface(bmp);
if (tex = nil) then
begin
SDL_DestroyRenderer(ren);
SDL_DestroyWindow(win);
Writeln('SDL_CreateTextureFromSurface Error: ', SDL_GetError);
SDL_Quit();
Exit(1);
end;
// A sleepy rendering loop, wait for 3 seconds and render and present the screen each time
for i := 0 to 2 do
begin
// First clear the renderer
SDL_RenderClear(ren);
// Draw the texture
SDL_RenderCopy(ren, tex, nil, nil);
// Update the screen
SDL_RenderPresent(ren);
// Take a quick break after all that hard work
SDL_Delay(1000);
end;
// Clean up our objects and quit
SDL_DestroyTexture(tex);
SDL_DestroyRenderer(ren);
SDL_DestroyWindow(win);
SDL_Quit();
end;
end.