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,保存为SDLHelloWorld,添加一个新的单元,保存为App.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;

Loading a Bitmap Image

To render a BMP image we’ll need to load it into memory and then onto the rendering platform we’re using (in this case the GPU). We can load the image with SDL_LoadBMP which gives us back a SDL_Surface* that we can then take and upload to a SDL_Texture that the renderer is able to use.

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;

With the image loaded we can now upload it to the renderer using SDL_CreateTextureFromSurface. We pass in the rendering context to upload to and the image in memory (the SDL_Surface) and get back the loaded texture, if something went wrong we’ll get back NULL. We’re also done with the original surface at this point so we’ll free it now.

  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;

Drawing the Texture All that’s left to do is get our texture on the screen! First we’ll clear the renderer, then render our texture and then present the updated screen to show the result. Since we want to render the whole image and have it stretch to fill the screen we’ll pass NULL as the source and destination rectangles for SDL_RenderCopy. We’ll also want to keep the window open for a bit so we can see the result before the program exits, so we’ll add in a call to SDL_Delay.

We’ll place all this rendering code within the main loop of our program, which for now will be a simple for loop. Each iteration through our loop we’ll sleep for a second, so we can increase or decrease the counter to make our program run for a longer or shorter period. When we get to event handling we’ll instead track a boolean that indicates if the user wants to quit our program (eg. clicked the X on the window) and exit our loop in that case.

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;

Cleaning Up

Before we exit we’ve got to destroy all the objects we created through the various SDL_DestroyX functions and quit SDL. Error handling note: previously in the program we may have encountered an error and exited early, in which case we’d have to destroy any SDL objects we had created and quit SDL to properly clean up before exiting. This part of the error handling is omitted from the lessons since they’re such small examples and it helps keep the code a bit shorter, but in a real world program proper error handling and clean up is absolutely required.

SDL_DestroyTexture(tex);
  SDL_DestroyRenderer(ren);
  SDL_DestroyWindow(win);
  SDL_Quit();

End of Lesson

If everything went well you should see the image you loaded render over the entire window, wait for 2s and then exit. If you have any problems, make sure you’ve got SDL installed and your project configured properly as discussed in Lesson 0: Setting up SDL, or post a question below.

完整代码如下:

SDLHelloWorld.dpr:

program SDLHelloWorld;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  sdl2 in '..\..\Pascal-SDL-2-Headers-master\sdl2.pas',
  App in 'App.pas';

begin
  Run;
end.

App.pas如下:

unit App;

interface

uses
  sdl2,
  res_path;

function Run: Integer;

implementation

function Run: 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.
Clone this wiki locally