mieki256's diary



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ぐらいは出せるようだなと…。

ファイル一式 :

ファイル一式は以下。

_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 については、以下の変更を行った。
  • 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 を眺めたら、そちらも多重起動禁止をしてないように見えたのだよな…。実はその手の処理を入れなくてもいいのだろうか…?

余談。FreeBASICのStringについて :

C言語で文字列(char配列など)を扱う際は、あらかじめ十分足りそうなだけのサイズを用意して、その領域内を書き換えながら処理したりするけれど。FreeBASIC の場合、そのあたりはどうなっているのか気になった。

_String
_STRING (関数)

よく分からないな…。とりあえず、"" を代入すると割り当てが解放される云々と書いてあるので、一応やっておこう…。

以上、1 日分です。

過去ログ表示

Prev - 2024/02 - Next
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29

カテゴリで表示

検索機能は Namazu for hns で提供されています。(詳細指定/ヘルプ


注意: 現在使用の日記自動生成システムは Version 2.19.6 です。
公開されている日記自動生成システムは Version 2.19.5 です。

Powered by hns-2.19.6, HyperNikkiSystem Project