HSPポータル
サイトマップ お問い合わせ


HSPTV!掲示板


未解決 解決 停止 削除要請

2012
0501
暇人WinXP環境のウィンドウ描画でティアリング(チラツキや波打つ現象)を無くすモジュール3解決


暇人

リンク

2012/5/1(Tue) 02:04:02|NO.46392

使ってるのはDirectX9のGetRasterStatusですが・・・
DX8以前にも機能は同じ(中身も同じらしいけど)命令は
あったけど環境依存で、使えなかったりCRTだとリフレッシュレートが
60じゃなかったりで、殆ど使えなかった

今は液晶が主流で大抵ウィンドウモードでも60なのと
DX9対応のチップ積んでれば、結構GetRasterStatusが
使えるみたいなので作ってみた(Vista以降だとOS側でティアリング対策してるらしいが・・・)


#module "mod_d3d9vsync" #uselib "d3d9" #cfunc Direct3DCreate9 "Direct3DCreate9" int #usecom IDirect3D9 "{81BDCBCA-64D4-426d-AE8D-AD0147F4275C}" #comfunc D3D9_GetAdapterDisplayMode 8 int, int #comfunc D3D9_GetDeviceCaps 14 int ,int,int,int #comfunc D3D9_CreateDevice 16 int, int, int, int, int, int #usecom IDirect3DDevice9 "{D0223B96-BF7A-43fd-92BD-A43B0D82B9EB}" #comfunc IDirect3DDevice9_GetRasterStatus 19 int,int #comfunc _D3DDV9_Reset 16 int #define D3DDEVTYPE_HAL 1 #define D3DDEVTYPE_REF 2 #define D3DDEVTYPE_SW 3 #define D3DDEVTYPE_NULLREF 4 #define D3DSWAPEFFECT_DISCARD 1 #define D3DSWAPEFFECT_FLIP 2 #define D3DSWAPEFFECT_COPY 3 #define D3DSWAPEFFECT_FORCE_DWORD 0xFFFFFFF #define D3DCREATE_HARDWARE_VERTEXPROCESSING 0x00000040 #define D3DCREATE_SOFTWARE_VERTEXPROCESSING 0x00000020 #define DDCAPS_READSCANLINE 0x00020000 #define D3DCREATE_FPU_PRESERVE 0x00000002 #define D3DCREATE_MULTITHREADED 0x00000004 #define ctype D3DPS_VERSION(%1,%2) (0xFFFF0000|((%1)<<8)|(%2)) #define ctype D3DVS_VERSION(%1,%2) (0xFFFE0000|((%1)<<8)|(%2)) #define ctype D3DVP_VERSION(%1) (((((%1))>>8)&$ff)),((%1&$ff)) #uselib "KERNEL32.DLL" #func _Sleep "Sleep" sptr #uselib "winmm.dll" #cfunc _timeGetTime "timeGetTime" #func _timeBeginPeriod "timeBeginPeriod" sptr #func _timeEndPeriod "timeEndPeriod" sptr #define global D3D9WIN_ID 9999 //D3D9_CreateDevice用ウィンドウID #define global d3d9vsync_cpu cpu@mod_d3d9vsync //1フレームのCPU使用率 #define global d3d9vsync_cpu10 cpu10@mod_d3d9vsync //10フレームのCPU平均使用率 #define global d3d9vsync_ptime Processing_time@mod_d3d9vsync //1フレームの処理時間 #define global d3d9vsync_fps fps@mod_d3d9vsync //1秒間のフレーム数 #define global d3d9vsync_ftime fps_time@mod_d3d9vsync //1フレームの秒間 #define global d3d9vsync_err d3d9err@mod_d3d9vsync //D3DERR //D3DDV9_Reset //D3DDV9リセット #deffunc D3DDV9_Reset if (vsyncoff\2)=0 { D3D9_GetAdapterDisplayMode objd3d, 0, varptr(D3DFORMAT) if D3DFORMAT(2)>0 {RefreshRate=D3DFORMAT(2)}else{RefreshRate=60} _D3DDV9_Reset objd3ddv,varptr(d3dpp) vsyncon=bak_vsyncon vsyncoff=0 if (set_fps = RefreshRate) {vsyncon=0}else{if vsyncon=0 {vsyncoff=2} } gosub *timeinit } return //RasterAdjust 修正値 //フルスクリーン時の走査線監視位置修正 修正値省略時20(最終ラインより20手前の走査線を監視,リフレッシュレート60でY解像度1080の場合) //修正値を省略した場合statに現在の設定値が返る #define global RasterAdjust(%1=Raster_adjust@mod_d3d9vsync) _RasterAdjust %1 #deffunc _RasterAdjust int p1 Raster_adjust=p1 return Raster_adjust //d3d9vsync_init フレームレート , 走査線監視モード優先フラグ //d3d9各種初期化 フレームレート省略時60(0以下を指定するとリフレッシュレートが使われる) //リフレッシュレート優先フラグ:1の時フレームレートとリフレッシュレートが違っても走査線監視モードを設定 #define global d3d9vsync_init(%1=60,%2=0) vsyncon@mod_d3d9vsync=%2:_d3d9vsync_init %1 #deffunc _d3d9vsync_init int _set_fps bak_ID = ginfo(3) bak_vsyncon=vsyncon desktop_sizey=ginfo(21) if set_fps {d3d9vsync_end} _timeBeginPeriod 1 RefreshRate=60 //とりあえずリフレッシュレート60にしとく if _set_fps<=0 {set_fps=60}else{set_fps=_set_fps} Raster_adjust=20 //フルスクリーン時の走査線監視位置修正値 Sleep_adjust_full=1 //フルスクリーン時スリープ修正値 dim D3DFORMAT, 4 dim D3DCAPS9,128 dim RasterStatus,2 d3dpp = 0, 0, D3DFORMAT(3), 1, 0, 0, D3DSWAPEFFECT_DISCARD, 0, 1, 0, 0, 0, 0, 0 p = 0 d3d9err=0 vsyncoff=0 if varptr(Direct3DCreate9) = 0 {vsyncoff=1: gosub *timeinit: return -1 } //Direct3DCreate9が無いのでタイマーモードを設定して初期化終了 newcom objd3d, , -1, Direct3DCreate9(32 | 0x80000000) D3D9_GetAdapterDisplayMode objd3d, 0, varptr(D3DFORMAT) if D3DFORMAT(2)>0 {RefreshRate=D3DFORMAT(2)}//リフレッシュレートが読み取れない場合60を設定 if _set_fps<=0 {set_fps=RefreshRate}//フレームレートに0以下が指定されたのでリフレッシュレートをフレームレートに D3D9_GetDeviceCaps objd3d,0,D3DDEVTYPE_HAL,varptr(D3DCAPS9) if stat<0 { D3D9_GetDeviceCaps objd3d,0,D3DDEVTYPE_REF,varptr(D3DCAPS9)//そもそもD3DDEVTYPE_REFだとDDCAPS_READSCANLINEに対応してないかも if stat<0 {d3d9err=stat:vsyncoff=1: gosub *timeinit: return -2} //D3D9_GetDeviceCapsが失敗した } if (D3DCAPS9(2)&DDCAPS_READSCANLINE) = 0 {vsyncoff=1: gosub *timeinit: return -3} //水平走査線取得非対応の場合タイマーモードを設定 FPUtes=str(9876543210.98745+1) FPU=(peek(FPUtes,15) = '5') //現在のFPU精度をテスト、小数点以下の'5'を読み取れなかったら単精度 bgscr D3D9WIN_ID,1,1,2 BF_SV=D3DCREATE_SOFTWARE_VERTEXPROCESSING | D3DCREATE_FPU_PRESERVE*FPU //大抵初期化できるSOFTWAREにする D3D9_CreateDevice objd3d, 0, D3DCAPS9 , hWnd, BF_SV, varptr(d3dpp), varptr(p) d3d9err=stat gsel bak_ID if d3d9err<0{ vsyncoff=1:gosub *timeinit: return -4} //D3D9_CreateDeviceが失敗した newcom objd3ddv, , -1, p if vsyncon=0 {//走査線監視モード優先フラグOFF if set_fps ! RefreshRate {//指定フレームレートとリフレッシュレートが違う場合タイマーモードを設定 vsyncoff=2: gosub *timeinit: return -5 //タイマーモード選択 } }else{ //走査線監視モード優先フラグON 指定フレームレートとリフレッシュレートが違ったらタイマーモード+走査線監視モードを設定 if (set_fps = RefreshRate) {vsyncon=0}//同じなら通常の走査線監視モードを設定 } gosub *timeinit return RefreshRate *timeinit Refresh_time=1000.0/RefreshRate fps_time=1000.0/set_fps timA=_timeGetTime() Start_tim=timA timB=timA timC=timA fs_tim=0.0 return //GetRasterStatus 垂直帰線期間フラグ, 水平走査線数 //水平走査線取得 #deffunc GetRasterStatus var V,var R //水平走査線取得 if (vsyncoff\2)=0 { IDirect3DDevice9_GetRasterStatus objd3ddv,0,varptr(RasterStatus) V=RasterStatus //1=垂直帰線期間中 R=RasterStatus(1) //現在の走査線、上が1の時0になる(環境によるかも知れない) } return //d3d9vsync 走査線監視フラグ //垂直同期 フラグに1を指定するとタイマーモード(省略時は0走査線監視モード) #deffunc d3d9vsync int _vsyncoff fps_cnt++ if (ginfo(2) ! 0) or (desktop_sizey ! ginfo(21)) {//非アクティブになった、解像度が変更された Reset_f=1 //D3DDV9リセット準備フラグセット }else{ if Reset_f=1 { //リセット準備フラグが立ってから1フレーム経過した Reset_f=2 //リセットフラグセット(アクティブになってから1フレーム待つため) }else{ if Reset_f=2{//リセットフラグが立っている D3DDV9_Reset Reset_f=0 } } } to=t Rasterok_f=0 Processing_time10+Processing_time if (fps_cnt\10)=0 {cpu10=(double(Processing_time10)*10)/fps_time:Processing_time10=0} cpu=double(Processing_time*100)/fps_time desktop_sizey=ginfo(21) adjust=((60.0/double(RefreshRate))*(double(desktop_sizey)/1080))//解像度とリフレッシュレートによって1ラインの時間が変るので係数を出す Raster_ms=double(desktop_sizey)/Refresh_time//1msで移動する走査線数 if (ginfo(7)<ginfo(21)) and (ginfo(7)>0) {WinRaster=ginfo(7)}else{WinRaster=ginfo(21)}//ウィンドウ下部位置とデスクトップYサイズを比較して低い方をラスター比較位置にする dup Raster,RasterStatus(1) WinUpRaster=limit(ginfo(5)-adjust*10,0,ginfo(5)) if (_vsyncoff | vsyncoff | Reset_f)=0 and vsyncon=0 { //水平走査線監視処理ON if ginfo(13)>=ginfo(21) {Sleep_adjust=Sleep_adjust_full } //フルスクリーン時スリープ修正値 timA = _timeGetTime() Processing_time = timA - timB //前回監視開始からの現在までの経過時間(監視期間も含まれるからメイン処理時間より1ms程度余分になる) sleep_tim=limit(fps_time-((timA - timC))-Sleep_adjust,0,fps_time)//スリープ時間(前回監視位置を超えたらSleep_adjust分短く) if sleep_tim>0 {_Sleep sleep_tim}//スリープ timB=_timeGetTime() if ginfo(13)>=ginfo(21) {//ウィンドウサイズがデスクトックサイズ以上(フルスクリーンとして処理開始) _WinRaster=WinRaster-(adjust*Raster_adjust) IDirect3DDevice9_GetRasterStatus objd3ddv,0,varptr(RasterStatus) if RasterStatus=1 or Raster=0 or (Raster>=_WinRaster) { }else{ Raster_bak=Raster repeat IDirect3DDevice9_GetRasterStatus objd3ddv,0,varptr(RasterStatus) if RasterStatus=1 or Raster=0 or (Raster>=_WinRaster) {Rasterok_f=1:break} if (cnt\100)=99 {if Raster_bak=Raster{break}else{Raster_bak=Raster}}//無限ループ回避策(100回ループしても同じ走査線数だったら抜ける)多分必要ないだろうけど・・・ loop } }else{ IDirect3DDevice9_GetRasterStatus objd3ddv,0,varptr(RasterStatus) if Raster>=WinRaster { Sleep_adjust=limit((Raster+Raster_ms-WinRaster)/Raster_ms,1,fps_time)//スリープ終了後すぐに監視位置を越えてるので次回のスリープを短くする修正値 }else{ Sleep_adjust=0 Raster_bak=Raster repeat IDirect3DDevice9_GetRasterStatus objd3ddv,0,varptr(RasterStatus) if (RasterStatus=1 or Raster=0 or Raster>=WinRaster) {Rasterok_f=1:break} if (cnt\100)=99 {if Raster_bak=Raster{break}else{Raster_bak=Raster}} loop } } fs_tim=0.0 timC=_timeGetTime() Start_tim=timC }else{ //タイマー処理ON fs_tim+fps_time //1フレームの時間を足して次フレームスタート時間にする if fs_tim > 50 {Start_tim+50:fs_tim-50} //HSPDX使用時は単精度になるから桁数を抑える timB=_timeGetTime() sms=fs_tim-(timB-Start_tim) //次フレームスタート時間からスタートからの時間を引いてスリープ時間にする(結果が負なら1フレームの時間を越えた) if sms < 0 {fs_tim-sms} //1フレームの時間以上使用したからオーバー分をフレームスタート時間に加算 _Sleep limit(sms,0,fps_time+1) //小数点以下のスリープは出来ないから最大スリープ時間を+1する if (vsyncoff\2)=0 and Reset_f=0 {gosub *GetRaster}//GetRasterStatusが使えるなら使う Processing_time=timB-timC//1フレームのスリープ時間を抜いた時間(処理に掛かった時間) timC=_timeGetTime() } t=timC/1000 if t ! to {d3d9vsync_fps=fps_cnt-fps_cnt_old:fps_cnt_old=fps_cnt} return Rasterok_f //これが1の場合ティアリングが起きる可能性が高い(水平走査線取得に対応してる場合) *GetRaster IDirect3DDevice9_GetRasterStatus objd3ddv,0,varptr(RasterStatus) if (Raster<=WinUpRaster or Raster>=WinRaster) {//メインウィンドウ外なら抜ける return }else{ if _vsyncoff>0 or vsyncon=0 {Rasterok_f=1:return }//タイマーモードなのですぐに抜ける Raster_bak=Raster repeat IDirect3DDevice9_GetRasterStatus objd3ddv,0,varptr(RasterStatus) if (Raster<=_WinRaster or Raster>=WinRaster) {Rasterok_f=1:break} if (cnt\100)=99 {if Raster_bak=Raster{break}else{Raster_bak=Raster}} loop } return #deffunc d3d9vsync_end onexit _timeEndPeriod 1 if vartype(objd3ddv)=6 {delcom objd3ddv} if vartype(objd3d)=6 {delcom objd3d} return //d3d9vsync_logsave //ログ保存 #deffunc d3d9vsync_logsave D3DCAPS9_log="-------------------------------\n D3DDISPLAYMODE\n-------------------------------\n" repeat 4 D3DCAPS9_log+=strf("%2d : %d\n",cnt,D3DFORMAT(cnt)) loop D3DCAPS9_log+"\n-------------------------------\n D3DCAPS9\n-------------------------------\n" repeat 76 if cnt=62 {D3DCAPS9_log+"D3DVSHADERCAPS2_0 VS20Caps{\n"} if cnt=66 {D3DCAPS9_log+"D3DPSHADERCAPS2_0 PS20Caps{\n"} D3DCAPS9_log+=strf("%2d : %s",cnt,str(D3DCAPS9(cnt))) if cnt=49 {D3DCAPS9_log+strf(" VertexShaderVersion(%d,%d)",D3DVP_VERSION(D3DCAPS9(cnt)))} if cnt=51 {D3DCAPS9_log+strf(" PixelShaderVersion(%d,%d)",D3DVP_VERSION(D3DCAPS9(cnt)))} if cnt=65 or cnt=69 {D3DCAPS9_log+"\n }\n"}else{D3DCAPS9_log+"\n"} loop D3DCAPS9_log+"\n-------------------------------\n D3DPRESENT_PARAMETERS\n-------------------------------\n" repeat 14 D3DCAPS9_log+=strf("%2d : %d\n",cnt,d3dpp(cnt)) loop notesel D3DCAPS9_log notesave "d3d9vsync.log" return #global

基本的に以下の3行追加で終わり
#include "mod_d3d9vsync.hsp"
d3d9vsync_init //初期化
d3d9vsync //垂直同期


モジュールをmod_d3d9vsync.hspとして保存したとして使用例

#include "mod_d3d9vsync.hsp" errmes="","Direct3DCreate9が無い","D3D9_GetDeviceCapsが失敗した","水平走査線取得非対応","D3D9_CreateDeviceが失敗した","タイマーモード選択" buffer 1 cls 4 color 200,200,200 repeat 5 boxf 64*cnt*2,0,64*cnt*2+64,480 loop gsel 0 //フレームレート60を設定しリフレッシュレートに関係なく走査線監視モード //リフレッシュレートがフレームレートと違うとスクロールがガタツク(その代わりティアリングが起きない) d3d9vsync_init 60,1 //メインウィンドウの初期化が終ってから実行 _stat=stat title ""+_stat //1以上なら走査線監視モードでリフレッシュレートが入ってる、負数ならタイマーモード(d3d9の初期化に失敗したかレート違いでタイマーを選択) if _stat<0 {dialog errmes(abs(_stat))} ;d3d9vsync_logsave //カレントフォルダにd3d9のログを保存(d3d9vsync.log) s=8 repeat redraw 0 stick k if k&128 {end}//Esc(終了) if k&32 {//Enter(フルスクリーン) winmode^1 if winmode=1{bgscr 0,640,480,0,0,0}else{screen 0,640,480,0,100,100} chgdisp winmode } pos 0,0 :gcopy 1,(cnt*s)\640,0,640-(cnt*s)\640,480 pos 640-(cnt*s)\640,0 :gcopy 1,0,0,(cnt*s)\640,480 color 100,100,100 :boxf 5,5,200,100 color ,255 pos 10,10 mes strf("d3d9vsync_fps(%d)",d3d9vsync_fps) mes strf("d3d9vsync_ptime(%2d)",d3d9vsync_ptime) mes strf("d3d9vsync_cpu10(%.1f%%)",d3d9vsync_cpu10) mes strf("d3d9vsync_cpu(%.1f%%)",d3d9vsync_cpu) mes strf("RasterStatus(%d,%4d)",v,r) //1を指定するとタイマーモード d3d9vsync //必ずメインウィンドウがgselで選択された状態で実行する事 GetRasterStatus v,r redraw 1 await 0 loop
フルスクリーンでも一応使えるけど
ちょいシビアだからRaster_adjustで微調整が必要かもしれない



この記事に返信する


暇人

リンク

2012/5/1(Tue) 02:18:18|NO.46393

HSPDXやhgimg3使用時も使えた

HSPDXの例

#include "HSPDX.AS" #include "mod_d3d9vsync.hsp" es_ini es_screen 640,480,32,,1,1 d3d9vsync_init 60 buffer 1,1024,1024 repeat 8 hsvcolor cnt*23,255,255-50*(cnt\2)-50 boxf cnt*80,0,cnt*80+80,480 loop es_buffer 0,2,,,1 ;RasterAdjust 10 //フルスクリーンの時は初期値20のRasterAdjustを10ぐらいにするとティアリングが消えた //(まぁ、フルスクリーンならes_syncで同期取った方が早いけど・・・) gsel 0,1 s=5 repeat gsposx=cnt*s repeat rnd(10)+1 pos 0,0 :es_excopy 0,gsposx\640,0,640-gsposx\640,480,640-gsposx\640,480 pos 640-(gsposx\640),0:es_excopy 0,0,0,gsposx\640,480,gsposx\640,480 pos 10,10 :es_mes strf("FPS(%2d) TIM(%2d ms) CPU(%.1f%%)", d3d9vsync_fps,d3d9vsync_ptime, d3d9vsync_cpu10) loop d3d9vsync es_sync 0,1 es_bufcheck await 0 stick k if k&128 : end; [ESC]で終了 loop



暇人

リンク

2012/5/1(Tue) 02:46:38|NO.46395

hgimg3の使用例

#include "hgimg3.as" #include "mod_d3d9vsync.hsp" gx=640 gy=480 hgsetreq SYSREQ_FPUPRESERVE ,1 hgini clscolor $40 setfont 16,16,12,0 texload "fontchr.bmp" d3d9vsync_init 60,1 buffer 1,1024,1024 repeat 16 hsvcolor cnt*23,255,255-50*(cnt\2) boxf cnt*80,0,cnt*80+80,1024 loop settex 1024,1024,0,-1 mest = stat gsel 0,1 redraw 0 repeat hgdraw gsposx=cnt*5 gmode 0,gx-(gsposx\gx),gy :pos gx/2-(gsposx\gx)/2,gy/2 :hgrotate mest,(gsposx\gx),0,0 gmode 0,(gsposx\gx),gy :pos gx-(gsposx\gx)/2,gy/2 :hgrotate mest,0,0,0 fprt strf("%d %d %.1f",d3d9vsync_fps,d3d9vsync_ptime,d3d9vsync_cpu10),8,8 d3d9vsync 0 hgsync 0 await 0 stick k if k&128 :end ; [ESC]で終了 loop


モジュールの説明で書き忘れ
ウィンドウが非アクティブになると走査線監視モードは一時解除される



暇人

リンク

2012/5/2(Wed) 03:46:13|NO.46405

リフレーシュレートにフレームレートを合わせて同期を取りゲームスピードを変えない例

#include "mod_d3d9vsync.hsp" #define Standard_FPS 60.0 //基準にするフレームレート #const double FPS_TIM (1000.0/Standard_FPS) //1フレームの時間 buffer 1 cls 4 color 150,150,150 repeat 5 boxf 64*cnt*2,0,64*cnt*2+64,480 loop gsel 0 //フレームレートをリフレッシュレートと同じにして、走査線監視モード設定 d3d9vsync_init -1,1 title ""+stat //{基準FPSをリフレッシュレートで割って1フレームの時間率を出す}else{1フレームの時間を基準の1フレームの時間で割って1フレームの時間率を出す} //1.0/リフレッシュレートにしないのは個人的好み(FPS60の1フレームで何ドット動いてって考える方が慣れてるから) if stat>0{timrate=Standard_FPS/stat}else{timrate=d3d9vsync_ftime/FPS_TIM} //昔はリフレッシュレート取得できない環境があったけど今はどうなんだろう・・・ my_smax=20 dim my_sf,my_smax ddim my_spx,my_smax ddim my_spy,my_smax my_px=200.0 my_py=200.0 myspd=3.0*timrate mysspd=10.0*timrate scrspd=4.0*timrate scrx=0.0 gtime=0.0//ゲーム時間 lcnt=0.0//ループカウンタ repeat redraw 0 stick k,$1f if k&128 {end}//Esc(終了) if k&32 {//Enter(フルスクリーン) winmode^1 if winmode=1{bgscr 0,640,480,0,0,0}else{screen 0,640,480,0,100,100} chgdisp winmode } scrx+scrspd pos 0,0 :gcopy 1,(scrx)\640,0,640-(scrx)\640,480 pos 640-(scrx)\640,0 :gcopy 1,0,0,(scrx)\640,480 color 100,100,100 :boxf 5,5,200,100 if k&1 {my_px-myspd} if k&2 {my_py-myspd} if k&4 {my_px+myspd} if k&8 {my_py+myspd} color ,255 boxf my_px-8,my_py-8,my_px+24,my_py+24 if k&16 { if (int(lcnt)-scnt)>10 { no=mysmax repeat mysmax if my_sf(cnt)=0{//空きを探す no=cnt break } loop if mysmax<my_smax { my_spx(no)=my_px my_spy(no)=my_py my_sf(no)=1 if no=mysmax { mysmax+1 } scnt=int(lcnt) } } }else{ scnt=0 } color 255 repeat mysmax if my_sf(cnt){ if my_spx(cnt)<640{ my_spx(cnt)+mysspd boxf my_spx(cnt),my_spy(cnt),my_spx(cnt)+16,my_spy(cnt)+16 }else{ my_sf(cnt)=0 } } loop color ,255 pos 10,10 mes strf("d3d9vsync_fps(%d)",d3d9vsync_fps) mes strf("d3d9vsync_ptime(%2d)",d3d9vsync_ptime) mes strf("d3d9vsync_cpu10(%.1f%%)",d3d9vsync_cpu10) mes strf("gtime(%f)",gtime) mes strf("lcnt(%d)",int(lcnt)) d3d9vsync //必ずメインウィンドウがgselで選択された状態で実行する事 redraw 1 await 0 gtime+d3d9vsync_ftime //ゲーム時間 lcnt+timrate//ループカウントの代わり(これが1になった時が基準FPS上で1フレーム経過したのと同じ) loop



ONION software Copyright 1997-2023(c) All rights reserved.