プログラミング

oyagame 0.0.3.0α

2008年5月10日

Pygame入門を参考にしながら、どんなことができればSDLがちゃんと使えたことになるか確認しながら開発を行っている。前回の0.0.2.3αでは、ウィンドウが表示できることが分かった。次は、このウィンドウに画像を表示させるというところだろう。

set SDL=CreateObject("sfcmini.oyagame")
execute SDL.ConstVBS
SDL.Init(SDL_INIT_EVERYTHING)
set surface=SDL.SetVideoMode(200,100,32,SDL_RESIZABLE+SDL_HWSURFACE+SDL_DOUBLEBUF)
call SDL.WM_SetCaption("Hello oyagame",0)
set temp=SDL.CreateStructure("SDL_Surface",SDL.LoadBMP_RW(SDL.RWFromFile("block.bmp", "rb"), 1))
set image=SDL.ConvertSurface ( temp, surface.format, SDL_SWSURFACE )
SDL.FreeSurface(temp)
call SDL.UpperBlit(image,image.clip_rect,surface,surface.clip_rect)
SDL.Flip(surface)
set ev=SDL.CreateStructure("SDL_Event")
do
  SDL.WaitEvent(ev)
  select case ev.type
    case SDL_KEYDOWN
      if ev.key_keysym_sym=SDLK_ESCAPE then exit do
  end select
loop until ev.type=SDL_QUIT
WScript.Quit

実行結果:
oyagame


まずはこのコードの2行目、『execute SDL.ConstVBS』で、SDL_DOUBLEBUFなどの定数が利用できるようになったことがポイント。JScriptでは、『eval(SDL.COnstJS);』と記述すればよい。ただ、5行目がひどい。SDL_LoadBMPがまだ実装できていなくて、その理由は、SDL_LoadBMPがSDL.dllからエクスポートされている関数ではなくて、マクロで記述されているものであるからである。上記のコードは、そのマクロ定義に従って書いているもの。SDL.UpperBlitの部分も同じで、マクロ定義に関連するところ。これらはちょっとひどいので、後でなんとかしなければならない。

マクロ関連は後で直すことにして、とりあえず画像を動かすことができるかどうかを調べたのが、次のコード。このコードをテストするに当たって、タイマーをサポートするようにしている。

set SDL=CreateObject("sfcmini.oyagame")
execute SDL.ConstVBS
SDL.Init(SDL_INIT_EVERYTHING)
set surface=SDL.SetVideoMode(200,100,32,SDL_RESIZABLE+SDL_HWSURFACE+SDL_DOUBLEBUF)
call SDL.WM_SetCaption("Hello oyagame",0)
set temp=SDL.CreateStructure("SDL_Surface",SDL.LoadBMP_RW(SDL.RWFromFile("block.bmp", "rb"), 1))
set image=SDL.ConvertSurface ( temp, surface.format, SDL_HWSURFACE )
SDL.FreeSurface(temp)
call SDL.AddTimer(60,SDL.NewTimerCallBack,0)
set ev=SDL.CreateStructure("SDL_Event")
do
  call SDL.FillRect(surface, surface.clip_rect, 0)
  call SDL.UpperBlit(image,0,surface,image.clip_rect)
  SDL.Flip(surface)
  image.clip_rect_x=image.clip_rect_x+1
  SDL.WaitEvent(ev)
  select case ev.type
    case SDL_USEREVENT
      call SDL.AddTimer(60,SDL.NewTimerCallBack,0)
    case SDL_KEYDOWN
      if ev.key_keysym_sym=SDLK_ESCAPE then exit do
  end select
loop until ev.type=SDL_QUIT
WScript.Quit

タイマーの利用は、call back 関数を使うことになっているので厄介かかなと思ったけれど、SDLの仕様書では、
The timer callback function may run in a different thread than your main program, and so shouldn't call any functions from within itself. You may always call SDL_PushEvent, however.
となっている。なら、call backをWSHで記述することはできないし、どうせPushEventしか使えないのなら、それ専用の call back 関数をoyagameの中に用意すれば良いはずである。次のようにした。

static Uint32 NewTimerCallback(Uint32 interval, void *param){
    SDL_Event ev;
    ev.type=SDL_USEREVENT;
    ev.user.type=SDL_USEREVENT;
    ev.user.code=interval;
    ev.user.data1=param;
    int Stack[1]={(int)&ev};
    return CallSDL(L"PushEvent", 1, Stack );
}

ここで、このcall back関数に引き渡されている2つのパラメータ、interval と、*param の両方ともユーザーイベントのパラメータとして保存しているので、すべての用途に対応できるはず。

これで、無事タイマーの実装も確認できた。ここまでくれば、基本設計は問題ないはず。気分よく、先のマクロ関連の問題を解決した後に作成したのが次のコード。

set SDL=CreateObject("sfcmini.oyagame")
execute SDL.ConstVBS
SDL.Init(SDL_INIT_EVERYTHING)
set surface=SDL.SetVideoMode(200,100,32,SDL_RESIZABLE+SDL_HWSURFACE+SDL_DOUBLEBUF)
call SDL.WM_SetCaption("Hello oyagame",0)
set temp=SDL.LoadBMP("block.bmp")
set image=SDL.ConvertSurface ( temp, surface.format, SDL_HWSURFACE )
SDL.FreeSurface(temp)
call SDL.AddTimer(7,SDL.NewTimerCallBack,0)
set ev=SDL.CreateStructure("SDL_Event")
key=0
do
  call SDL.FillRect(surface, surface.clip_rect, 0)
  call SDL.BlitSurface(image,0,surface,image.clip_rect)
  SDL.Flip(surface)
  SDL.WaitEvent(ev)
  select case ev.type
    case SDL_USEREVENT
      call SDL.AddTimer(7,SDL.NewTimerCallBack,0)
    case SDL_KEYUP
      if ev.key_keysym_sym=key then key=0
    case SDL_KEYDOWN
      key=ev.key_keysym_sym
      case SDLK_ESCAPE
        exit do
  end select
  select case key
    case SDLK_UP
      image.clip_rect_y=image.clip_rect_y-1
    case SDLK_DOWN
      image.clip_rect_y=image.clip_rect_y+1
    case SDLK_RIGHT
      image.clip_rect_x=image.clip_rect_x+1
    case SDLK_LEFT
      image.clip_rect_x=image.clip_rect_x-1
  end select
loop until ev.type=SDL_QUIT
WScript.Quit

矢印キーを押すことで、画像を自由に動かすことに成功。上記VBScriptのコードも、無理なく記述することが可能になっている。ほぼ、完成だろう。

次に取り組んだのが、文字の表示。これはSDL本体では実装されていないようで、別途SDL_ttfというライブラリを使う必要があるらしい。そこで、SDL.dll 以外の DLL も呼び出せるようにし、その機能を用いて文字を表示させたテストコードが次。

set SDL=CreateObject("sfcmini.oyagame")
execute SDL.ConstVBS
SDL.Init(SDL_INIT_EVERYTHING)
set surface=SDL.SetVideoMode(200,100,32,SDL_RESIZABLE+SDL_HWSURFACE+SDL_DOUBLEBUF)
call SDL.WM_SetCaption("Hello oyagame",0)
set TTF=CreateObject("sfcmini.oyagame")
call TTF.LoadLibrary("SDL_ttf.dll","TTF_")
TTF.Init()
font=TTF.OpenFont("c:\windows\fonts\msgothic.ttc",16)
text=TTF.RenderText_Solid(font,"Hello oyagame!",&Hffffff)
set text=SDL.CreateStructure("SDL_Surface",text)
set ev=SDL.CreateStructure("SDL_Event")
do
  call SDL.BlitSurface(text,0,surface,text.clip_rect)
  SDL.Flip(surface)
  SDL.WaitEvent(ev)
loop until ev.type=SDL_QUIT
WScript.Quit

実行結果:
hello

TTF.RenderText_Solidの戻り値が整数なので、SDL.CreateStructureを用いて構造体オブジェクトに変換している。ここの仕様をどうしようか迷っているが、おそらくこのままにすると思う。SDL.dll 以外の機能を使う場合はこのような変換が必要と決めておくことで、仕様として統制が取れるからである。

まだ、SDL_ttf用の定数が使えないが、これは後のバージョンでサポートする予定。SDL_ttf, SDL_image, SDL_mixerの3つは良く使いそうなので、サポートすることになりそう。

次の予定としては、何か一つゲームをVBScriptかJScriptに移植して動作確認を取ること。これが済めば、正式版として公表かな。

あと、すぐにはやらないが、pygame互換のインターフェースが使えるようにもしたい。pygame入門のページをみていると、pygameのインターフェースは結構よさそうなので。

もう一つやりたいのは、ボタンやテキストボックスなどのコントロールを表示させること。SDL_EventStateでシステム固有のイベントをトラックするようにすれば、SDL_SysWMEvent構造体でmsgを取得可能のはず。ここで、msg が WM_CREATE のときに、CreateWindowすればよいのではないかと思う。ただ、ボタンをクリックしたときや、テキストボックスを変更したときなどのイベントをすべてフォローできるかどうか分からない(おそらく、すべては無理;wparamとlparamが受け渡されないようだ)。

<%media(20080508-oyagame_0030a.zip|oyagame0.0.3.0αはここからダウンロードできます。)%>

コメント

Kat (2008年5月11日 23:24:28)

SDL_SysWMEvent構造体のmsgは、SDL_SysWMmsg構造体へのポインタのようだ。SDL_SysWMmsgは、wparam, lparamを含んでいるので、すべてのイベントをトラックできるのではないだろうか。

コメント送信