; HSPを使ったスクリーンセーバ作成のテスト ; ; 「HSPによるスクリーンセーバーサンプル」を改造。 ; Ctrl+F9で .scr が作成される。 ; ---------------------------------------- ; Windows用のスクリーンセーバーは、 ; 以下のコマンドラインオプションで処理を分けることになってる。 ; ; /C コンフィグ。設定画面を表示する。 ; /P プレビュー。小さな画面に表示されるモード。 ; /S フルスクリーン。実際のスクリーンセーバー画面。 ; エディタから動作確認した際は Alt+F4 で終了。 ; ; ---------------------------------------- ; 実行ファイルの作成パラメータ ; #packopt で生成するファイルの設定ができる。 ; name で実行ファイル名を指定。 ; type 2 でスクリーンセーバ生成を指定。 ; xsize, ysize は初期ウインドウサイズ #packopt name "tlssaver" #packopt type 2 #packopt xsize 320 ; 設定画面の横サイズ #packopt ysize 200 ; 設定画面の縦サイズ ; ---------------------------------------- ; コマンドラインオプションで処理を分ける。 ; HSPスクリプトエディタ上で動作確認する場合は、 ; HSP → 起動オプション → /s、/p、/c のどれか指定してから ; F5キー(コンパイル+実行)。 s=dir_cmdline ; コマンドライン文字列を取得 a=peek(s,1) ; 2文字目取得 if (a>='A') & (a<='Z') : a += 32 ; 大文字を小文字に変換 if a = 'p' : goto *sspre ; プレビュー if a = 's' : goto *ssproc ; フルスクリーン if a = 'c' : goto *sscfg ; コンフィグ ; if a = 0 : goto *ssproc ; 開発時用 ; if a = 0 : goto *sspre ; 開発時用 if a = 0 : goto *sscfg ; コンフィグ ; コマンドラインがどれでもない時はエラーを表示。 dialog "Unknown switch ["+s+"]" end *sscfg ; コンフィグ(設定画面) ; このスクリプトでは単にタイトルを表示。 ; 実際は、スクリーンセーバーの状態やモードなどを設定させる。 ; その場合、設定保存ファイルを作って bload, bsave で管理すると良い。 tt="Tail Light Screensaver 0.2.0" title tt gsel 0 width 320,200 cls pos 4,8 mes tt mes "" mes "Author : mieki256" mes "License: CC0 / Public Domain" pos 220,140 button "OK", *ssend stop *ssend end *sspre ; プレビュー画面。 ; スクリーンセーバ選択時に見える、小さな画面内でのプレビュー処理。 ; プレビューモードでは、HSPメイン画面(ウインドゥID0)が、 ; プレビュー画面に合わせて小さくなっている。 ; ginfo_winx, ginfo_winy で画面サイズを取得してその中に描画。 onexit goto *ssend ; 画面IDと画面サイズを指定 bg_id = 0 gsel bg_id scrw = ginfo_winx scrh = ginfo_winy cls 4 ; フルスクリーン画面と同じ処理をする goto *ssproc_start ; stop *ssproc ; フルスクリーン画面。 ; 実際に実行されるスクリーンセーバ本体の処理。 ; ginfo_dispx , ginfo_dispy から全画面サイズのウインドウを作成。 ; このための命令は以下。 ; bgscr 2,scrw,scrh,0,0,0,scrw,scrh ; ID2 の画面に描画された内容がスクリーンセーバの実行画面になる。 ; スクリーンセーバからの復帰チェックは行なう必要は無く、 ; 実行中にマウスを動かすと自動的に中断され復帰するようになっている。 ; ただし、スクリプトエディタから実行した際は ; マウスを動かしても復帰しないので Alt + F4 で終了すること。 onexit goto *ssend ; デスクトップ画面サイズを取得 scrw = ginfo_dispx scrh = ginfo_dispy ; 枠無しウインドウを初期化。ID = bg_id にフルスクリーン画面を作成 bg_id = 2 bgscr bg_id, scrw, scrh, 0, 0, 0, scrw, scrh cls 4 ; 黒で画面クリア *ssproc_start randomize ; 起動のたびに違う乱数を作る ; ぼけた円画像を2枚生成 img_id = 3 gosub *make_src_image ; ID = img_id に画像を生成 img_id = 4 gosub *make_src_image ; 画像を拡大縮小するための仮バッファを生成 src_id = 8 buffer src_id, scrw * 4, scrh * 4 cls 4 tmpbg_id = 9 buffer tmpbg_id, scrw, scrh cls 4 gosub *ssini goto *sslp *ssini ; 各種変数初期化 dist = double(scrw / 5) ; 画面までの距離 yofs = double(scrh / 5) ; ベジェ曲線用の座標を決定 ddim p, 4, 3 ; スタート地点 p(0, 0) = double(rnd(scrw) - (scrw / 2)) * 1.2 p(0, 1) = double(rnd(scrh)) * 2.0 p(0, 2) = dist * 0.5 repeat 3 ; x, y, z を決定 i = cnt + 1 p(i, 0) = double(rnd(scrw) - (scrw / 2)) * 40 p(i, 1) = double(rnd(scrh) - (scrh / 2)) * 20 if i = 3 { p(i, 2) = double((scrw * 12) + rnd(scrw * 12)) } else { p(i, 2) = double(rnd(scrw * 16)) } loop if rnd(100) < 20 { ; 時々、前後を交換 repeat 3 t = p(3, cnt) p(3, cnt) = p(0, cnt) p(0, cnt) = t loop zplus = 0 } else { zplus = 1 } startw = double(scrw / 3) ; ライトの初期横幅 starth = double(startw * 0.5) ; ライトの初期縦幅 xdist = startw * 1.3 ; ライトの間隔 alpha = 32.0 if rnd(100) < 70 { img_id = 3 } else { img_id = 4 } rep_cnt = 8 count_max = double(60 * (2 + rnd(3)) * rep_cnt) t_spd = double(1.0) / count_max nt = double(0.0) wait_timer = rnd(70) ; 次の車が走るまでの時間待ち return *sslp ; 表示メインループ redraw 0 ; 画面のちらつき防止 ; 画面全体を黒の半透明でうっすらと塗り潰す gsel tmpbg_id gmode gmode_alpha,,,1 color 0,0,0 grect (scrw/2), (scrh/2), 0, scrw, scrh if wait_timer <= 0 { repeat rep_cnt ; 現在座標(3D)を取得 ; ライトを描画 t = nt gosub *get_position tgt_id = tmpbg_id talpha = alpha gosub *draw_light nt += t_spd if nt >= 1.0 { tgt_id = tmpbg_id gosub *draw_big_light ; 目標値を変更 gosub *ssini break } loop ; 実画面に描画 gsel bg_id gmode 0 pos 0, 0 gcopy tmpbg_id, 0, 0, scrw, scrh ; ライト(大)を描画 if wait_timer <= 0 { tgt_id = bg_id gosub *draw_big_light } } else { ; 実画面に描画 gsel bg_id gmode 0 pos 0, 0 gcopy tmpbg_id, 0, 0, scrw, scrh wait_timer -= 1 } redraw 1 ; 画面のちらつき防止 await 16 goto *sslp *get_position ; ライトを描画する ; t に 0.0から1.0の間の数値を入れて呼ぶ。 ; x0, y0, x1, y1 にライトの描画座標、 ; w0, h0, w1, h1 にライトのサイズが入る。 org_t = t ; #define ANGLE_ENABLE #ifdef ANGLE_ENABLE ; 角度を得てライトの位置を変える処理を試したけれど ; 何故か時々くるっと回ってしまうのでやめることにした。 ; どうして回っちゃうんだろう…? ; ベジェ曲線の一点を取得 ; nx, ny, nz に座標が入る ; 少し先の座標を得る t = org_t + 0.1 gosub *get_bezier_point nnx = nx nny = ny nnz = nz ; 現在座標を得る t = org_t gosub *get_bezier_point ; 角度を得る rad = atan(nnz - nz, nnx - nx) rad += 270.0 * 3.14159 / 180.0 ; 向きを90度変える rx = cos(rad) * double(xdist) rz = sin(rad) * double(xdist) ; 右ライトの座標とサイズを取得 tx = nx + rx tz = nz + rz ty = ny zv = dist / tz x0 = (tx * zv) + (scrw / 2) y0 = (ty * zv) + yofs w0 = startw * zv h0 = starth * zv ; 左ライトの座標とサイズを取得 tx = nx - rx tz = nz - rz ty = ny zv = dist / tz x1 = (tx * zv) + (scrw / 2) y1 = (ty * zv) + yofs w1 = startw * zv h1 = starth * zv #else ; ライトの位置を角度で変えない場合の処理 ; 現在座標を得る t = org_t gosub *get_bezier_point ; 右ライトの座標とサイズを取得 tx = nx + xdist ty = ny tz = nz zv = dist / tz x0 = (tx * zv) + (scrw / 2) y0 = (ty * zv) + yofs w0 = startw * zv h0 = starth * zv ; 左ライトの座標とサイズを取得 tx = nx - xdist ty = ny tz = nz zv = dist / tz x1 = (tx * zv) + (scrw / 2) y1 = (ty * zv) + yofs w1 = startw * zv h1 = starth * zv #endif t = org_t return *get_bezier_point ; ベジェ曲線の一点を取得 ; t に 0.0 から 1.0 の範囲の値を入れて呼ぶ。 ; nx, ny, nz に座標値が入ってくる。 k = double(1.0) - t v0 = k * k * k v1 = 3.0 * k * k * t v2 = 3.0 * k * t * t v3 = t * t * t nx = v0 * p(0, 0) + v1 * p(1, 0) + v2 * p(2, 0) + v3 * p(3, 0) ny = v0 * p(0, 1) + v1 * p(1, 1) + v2 * p(2, 1) + v3 * p(3, 1) nz = v0 * p(0, 2) + v1 * p(1, 2) + v2 * p(2, 2) + v3 * p(3, 2) return *draw_light ; ライトを描画 ; tgt_id に描画先画面のID ; talpha に加算合成の割合(0-255)を入れて呼ぶ。 ; 加算合成の割合を、t を使って求める if zplus = 0 { ; 手前に向かって走ってる a = talpha } else { ; 奥に向かって走ってる if t <= 0.80 { ; まだ終わりに近づいてない a = talpha } else { ; 終わりに近づいているので薄くする a = double(talpha) * (1.0 - t) * 5.0 } } ; 右ライト描画 ; 仮バッファに画像を拡大縮小して描画 gsel src_id pos 0, 0 gzoom w0, h0, img_id, 0, 0, imgw, imgh ; 仮バッファをソース画像として加算合成描画 gsel tgt_id gmode gmode_add, w0, h0, a pos x0 - (w0 / 2), y0 - (h0 / 2) gcopy src_id, 0, 0, w0, h0 ; 左ライト描画 gsel src_id pos 0, 0 gzoom w1, h1, img_id, 0, 0, imgw, imgh gsel tgt_id gmode gmode_add, w1, h1, a pos x1 - (w1 / 2), y1 - (h1 / 2) gcopy src_id, 0, 0, w1, h1 return *draw_big_light ; ライト(大)を描画 ; tgt_id に描画先画面のIDを入れて呼ぶ w0 *= 3.0 h0 *= 3.0 w1 *= 3.0 h1 *= 3.0 talpha = 192 gosub *draw_light return *make_src_image ; ID = img_id の画面に、ソース画像として使うための画像を生成 ; 画像サイズを imgw, imgh に入れて戻る。 ; imgw, imgh は、その後別画面に転送する時に使用される。 buffer img_id, 256, 256, 0 imgw = ginfo_winx imgh = ginfo_winy cls 4 if img_id = 3 { ; 赤い円 col_r = 1.0 col_g = 0.0 col_b = 0.0 col_r_add = 3.0 col_g_add = 0.8 col_b_add = 0.5 } else { ; 黄色い円 col_r = 1.0 col_g = 1.0 col_b = 0.0 col_r_add = 2.0 col_g_add = 1.8 col_b_add = 0.8 } ; 少しずつ色を変えて円を描画して、ぼけた円にする x0 = 0.0 x1 = 255.0 repeat 120 gsel img_id color col_r, col_g, col_b circle x0, x0, x1, x1, 1 col_r += col_r_add col_g += col_g_add col_b += col_b_add if col_r >= 256 : col_r = 255 if col_g >= 256 : col_g = 255 if col_b >= 256 : col_b = 255 x0 += 1.0 x1 -= 1.0 loop return