Wednesday, December 09, 2009

SDL & Object Pascal (Delphi) [3] 制作一个选单

一个选单大致是这样:

1.可以被以某种形式呼叫出来,如按下esc;
2.在这个选单中功能键有不同的定义,如原本方向键控制走路,但现在改为控制光标的位置;
3.按下确定键后有对应功能被执行;
4.可以被关闭。

那么我的办法是编写两个子程,其中一个处理选单中的事件,另外一个专门负责画选单。

在从步行切换到选单的时候,可能需要清除当前的键值。如果步行中使用了SDL_EnableKeyRepeat方法把键盘的频率变高,对于选单来说就太快了。所以可能要在调用选单之前写上:

SDL_EnableKeyRepeat(0, 0);
event.key.keysym.sym := 0;

这样降低了键盘的反应,同时清除当前的键值,要不然呼出选单之后它可能还会自己转一会(如果之前用了PollEvent方式,我还没搞清楚原因)。不过如果在行走中使用的是WaitEvent方式,事情就会简单很多。PollEvent方式看来是太快了,在选单中不合适。

处理选单事件的子程:

procedure MenuSystem;
var
  menu: integer;
begin
  while (SDL_WaitEvent(@event) >= 0) do
  begin
    case event.type_ of
      SDL_QUITEV: //这里处理窗口退出事件
        if messagebox(0, 'Are you sure to quit?', 'KYS Windows', MB_OKCANCEL) = IDOK then Quit;
      SDL_KEYUP:
        begin
          if (event.key.keysym.sym = sdlk_down) then
          begin
            menu := menu+1;
            if menu > 3 then menu := 0; //按下下键的溢出
            showMenusystem(menu); //每次当前选中发生变化时, 均重画选单
          end;
          if (event.key.keysym.sym = sdlk_up) then
          begin
            menu := menu-1;
            if menu < 0 then menu := 3; //按下上键的溢出
            showMenusystem(menu);
          end;
          if (event.key.keysym.sym = sdlk_escape) then
          begin
            break; //按下退出键
          end;
          if (event.key.keysym.sym = sdlk_return) or (event.key.keysym.sym = sdlk_space) then
          begin //按下确定键
            case menu of
              2: MenuQuit;
              1: MenuSave;
              0: Menuload;
            end;
          end;
        end;
    end;
  end;
end;

显示选单的子程,里面有一些其他的东西,不必太在意:

procedure ShowMenuSystem(menu: integer);
var
  word: array[0..2] of Widestring;
  i: integer;
begin
  Word[0] := ' 读取';
  Word[1] := ' 存档';
  Word[2] := ' 退出';
  if fullscreen = 1 then Word[2] := ' 窗口';
  ReDraw; //你需要自己写一个清屏子程
  for i := 0 to 3 do
    if i = menu then
    begin
      drawtext(screen, @word[i][1], 64, 32+22*i, $FFFFFF)); //当前的项显示为不同的颜色
    end
    else begin
      drawtext(screen, @word[i][1], 64, 32+22*i, $00FFFF);
    end;
  SDL_UpdateRect(screen, 80, 30, 47, 93); //根据区域而定,如果觉得麻烦就更新全屏
end;

No comments: