Wednesday, December 09, 2009

SDL & Object Pascal (Delphi) [1] 配置,第一个视频窗口,关闭

在Delphi下面配置SDL是非常简单的,因为有一个组织(JEDI)连安装程序都做好了。

你可以到这个网站http://www.delphi-jedi.org/,下载一个JEDI-SDL的安装程序。文件的名字是[JEDI-SDLFullSetup.exe],安装就是了。当然你事先要有一个Delphi,我用的是7,其他版本的我还真不清楚。

不过要想你的游戏能正常运行,你还要去下载SDL的DLL文件,这个建议你到SDL的主页去看看。如果需要image,ttf,mixer等其他的支持,可以到这里http://www.libsdl.org/projects/。之后你要把你用到的部分放到你的游戏目录里面。当然嫌麻烦的话放到系统目录里面应该也可以,不过据说现在很多人不喜欢往系统目录里面放东西。

这些都做好了你就可以试着编写一个使用SDL的Delphi程序了。我第一次做的时候是选了一个新建控制台程序,这样打开游戏的时候会有两个窗口。这倒不是问题,至少你可以随时把

{$APPTYPE CONSOLE}

这一行注释掉取消那个控制台窗口,但是我建议你还是暂时留着。至少控制台窗口的关闭按钮是随时都有效的,而SDL的主窗口的关闭按钮却不一定随时能用,在编写和调试阶段还是很方便的。

这时你有了一个dpr文件,如果把所有的函数与子程全写到这里当然不成问题,但是我认为更好的办法是把子程全写到另一个pas文件里,再在dpr文件的uses部分添加这个pas文件。因为在dpr文件里面我还没搞清楚能不能用函数预声明,如果不能的话就只能引用前面已经写好的部分,这无论如何不是个好的选择。而pas文件里面随意性就大得多。

这样你的dpr文件的内容大致应是这样(这里的语法加亮不行,凑合着看吧):

program MyGame

{$APPTYPE CONSOLE}

uses

  SysUtils,

  windows,

  Dialogs,

  SDL,

  myfunctions in 'myfunctions.pas';

begin

  Run;

end.

而myfunctions.pas文件里面应有这样的内容:

unit myfunctions;

interface

uses
  sysutils,
  windows,
  sdl;

procedure Run;

implementation

var
  screen: PSDL_Surface;

procedure Run;
begin
  SDL_Init(SDL_INIT_VIDEO);
  screen:=SDL_SetVideoMode(640,480,32,SDL_HWSURFACE or SDL_DOUBLEBUF);
  while true do
  ;

SDL_Quit;      
  exit;

end;

end.

uses里面加上Windows和Sysutils显然是必要的,因为我们可能还需要Windows的API。调试中如果想用showmessage显示一些结果的话还需要Dialogs,它比MessageBox还是方便很多。

全局变量里有一个screen,用它作为最主要的画图板,其余的画图板可以在使用的时候由子程定义。至于类型译成“SDL表面指针”看来也并不合适,不过没关系,知道它是做什么的就可以了。

SDL_Init(SDL_INIT_VIDEO)的作用是初始化视频系统,SDL_INIT_VIDEO其实是一个定义好的常数。一般这样用大致是可以,但是一个较为正式的用法应是这样:

if (SDL_Init(SDL_INIT_VIDEO)<0) then  
  begin      
    MessageBox(0, PChar(Format('Couldn''t initialize SDL : %s',[SDL_GetError])), 'Error', MB_OK or MB_ICONHAND);
    SDL_Quit;      
    exit;  
  end;

就是尝试初始化视频系统,一旦失败用MessageBox输出错误消息(SDL_GetError),并退出程序。不仅是视频,其他的许多比如初始化音频时都应该添加类似的判断。我在这里略去了这部分是为了让代码看来清晰一点,但是你如果真的动手编写一个游戏的时候,判断一下无疑还是必要的。

而在初始化视频之后,还需要一个窗口显示游戏画面,就是

screen:=SDL_SetVideoMode(640,480,32,SDL_HWSURFACE or SDL_DOUBLEBUF);

这一行的作用了,前面3个数字是屏幕的分辨率和色深,后面就是初始化的一些选项了。这里用的两个选项是在显存中生成画面并使用双缓冲,可能是要求比较高的一种,其实用 SDL_ANYFORMAT 和 SDL_SWSURFACE 也许就能满足多数情况。这里就不再一一解释这些选项的含义了,可以查看SDL的说明文件。

这样就获得了一个640*480大小的窗口,并用screen标记它。

再往后是一个死循环,这只是为了让这个窗口保持住,否则再执行下去,SDL_Quit 加上 exit 谁都知道代表什么含义。当然我们并非是真的需要一个死循环,一个游戏所需的循环应是这样:

1 如果接收到指令
2 执行指令
3 返回1

在执行指令这里应有:

如果指令为“退出”,则退出循环。

把原来的死循环这样改写,并在全局变量里面添加一个event:

event: TSDL_event;
  ……

while SDL_PollEvent(@event)>=0 do
  begin
    if event.type_=SDL_QUITEV then break;
  end;

event是定义用来接收游戏的消息(事件)的,任何游戏都应该有这样的一个变量(而且似乎只能作为全局变量或者在最初的子程序中定义,这一点我还并没确定)。而PollEvent就是用于查询的,@在Delphi中是取地址算符,因为SDL_PollEvent的参数是指针类型。

通常可以用两种方式进行查询:

SDL_PollEvent:直接查询。返回值为0表示未查询到事件,大于0表示有事件。无论查询结果如何,程序都会继续执行。

SDL_WaitEvent:等待查询。与上面的区别是如果未查询到事件,会使游戏的流程停在此句。

何时使用这两种方式需根据情况决定。如果你觉得某些情况只需处理按键(比如在选单内部),那么Wait方式会好一些;而如果在没有事件的时候你需要一些自动效果,Poll方式也许更适合。

循环中的判断就是:一旦发生关闭窗口事件(SDL_QUITEV,注意在说明文档里面写成了SDL_QUIT,这与SDL中用于退出的重要函数同名,显然是写错了),则退出循环。

这个程序在执行时可能会占用CPU过大,这时可以在每次循环时让CPU休息一下。在while内
部加上一个:

Sdl_delay(10);

即可大大降低CPU的占用,这可能会带来10毫秒的延时,但不会真的有人在乎吧!

No comments: