2024/02/02(金) [n年前の日記]
#1 [basic] FreeBASICでスクリーンセーバを作成その2
_昨日、
FreeBASIC Screensaver Kit を使って、FreeBASIC 1.10.1 でWindows用スクリーンセーバの雛形を作成したけれど。
_freeBASIC Screensaver Kit Updated - freebasic.net
_FreeBASIC Screensaver Kit - freebasic.net
おそらくは描画処理と表示のタイミングが同期していない(のであろう)点が気になった。せっかくだから、スクリーンセーバ用ウインドウの処理の中で、1フレーム毎に描画処理を呼ぶ形にできないか試してみた。これで、バッファに描画した直後にウインドウ内容の更新(バッファからウインドウにコピー)をするから、タイミングがずれることはないはず…。
環境は、Windows10 x64 22H2 + FreeBASIC 1.10.1 32bit。
実行した結果は以下のような感じ。昨日書いた版と見た目の違いはないけれど、描画と表示の同期は取れているはず。
ちなみに、メインPC (CPU: Ryzen 5 5600X)、サブPC (CPU: AMD Athlon 5350)上では、60FPS前後をふらふらしている。このくらいのシンプルな描画なら、低消費電力重視のCPU、ソフトウェア処理による描画でも、60FPSぐらいは出せるようだなと…。
_freeBASIC Screensaver Kit Updated - freebasic.net
_FreeBASIC Screensaver Kit - freebasic.net
おそらくは描画処理と表示のタイミングが同期していない(のであろう)点が気になった。せっかくだから、スクリーンセーバ用ウインドウの処理の中で、1フレーム毎に描画処理を呼ぶ形にできないか試してみた。これで、バッファに描画した直後にウインドウ内容の更新(バッファからウインドウにコピー)をするから、タイミングがずれることはないはず…。
環境は、Windows10 x64 22H2 + FreeBASIC 1.10.1 32bit。
実行した結果は以下のような感じ。昨日書いた版と見た目の違いはないけれど、描画と表示の同期は取れているはず。
ちなみに、メインPC (CPU: Ryzen 5 5600X)、サブPC (CPU: AMD Athlon 5350)上では、60FPS前後をふらふらしている。このくらいのシンプルな描画なら、低消費電力重視のCPU、ソフトウェア処理による描画でも、60FPSぐらいは出せるようだなと…。
◎ ファイル一式 :
ファイル一式は以下。
_ssfbscrsample3.zip
解凍して、build.bat を実行すれば、FreeBASICでコンパイルできる。ssfbscrsample3.exe が生成されたら、ssfbscrsample3.scr にリネームコピーされる。
ssfbscrsample3.scr をエクスプローラ上で右クリックして、「Test」「構成」を選べば、動作確認ができる。
出来上がった .scr を以下のフォルダにコピーすれば、スクリーンセーバとして使えるようになる。
自分が書いた部分は、CC0/ Public Domain ということで…。
_ssfbscrsample3.zip
解凍して、build.bat を実行すれば、FreeBASICでコンパイルできる。ssfbscrsample3.exe が生成されたら、ssfbscrsample3.scr にリネームコピーされる。
ssfbscrsample3.scr をエクスプローラ上で右クリックして、「Test」「構成」を選べば、動作確認ができる。
出来上がった .scr を以下のフォルダにコピーすれば、スクリーンセーバとして使えるようになる。
- スクリーンセーバが32bit版アプリで、Windows が 64bit版の場合、C:\Windows\SysWOW64\ にコピー。
- スクリーンセーバが32bit版アプリで、Windows が 32bit版の場合、C:\Windows\System32\ にコピー。
- スクリーンセーバが64bit版アプリで、Windows が 64bit版の場合、C:\Windows\System32\ にコピー。
自分が書いた部分は、CC0/ Public Domain ということで…。
◎ ソースも貼っておく :
一応ソースも貼っておく。
_build.bat
_ssfbscrsample3.xml
_fb_screensaverkit.bas
fb_screensaverkit.bas については、以下の変更を行った。
描画処理を担当しているのは ssfbscrsample3.bas。このファイルをカスタマイズすれば自分好みのスクリーンセーバが作れる。
_ssfbscrsample3.bas
以下の3つの関数/サブルーチンをカスタマイズすればいい。
リソースファイル。スクリーンセーバ名、アイコン画像ファイル名、設定ダイアログのレイアウトが記述されている。今回、設定ダイアログは、スクリーンセーバ名とOKボタンを表示するだけのダイアログになっている。
_ssfbscrsample3.rc
_build.bat
_ssfbscrsample3.xml
_fb_screensaverkit.bas
fb_screensaverkit.bas については、以下の変更を行った。
- ScreenSaverProc() の中に、時間計測処理と、初期化処理、描画処理、終了処理を呼び出す部分を追加した。
- StartScreenSaver(time_msec) に、タイマーをセットする際のミリ秒値を指定できるようにした。ここで与えたミリ秒の間隔で画面が更新される。
- FPSもカウントしてる。SaverInfo.fps の中に、計測したFPSが入っている。
描画処理を担当しているのは ssfbscrsample3.bas。このファイルをカスタマイズすれば自分好みのスクリーンセーバが作れる。
_ssfbscrsample3.bas
#include "fb_screensaverkit.bas" ' use mmsystem #include "windows.bi" #include "win/mmsystem.bi" ' global work Type wk scrw As Integer scrh As Integer x As Double y As Double dx As Double dy As Double r As Double max_fps As Double End Type Dim Shared wk As wk '-------------------- ' Initialize work Sub InitProc() ' get screen size. width and height ' ScreenInfo scrw, scrh wk.scrw = SaverInfo.ScrWidth wk.scrh = SaverInfo.ScrHeight wk.max_fps = SaverInfo.max_fps wk.x = wk.scrw / 2.0 wk.y = wk.scrh / 2.0 wk.dx = (CDbl(wk.scrw) / wk.max_fps) * 0.6 wk.dy = (CDbl(wk.scrh) / wk.max_fps) * 0.4 wk.r = wk.scrh / 16.0 ' timeBeginPeriod(1) End Sub '-------------------- ' Rendering Sub Render(ByVal delta As Double) If delta >= 1.0 Then delta = 1.0 / wk.max_fps ' move ball position wk.x += (wk.dx * wk.max_fps * delta) wk.y += (wk.dy * wk.max_fps * delta) If (wk.x <= wk.r And wk.dx < 0) Or (wk.x >= (wk.scrw - wk.r) And wk.dx > 0) Then wk.dx *= -1.0 If (wk.y <= wk.r And wk.dy < 0) Or (wk.y >= (wk.scrh - wk.r) And wk.dy > 0) Then wk.dy *= -1.0 ' draw start ' clear screen ' Line (0, 0)-(scrw, scrh), Rgb(0, 0, 0), BF color RGB(0, 0, 0), RGB(0, 0, 0) cls ' draw ball circle (wk.x, wk.y), wk.r, RGB(255, 0, 0), , , , F ' Get and draw FPS Dim As String fpstext = Str(SaverInfo.fps) & "FPS" Draw String ((wk.scrw - Len(fpstext) * 8) / 2, 10), fpstext, RGB(255, 255, 255) fpstext = "" ' destroying a string ' draw end End Sub '-------------------- ' End process Sub ExitProc() ' timeEndPeriod(1) End Sub ' -------------------- ' main routine Randomize Timer 'Start the Screen Saver. Set timer XX sec StartScreenSaver(14) ' Wait until the screensaver ends Do Until SaverInfo.IsClosing = True sleep 10 Loop
以下の3つの関数/サブルーチンをカスタマイズすればいい。
- InitProc() : 初期化処理。スクリーンセーバ用ウインドウが作成されたタイミングで呼ばれる。
- Render(delta) : 描画処理。毎フレーム呼ばれる。delta に、前回フレームからの経過時間(単位は秒。小数点以下有り)が入っているので、座標更新処理に使える。
- ExitProc() : 終了処理。スクリーンセーバ用ウインドウが破棄されるタイミングで呼ばれる。
リソースファイル。スクリーンセーバ名、アイコン画像ファイル名、設定ダイアログのレイアウトが記述されている。今回、設定ダイアログは、スクリーンセーバ名とOKボタンを表示するだけのダイアログになっている。
_ssfbscrsample3.rc
/* Screensaver title */ STRINGTABLE BEGIN 1 "FreeBASIC Screensaver 3" END /* icon */ 100 ICON "fbscricon.ico" /* dialog */ FB_SCRNSAVER_ABOUT DIALOGEX 6, 18, 160, 62 STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | DS_NOFAILCREATE FONT 12,"Arial" CAPTION "FB Screensaver Sample 3" BEGIN DEFPUSHBUTTON "OK" IDOK, 55, 40, 50, 14 CTEXT "FB Screensaver Sample 3" -1, 0, 5, 160, 8 CTEXT "Version 1.0" -1, 0, 15, 160, 8 END 1 24 "ssfbscrsample3.xml"
◎ 問題点 :
サブPC上で動かしていたら、何かの拍子にボールが画面内から消えた…。
おそらく、何かのプログラムがCPUリソースをごっそり持っていく時があるようで、スクリーンセーバの処理が数秒に渡って呼ばれない → 前フレームからの経過時間が数秒に達する → 移動速度がとんでもなく大きくなる → ボールが画面外に消えてしまう、という状態じゃないかなと。
であれば…。前フレームからの経過時間が、例えば1.0秒を超えるときは、全体的に負荷がかかっていてまともに処理できる状態じゃないのだろうから、経過時間を強制的に1/60秒 = 0.016秒程度にして計算する、等の処理が必要かもしれない。一応、その処理も入れてみた。
また別の話。今のところ、多重起動禁止をしてないのだけど、これでもいいのだろうか…。windows.bi を include して CreateMutex() を使うやり方で多重起動禁止はできそうな気がするけど…。MinGW の scrnsave.c を眺めたら、そちらも多重起動禁止をしてないように見えたのだよな…。実はその手の処理を入れなくてもいいのだろうか…?
おそらく、何かのプログラムがCPUリソースをごっそり持っていく時があるようで、スクリーンセーバの処理が数秒に渡って呼ばれない → 前フレームからの経過時間が数秒に達する → 移動速度がとんでもなく大きくなる → ボールが画面外に消えてしまう、という状態じゃないかなと。
であれば…。前フレームからの経過時間が、例えば1.0秒を超えるときは、全体的に負荷がかかっていてまともに処理できる状態じゃないのだろうから、経過時間を強制的に1/60秒 = 0.016秒程度にして計算する、等の処理が必要かもしれない。一応、その処理も入れてみた。
また別の話。今のところ、多重起動禁止をしてないのだけど、これでもいいのだろうか…。windows.bi を include して CreateMutex() を使うやり方で多重起動禁止はできそうな気がするけど…。MinGW の scrnsave.c を眺めたら、そちらも多重起動禁止をしてないように見えたのだよな…。実はその手の処理を入れなくてもいいのだろうか…?
◎ 余談。FreeBASICのStringについて :
C言語で文字列(char配列など)を扱う際は、あらかじめ十分足りそうなだけのサイズを用意して、その領域内を書き換えながら処理したりするけれど。FreeBASIC の場合、そのあたりはどうなっているのか気になった。
_String
_STRING (関数)
よく分からないな…。とりあえず、"" を代入すると割り当てが解放される云々と書いてあるので、一応やっておこう…。
_String
_STRING (関数)
よく分からないな…。とりあえず、"" を代入すると割り当てが解放される云々と書いてあるので、一応やっておこう…。
[ ツッコむ ]
以上です。