Skip to content

SDLHelloWord

Zhao Yipeng edited this page Dec 6, 2017 · 11 revisions

SDL HelloWorld!

英文原文地址:http://www.willusher.io/sdl2%20tutorials/2013/08/17/lesson-1-hello-world

这一节我们将学习简单的将一张图片绘制到屏幕上的方法。具体来讲是绘制下面这张图片。

hello

你可以通过右键另存为下载这张图片。如果你丢了资源或者想要偷看一下我的代码,就从这里抓好了。但切忌复制粘贴!

创建一个SDL Project

首先在delphi当中File->New->Other,创建一个Console Application,保存为Lesson1,添加一个新的单元,保存为uMain.pas。

启动SDL

使用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.