2023/09/13(水) [n年前の日記]
#1 [hsp] 疑似3D道路その10
_昨日
の続き。HSP 3.6 を使って、疑似3D道路が作れないか試しているところ。環境は Windows10 x64 22H2。
一応、BG/遠景を、仮想画面の解像度に合わせて縮小描画させつつスクロールできるようにはなったのだけど、目で見て分かるぐらいに処理落ちが増えてしまった。元画像から縮小しながら 1280x720 の面積で、多重スクロールのためにBGを2枚描画するのは、今時のCPUで処理しても厳しいものがあるのだなと…。
DircetX や OpenGL を使ってGPUに描画させればあっさり状況は変わりそうだけど、後々スクリーンセーバにしたいと考えているので…。HSP でスクリーンセーバを作る際、DirectX や OpenGL が使えるのかどうかがよく分からなくて、おそらくは CPU で真面目に計算して拡大縮小描画をしているのであろう gzoom命令を使ってるという…。
ここまで遅くなるのでは、何か策を打たないといけない気がする。
余談。上記の方針を立てて画像の再作成作業を始めたら、メインPCがブルースクリーン(BSOD)に。最近BSODは見かけなかったのだけどなあ…。ちょうど Windows Update のタイミングだったようで、そのあたりが影響してる気がする…。
一応、BG/遠景を、仮想画面の解像度に合わせて縮小描画させつつスクロールできるようにはなったのだけど、目で見て分かるぐらいに処理落ちが増えてしまった。元画像から縮小しながら 1280x720 の面積で、多重スクロールのためにBGを2枚描画するのは、今時のCPUで処理しても厳しいものがあるのだなと…。
DircetX や OpenGL を使ってGPUに描画させればあっさり状況は変わりそうだけど、後々スクリーンセーバにしたいと考えているので…。HSP でスクリーンセーバを作る際、DirectX や OpenGL が使えるのかどうかがよく分からなくて、おそらくは CPU で真面目に計算して拡大縮小描画をしているのであろう gzoom命令を使ってるという…。
ここまで遅くなるのでは、何か策を打たないといけない気がする。
- 多重スクロールは諦めて、BG1枚だけを描画することにする。そもそも遠景だから多重スクロールの効果も弱かったし。画像作成時も、前後2枚になるように切り出しをする作業が面倒臭かったし。
- 事前に、仮想画面の解像度種類分、BG画像を用意しておいて、メインループを回してる時は拡大縮小描画(gzoom)ではなく、ベタ転送(gcopy)を使って描画する。その分メモリは食うけど…。
余談。上記の方針を立てて画像の再作成作業を始めたら、メインPCがブルースクリーン(BSOD)に。最近BSODは見かけなかったのだけどなあ…。ちょうど Windows Update のタイミングだったようで、そのあたりが影響してる気がする…。
◎ テストプログラムを置いておく :
一応、2枚のBG画像を縮小描画しながらスクロールさせるテストプログラムを置いておく。
_bgscroll.hsp
使用画像は以下。
_bg_bg0.png
_bg_bg1.png
動作結果は以下。
今気づいたけど、奥側のBGレイヤーについては、RGB=(0,0,0) を透明色として扱う gmode gmode_rgb0 を使わずに、ベタ転送する gmode gmode_gdi を使ったほうが良かったのでは…? そうすれば、少しは処理時間が短くなったのでは…。いや、焼け石に水か…。
これまた今気づいたけど、手前側のBGレイヤーの下半分で、奥側のBGレイヤーは完全に隠されてしまうのだから、そこは奥側を描画しなくても良かったのでは…? そうすれば、少しは処理時間が短くなったのでは…。まあ、それも焼け石に水かな…。
考えてみたら、BGが1枚しかなくても、ラスタースクロール相当をすれば多重スクロールに見えるよな…。縦方向に多重スクロールさせないなら、それで十分かも。まあ、そういう画像を作らないといかんのだけど。
_bgscroll.hsp
; BGスクロールのテスト ; ; Left, Right, Up, Down key : 表示位置変更 ; Z key : 仮想画面の解像度変更 ; ; 2023/09/13 by mieki256 ; 実画面解像度 #define SCR_W 640 #define SCR_H 360 ; #define SCR_W 1280 ; #define SCR_H 720 ; #define SCR_W 1600 ; #define SCR_H 900 ; #define SCR_W 1200 ; #define SCR_H 900 ; #define SCR_W 1920 ; #define SCR_H 1080 #define set_array_n2(%1, %2, %3, %4) \ %1(%2, 0) = %3 :\ %1(%2, 1) = %4 : ; 仮想画面解像度 dim vscr_size, 6, 2 set_array_n2 vscr_size, 0, 640, 360 set_array_n2 vscr_size, 1, 800, 450 set_array_n2 vscr_size, 2, 1280, 720 set_array_n2 vscr_size, 3, 320, 180 set_array_n2 vscr_size, 4, 480, 270 set_array_n2 vscr_size, 5, 512, 288 ; set_array_n2 vscr_size, 6, 1920, 1080 vscr_size_idx = 0 VSCR_W_MAX = 1920 VSCR_H_MAX = 1080 ; 同梱ファイル / bundled files #define BG0_FILE "bg_bg0.png" #define BG1_FILE "bg_bg1.png" ; #define BG0_FILE "bg_bg0_orig.png" ; #define BG1_FILE "bg_bg1_orig.png" #pack BG0_FILE #pack BG1_FILE #packopt name "bgscroll" ; exe filename #packopt type 0 ; generate ".exe" #packopt xsize SCR_W #packopt ysize SCR_H ; get windows size screen 0, SCR_W, SCR_H, 0 gsel 0 screen_w = ginfo_winx screen_h = ginfo_winy cls 4 ; init virtual screen buffer vscrbufid = 1 buffer vscrbufid, VSCR_W_MAX, VSCR_H_MAX dispw = vscr_size(vscr_size_idx, 0) disph = vscr_size(vscr_size_idx, 1) ; load bg image dim bgimgid, 2 dim bgw, 2 dim bgh, 2 dim bgfilename, 2 bgfilename(0) = BG0_FILE bgfilename(1) = BG1_FILE repeat 2 bgimgid(cnt) = 4 + cnt buffer bgimgid(cnt), 4096, 2048 picload bgfilename(cnt) bgw(cnt) = ginfo_winx bgh(cnt) = ginfo_winy loop ; init temporary image buffer tmpid = 7 buffer tmpid, 4096, 4096 ; bg scroll. 0.0 - 1.0 bg_x = 0.0 bg_y = 0.0 vscr_change = 0 nowframerate = 60.0 *mainloop #define KEY_ESC 128 #define KEY_UP 2 #define KEY_DOWN 8 #define KEY_LEFT 1 #define KEY_RIGHT 4 #define KEY_Z 2048 #define KEY_X 4096 #define KEY_PRESS (KEY_UP + KEY_DOWN + KEY_LEFT + KEY_RIGHT) stick k, KEY_PRESS ; ESC key to exit if k & KEY_ESC : goto *jobend ; 仮想画面サイズを変更 if k & KEY_Z { if vscr_change == 0 { vscr_change = 1 vscr_size_idx = (vscr_size_idx + 1) \ length(vscr_size) dispw = vscr_size(vscr_size_idx, 0) disph = vscr_size(vscr_size_idx, 1) } } else { vscr_change = 0 } ; check cursor key spd = 1.0 / (nowframerate * 1.0) if k & KEY_UP : bg_y -= spd if k & KEY_DOWN : bg_y += spd if k & KEY_LEFT : bg_x -= spd if k & KEY_RIGHT : bg_x += spd if bg_y < -1.0 : bg_y = -1.0 if bg_y > 1.0 : bg_y = 1.0 base_y = int(double(disph) * 0.5) ; 描画開始 / draw start redraw 0 gsel vscrbufid ; 背景(遠景)を描画 / draw BG draw_bg vscrbufid, bg_x, bg_y, bgimgid, bgw, bgh, dispw, disph, tmpid ; 仮想画面を実画面に転送 gsel 0 gmode gmode_gdi if screen_w == dispw and screen_h == disph { ; 等倍で転送 pos 0, 0 gcopy vscrbufid, 0, 0, dispw, disph } else { ; 拡大縮小して転送 xscale = double(screen_w) / double(dispw) yscale = double(screen_h) / double(disph) ; if xscale < yscale : scale = xscale : else : scale = yscale if xscale < yscale : scale = yscale : else : scale = xscale dstw = int(double(dispw) * scale) dsth = int(double(disph) * scale) dstx = (SCR_W - dstw) / 2 dsty = (SCR_H - dsth) / 2 pos dstx, dsty gzoom dstw, dsth, vscrbufid, 0, 0, dispw, disph, 0 } ; draw text if 1 { gsel 0 font "Arial", 16, 1, 2 objcolor 255, 255, 255 color 1, 1, 1 pos 4, 20 mes "Press cursor key or Z", 4 pos 4, 40 mes "Res:" + dispw + "x" + disph, 4 pos 4, 60 mes "bg_x, bg_y = " + bg_x + ", " + bg_y, 4 } redraw 1 ; 描画終了 / draw end await (1000 / int(nowframerate)) goto *mainloop #module #deffunc draw_bg int dstid, double bg_x, double bg_y, array bgimgid, array bgw, array bgh, int dispw, int disph, int tmpid ; BGを描画 / draw BG ; ; dstid : 描画先バッファID ; bg_x, bg_y : bg position. 0.0 - 1.0 ; bgimgid : bg images buffer ID. array. ; bgw, bgh : bg width, height. array. ; dispw, disph : screen width, height ; tmpid : 作業バッファID bx = bg_x by = bg_y if by < -1.0 : by = -1.0 if by > 1.0 : by = 1.0 repeat 2 i = (2 - 1) - cnt bgid = bgimgid(i) bw = bgw(i) bh = bgh(i) shiftx = bx if i == 1 : shiftx *= 0.75 sx = int(double(bh / 4) * shiftx) \ bw sy = int(double(bh / 4) * by) + (bh / 2) - (bh / 4) sw = (bh / 2) * dispw / disph sh = bh / 2 dx = 0 dy = 0 dw = dispw dh = disph if sx >= 0 and (sx + sw) < bw { draw_by_gzoom dstid, dx, dy, dw, dh, bgid, sx, sy, sw, sh, tmpid, dispw, disph } else { if (sx + sw) > bw : sx = (sx - bw) \ bw w = -sx sw0 = w sx0 = bw - w dx0 = 0 dw0 = w * dw / sw sw1 = sw - w sx1 = 0 dx1 = dw0 dw1 = dw - dw0 draw_by_gzoom dstid, dx0, dy, dw0, dh, bgid, sx0, sy, sw0, sh, tmpid, dispw, disph draw_by_gzoom dstid, dx1, dy, dw1, dh, bgid, sx1, sy, sw1, sh, tmpid, dispw, disph } loop return #global #module #deffunc draw_by_gzoom int dstid, int dstx, int dsty, int dstw, int dsth, int srcid, int srcx, int srcy, int srcw, int srch, int tmpid, int dispw, int disph ; 画面外にはみ出る分を考量して最小面積で画像を拡大縮小描画 ; ; dstid : 描画先バッファID ; dstx, dsty, dstw, dsth : 描画先左上座標, 描画先サイズ ; srcid : 元画像バッファID ; srcx, srcy, srcw, scrh : 元画像左上座標, 元画像サイズ ; tmpid : 作業用イメージバッファID ; dispw, disph : 画面サイズ dx = dstx dy = dsty dw = dstw dh = dsth sx = srcx sy = srcy sw = srcw sh = srch if dx >= dispw or (dx + dw) < 0 : return if dy >= disph or (dy + dh) < 0 : return if dx < 0 { w = (-dx) * sw / dw sx += w sw -= w dw += dx dx = 0 } if dy < 0 { h = (-dy) * sh / dh sy += h sh -= h dh += dy dy = 0 } if (dx + dw) > dispw { dwd = (dx + dw) - dispw swd = dwd * sw / dw sw -= swd dw -= dwd } if (dy + dh) > disph { dhd = (dy + dh) - disph shd = dhd * sh / dh sh -= shd dh -= dhd } ; gzoom は透明色を扱えないので小技が必要 ; 一旦、仮バッファに拡大縮小描画 gsel tmpid pos 0, 0 gzoom dw, dh, srcid, sx, sy, sw, sh, 0 ; 仮バッファから実スクリーンに gcopy でコピー ; gcopy なら RGB=(0,0,0) を透明色として扱える gsel dstid gmode gmode_rgb0 ; RGB=(0,0,0)を透明色として扱う pos dx, dy gcopy tmpid, 0, 0, dw, dh ; draw text if 0 { gmode gmode_gdi font "Arial", 18, 1 color 255, 255, 255 pos 4, 80 mes "x, y = " + dstx + ", " + dsty pos 4, 100 mes "dst x, y, w, h = " + dx + ", " + dy + " ," + dw + " ," + dh pos 4, 120 mes "src x, y, w, h = " + sx + ", " + sy + " ," + sw + " ," + sh } return #global *jobend end
使用画像は以下。
_bg_bg0.png
_bg_bg1.png
動作結果は以下。
今気づいたけど、奥側のBGレイヤーについては、RGB=(0,0,0) を透明色として扱う gmode gmode_rgb0 を使わずに、ベタ転送する gmode gmode_gdi を使ったほうが良かったのでは…? そうすれば、少しは処理時間が短くなったのでは…。いや、焼け石に水か…。
これまた今気づいたけど、手前側のBGレイヤーの下半分で、奥側のBGレイヤーは完全に隠されてしまうのだから、そこは奥側を描画しなくても良かったのでは…? そうすれば、少しは処理時間が短くなったのでは…。まあ、それも焼け石に水かな…。
考えてみたら、BGが1枚しかなくても、ラスタースクロール相当をすれば多重スクロールに見えるよな…。縦方向に多重スクロールさせないなら、それで十分かも。まあ、そういう画像を作らないといかんのだけど。
[ ツッコむ ]
以上です。