aliens.vbs
2008年5月17日
先日配布の、oyagameを利用したゲーム、aliensのコードを少しばかり解説。
まず、ライセンスはGPL。これは、CでできたオリジナルのaliensがGPLであることによる。
はじめに、3つのoyagameオブジェクトをCreateObject()を使って作成。『execute SDL.ConstVBS』で定数の定義を行った後、SDLを初期化。SDL_imageについては、『call IMG.LoadLibrary("SDL_image.dll","IMG_")』のように設定している。2番目の引数に"IMG_"と入れておくことで、以後の呼び出しでは『IMG.IMG_xxx』ではなく『IMG.xxx』を用いることができる。SDL_mixerについても、同様。
DATAFILE()は、aliens.cで使われていたマクロをVBScriptの関数に置き換えたもの。Mix_LoadWAV()は、SDL_mixer.hで定義されているマクロをVBScriptの関数にしたもの。このあたりがSDLコア以外のライブラリを使うときのウィークポイントになりそう。ただ、一部のライブラリだけを差別化したくないので、仕様としてはこういった記述を加えないといけないことになると思う。
このあたりは、オリジナルのaliensを素直に移植したようなコード。『SDL.CreateStructure("SDL_Rect")』は、oyagameを使う場合、おそらく頻繁に出てくるコードだ。
LoadImage()関数を定義するコードの4行目、『SDL.int8(image.pixels)』に注目。オリジナルのaliens.cでは、『SDL_SetColorKey(image, (SDL_SRCCOLORKEY|SDL_RLEACCEL), *(Uint8 *)image->pixels);』となっている部分。SDL_Surfaceのpixels要素は void* 型なので、ここの例のようにSDL.int8()を利用して、void* というポインタの参照先から8ビット整数を取り出している。
UpdateScreen()関数の定義コード5行目。ここは、オリジナルではSDL_UpdateRects()を使っている部分だが、ここにoyagameの弱点があるかも。たぶん、oyagameではSDL_UpdateRects()は使えない。ここでは代わりにSDL.UpdateRectを使っている。
上記コードの、下から13行目、『SDL.int8(keys+SDLK_SPACE)=SDL_PRESSED』は、オリジナルでは『keys[SDLK_SPACE] == SDL_PRESSED 』となっている。このあたりの使い方は、Cにおけるメモリ管理について知識がないと分かりにくい部分だ。キーステータスの取得はこんな風に行うと覚えるしかないかなと言うところ。
残りの部分は、おそらく解説の必要は無いと思う。『'Main routine follows』から後の記述(上記コードの最後の数行)は、Cにおけるmain()関数内に記述されているコードを移植したもの。
ゲームを一つ移植してみて分かったことは
1)現在の仕様でちゃんとゲームが作れる
2)oyagame独特のコード記述がいくつかある
といったこと。最終目標は、中学生・高校生にゲームを作ってもらうことだが、まだまだそのレベルではなさそう。速いうちに、pygame互換のインターフェースを用意したほうが良いかもしれない。
'''''''''''''''''''''''''''''''''''''
' The license of this script is GPL '
' '
'''''''''''''''''''''''''''''''''''''
option explicit
dim SDL,IMG,Mix,i
set SDL=CreateObject("sfcmini.oyagame")
execute SDL.ConstVBS
call SDL.Init(SDL_INIT_EVERYTHING)
set IMG=CreateObject("sfcmini.oyagame")
call IMG.LoadLibrary("SDL_image.dll","IMG_")
set Mix=CreateObject("sfcmini.oyagame")
call Mix.LoadLibrary("SDL_mixer.dll","Mix_")まず、ライセンスはGPL。これは、CでできたオリジナルのaliensがGPLであることによる。
はじめに、3つのoyagameオブジェクトをCreateObject()を使って作成。『execute SDL.ConstVBS』で定数の定義を行った後、SDLを初期化。SDL_imageについては、『call IMG.LoadLibrary("SDL_image.dll","IMG_")』のように設定している。2番目の引数に"IMG_"と入れておくことで、以後の呼び出しでは『IMG.IMG_xxx』ではなく『IMG.xxx』を用いることができる。SDL_mixerについても、同様。
Function DATAFILE(X) DATAFILE="data\" & X End Function Function Mix_LoadWAV(file) Mix_LoadWAV=Mix.LoadWAV_RW(SDL.RWFromFile(file,"rb"),1) End Function
DATAFILE()は、aliens.cで使われていたマクロをVBScriptの関数に置き換えたもの。Mix_LoadWAV()は、SDL_mixer.hで定義されているマクロをVBScriptの関数にしたもの。このあたりがSDLコア以外のライブラリを使うときのウィークポイントになりそう。ただ、一部のライブラリだけを差別化したくないので、仕様としてはこういった記述を加えないといけないことになると思う。
const FRAMES_PER_SEC=50
const PLAYER_SPEED=4
const MAX_SHOTS=3
const SHOT_SPEED=6
const MAX_ALIENS=30
const ALIEN_SPEED=5
const ALIEN_ODDS=50 '(1*FRAMES_PER_SEC)
const EXPLODE_TIME=20
const MAX_UPDATES=102 '3*(1+MAX_SHOTS+MAX_ALIENS)
class object
public alive
public facing
public x,y
public image
private Sub Class_Initialize
alive=0
facing=0
x=0
y=0
image=0
end sub
end class
dim screen,background
dim player
set player=new object
dim reloading
reloading=0
dim shots(3)'MAX_SHOTS
for i=0 to MAX_SHOTS-1
set shots(i)=new object
next
dim aliens(30)'MAX_ALIENS
for i=0 to MAX_ALIENS-1
set aliens(i)=new object
next
dim explosions(31)'MAX_ALIENS+1
for i=0 to MAX_ALIENS
set explosions(i)=new object
next
dim numupdates
dim srcupdates(102),dstupdates(102)'MAX_UPDATES
for i=0 to MAX_UPDATES-1
set srcupdates(i)=SDL.CreateStructure("SDL_Rect")
set dstupdates(i)=SDL.CreateStructure("SDL_Rect")
next
class blit
public src
public srcrect
public dstrect
private Sub Class_Initialize
set srcrect=SDL.CreateStructure("SDL_Rect")
set dstrect=SDL.CreateStructure("SDL_Rect")
End Sub
end class
dim blits(102)'MAX_UPDATES
for i=0 to MAX_UPDATES-1
set blits(i)=new blit
nextこのあたりは、オリジナルのaliensを素直に移植したようなコード。『SDL.CreateStructure("SDL_Rect")』は、oyagameを使う場合、おそらく頻繁に出てくるコードだ。
dim music
const MUSIC_WAV=0
const SHOT_WAV=1
const EXPLODE_WAV=2
const NUM_WAVES=3
dim sounds(3)'NUM_WAVES
Function LoadImage(datafile, transparent)
dim image,surface
image=IMG.Load(datafile)
set image=SDL.CreateStructure("SDL_Surface",image)
if transparent then call SDL.SetColorKey(image,SDL_SRCCOLORKEY+SDL_RLEACCEL,SDL.int8(image.pixels))
set surface=SDL.DisplayFormat(image)
SDL.FreeSurface(image)
set LoadImage=surface
End FunctionLoadImage()関数を定義するコードの4行目、『SDL.int8(image.pixels)』に注目。オリジナルのaliens.cでは、『SDL_SetColorKey(image, (SDL_SRCCOLORKEY|SDL_RLEACCEL), *(Uint8 *)image->pixels);』となっている部分。SDL_Surfaceのpixels要素は void* 型なので、ここの例のようにSDL.int8()を利用して、void* というポインタの参照先から8ビット整数を取り出している。
Function LoadData
dim i
music=Mix.LoadMUS(DATAFILE("music.it"))
sounds(MUSIC_WAV)=Mix_LoadWAV(DATAFILE("music.wav"))
sounds(SHOT_WAV)=Mix_LoadWAV(DATAFILE("shot.wav"))
sounds(EXPLODE_WAV)=Mix_LoadWAV(DATAFILE("explode.wav"))
set player.image=LoadImage(DATAFILE("player.gif"),1)
set shots(0).image=LoadImage(DATAFILE("shot.gif"),0)
for i=1 to MAX_SHOTS-1
set shots(i).image=shots(0).image
next
set aliens(0).image=LoadImage(DATAFILE("alien.gif"),1)
for i=1 to MAX_ALIENS-1
set aliens(i).image=aliens(0).image
next
set explosions(0).image=LoadImage(DATAFILE("explosion.gif"),1)
for i=1 to MAX_ALIENS
set explosions(i).image=explosions(0).image
next
set background=LoadImage(DATAFILE("background.gif"),0)
for i=0 to MAX_UPDATES-1
set blits(i).srcrect=srcupdates(i)
set blits(i).dstrect=dstupdates(i)
next
LoadData=1
End Function
Function FreeData
dim i
if music then Mix.FreeMusic(music)
for i=0 to NUM_WAVES-1
if sounds(i) then Mix.FreeMusic(sounds(i))
next
SDL.FreeSurface(player.image)
SDL.FreeSurface(shots(0).image)
SDL.FreeSurface(aliens(0).image)
SDL.FreeSurface(explosions(0).image)
SDL.FreeSurface(background)
End Function
Function CreateAlien
dim i
for i=0 to MAX_ALIENS-1
if aliens(i).alive=0 then exit for
next
if i=MAX_ALIENS then exit function
with aliens(i)
.facing=1
if rnd(1)<0.5 then .facing=-1
.y=0
.x=0
if .facing<0 then .x=screen.w - .image.w - 1
.alive=1
end with
End Function
Function DrawObject(sprite)
dim update
set update=blits(numupdates)
numupdates=numupdates+1
set update.src=sprite.image
update.srcrect.x=0
update.srcrect.y=0
update.srcrect.w=sprite.image.w
update.srcrect.h=sprite.image.h
update.dstrect.x=sprite.x
update.dstrect.y=sprite.y
update.dstrect.w=sprite.image.w
update.dstrect.h=sprite.image.h
End Function
Function EraseObject(sprite)
dim update,wrap
set update=blits(numupdates)
numupdates=numupdates+1
set update.src=background
update.srcrect.x=sprite.x mod background.w
update.srcrect.y=sprite.y
update.srcrect.w=sprite.image.w
update.srcrect.h=sprite.image.h
wrap=(update.srcrect.x+update.srcrect.w)-(background.w)
if wrap>0 then update.srcrect.w=update.srcrect.w-wrap
update.dstrect.x=sprite.x
update.dstrect.y=sprite.y
update.dstrect.w=update.srcrect.w
update.dstrect.h=update.srcrect.h
if wrap>0 then
set update=blits(numupdates)
numupdates=numupdates+1
set update.src=background
update.srcrect.x=0
update.srcrect.y=sprite.y
update.srcrect.w=wrap
update.srcrect.h=sprite.image.h
update.dstrect.x=((sprite.x\background.w)+1)*background.w
update.dstrect.y=sprite.y
update.dstrect.w=update.srcrect.w
update.dstrect.h=update.srcrect.h
end if
End Function
Function UpdateScreen
dim i
for i=0 to numupdates-1
call SDL.LowerBlit(blits(i).src,blits(i).srcrect,screen,blits(i).dstrect)
next
call SDL.UpdateRect(screen, 0, 0, 0, 0)
'SDL.Flip()
numupdates=0
End FunctionUpdateScreen()関数の定義コード5行目。ここは、オリジナルではSDL_UpdateRects()を使っている部分だが、ここにoyagameの弱点があるかも。たぶん、oyagameではSDL_UpdateRects()は使えない。ここでは代わりにSDL.UpdateRectを使っている。
Function Collide(sprite1,sprite2)
Collide=0
if sprite1.y >= (sprite2.y+sprite2.image.h) then exit function
if sprite1.x >= (sprite2.x+sprite2.image.w) then exit function
if sprite2.y >= (sprite1.y+sprite1.image.h) then exit function
if sprite2.x >= (sprite1.x+sprite1.image.w) then exit function
Collide=1
End Function
dim next_tick 'used as static var in following function
next_tick=0
Function WaitFrame
dim this_tick
this_tick=SDL.GetTicks()
if (this_tick<next_tick) then SDL.Delay(next_tick-this_tick)
next_tick=this_tick + (1000/FRAMES_PER_SEC)
End Function
Function RunGame
dim i,j,ev,keys,dst
set ev=SDL.CreateStructure("SDL_Event")
set dst=SDL.CreateStructure("SDL_Rect")
'Paint the background
numupdates=0
for i=0 to screen.w step background.w
dst.x=i
dst.y=0
dst.w=background.w
dst.h=background.h
call SDL.BlitSurface(background,0,screen,dst)
next
call SDL.UpdateRect(screen, 0, 0, 0, 0)
'SDL.Flip()
'Initialize the objects
player.alive=1
player.x=(screen.w-player.image.w)\2
player.y=(screen.h-player.image.h)-1
player.facing=0
DrawObject(player)
for i=0 to MAX_SHOTS-1
shots(i).alive=0
next
for i=0 to MAX_ALIENS -1
aliens(i).alive=0
next
CreateAlien()
DrawObject(aliens(0))
UpdateScreen()
do while (player.alive)
'Wait for the next frame
WaitFrame()
'Poll input quese, run keyboard loop
do while (SDL.PollEvent(ev))
if ev.type=SDL_QUIT then exit function
loop
keys=SDL.GetKeyState(0)
'Erase everything from the screen
for i=0 to MAX_SHOTS-1
if shots(i).alive then EraseObject(shots(i))
next
for i=0 to MAX_ALIENS-1
if aliens(i).alive then EraseObject(aliens(i))
next
EraseObject(player)
for i=0 to MAX_ALIENS
if explosions(i).alive then EraseObject(explosions(i))
next
'decrement the lifetime of explosions
for i=0 to MAX_ALIENS
j=explosions(i).alive
if j then explosions(i).alive=j-1
next
'Create new alien
if (rnd(1)*ALIEN_ODDS<1) then CreateAlien()
'Create new shots
if reloading=0 then
if SDL.int8(keys+SDLK_SPACE)=SDL_PRESSED then
for i=0 to MAX_SHOTS-1
if shots(i).alive=0 then exit for
next
if i<MAX_SHOTS then
shots(i).x=player.x+(player.image.w-shots(i).image.w)\2
shots(i).y=player.y-shots(i).image.h
shots(i).alive=1
call Mix.PlayChannelTimed(SHOT_WAV,sounds(SHOT_WAV),0,-1)
end if
end if
end if
reloading=SDL.int8(keys+SDLK_SPACE)上記コードの、下から13行目、『SDL.int8(keys+SDLK_SPACE)=SDL_PRESSED』は、オリジナルでは『keys[SDLK_SPACE] == SDL_PRESSED 』となっている。このあたりの使い方は、Cにおけるメモリ管理について知識がないと分かりにくい部分だ。キーステータスの取得はこんな風に行うと覚えるしかないかなと言うところ。
'Move the player dim facing,x facing=0 if SDL.int8(keys+SDLK_RIGHT) then facing=facing+1 if SDL.int8(keys+SDLK_LEFT) then facing=facing-1 x=player.x+facing*PLAYER_SPEED if x<0 then x=0 if x>=screen.w-player.image.w then x=screen.w-player.image.w-1 player.x=x player.facing=facing 'Move the aliens for i=0 to MAX_ALIENS-1 if aliens(i).alive then x=aliens(i).x+aliens(i).facing*ALIEN_SPEED if x<0 then x=0 aliens(i).y=aliens(i).y+aliens(i).image.h aliens(i).facing=1 elseif x>=screen.w-aliens(i).image.w then x=screen.w-aliens(i).image.w-1 aliens(i).y=aliens(i).y+aliens(i).image.h aliens(i).facing=-1 end if aliens(i).x=x end if next 'Move the shots for i=0 to MAX_SHOTS-1 if shots(i).alive then shots(i).y=shots(i).y-SHOT_SPEED if shots(i).y<0 then shots(i).alive=0 end if next 'Detect collisions for j=0 to MAX_SHOTS-1 for i=0 to MAX_ALIENS-1 if shots(j).alive and aliens(i).alive then if Collide(shots(j),aliens(i)) then aliens(i).alive=0 explosions(i).x=aliens(i).x explosions(i).y=aliens(i).y explosions(i).alive=EXPLODE_TIME call Mix.PlayChannelTimed(EXPLODE_WAV,sounds(EXPLODE_WAV),0,-1) shots(j).alive=0 exit for end if end if next next for i=0 to MAX_ALIENS-1 if aliens(i).alive then if Collide(player,aliens(i)) then aliens(i).alive=0 explosions(i).x=aliens(i).x explosions(i).y=aliens(i).y explosions(i).alive=EXPLODE_TIME player.alive=0 explosions(MAX_ALIENS).x=player.x explosions(MAX_ALIENS).y=player.y explosions(MAX_ALIENS).alive=EXPLODE_TIME call Mix.PlayChannelTimed(EXPLODE_WAV,sounds(EXPLODE_WAV),0,-1) end if end if next 'Draw the aliens, shots, player, and explosions for i=0 to MAX_ALIENS-1 if aliens(i).alive then DrawObject(aliens(i)) next for i=0 to MAX_SHOTS-1 if shots(i).alive then DrawObject(shots(i)) next if player.alive then DrawObject(player) for i=0 to MAX_ALIENS if explosions(i).alive then DrawObject(explosions(i)) next UpdateScreen() 'Loop the music if Mix.PlayingMusic()=0 then call Mix.PlayMusic(music,0) 'Check for keyboard abort if SDL.int8(keys+SDLK_ESCAPE) then player.alive=0 loop do while Mix.Playing(EXPLODE_WAV) WaitFrame() loop Mix.HaltChannel(-1) End Function 'Main routine follows randomize call Mix.OpenAudio(11025, AUDIO_U8, 1, 512) set screen = SDL.SetVideoMode(640, 480, 0, SDL_SWSURFACE) LoadData() RunGame() FreeData() Mix.CloseAudio() WScript.Quit
残りの部分は、おそらく解説の必要は無いと思う。『'Main routine follows』から後の記述(上記コードの最後の数行)は、Cにおけるmain()関数内に記述されているコードを移植したもの。
ゲームを一つ移植してみて分かったことは
1)現在の仕様でちゃんとゲームが作れる
2)oyagame独特のコード記述がいくつかある
といったこと。最終目標は、中学生・高校生にゲームを作ってもらうことだが、まだまだそのレベルではなさそう。速いうちに、pygame互換のインターフェースを用意したほうが良いかもしれない。