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


HSPTV!掲示板


未解決 解決 停止 削除要請

2019
0815
とあるプログラマawait/waitはちゃんと待ってくれない6解決


とあるプログラマ

リンク

2019/8/15(Thu) 19:23:39|NO.88056

HSPのawait/wait命令は待機中にメッセージ割込みが入ると戻ってきたときに残りの時間に関わらず次の処理に移ると思います。
(例:wait 100で1秒間待機を設定しても0.2秒の時点でメッセージが来てジャンプ先から戻ってくると残りの0.8秒は待たずに次行に移る)

そこそこの規模のプログラムになるとonkeyやoncmd等の割込みは増えてくると思いますが、repeat〜loop等での待機で、特に時間が重視される部分だとこの仕様はかなり致命的になってきます。
kernel32のsleep関数は割込みが来ないので純粋に待ってくれますが、全ての割込みがこないので待機時間が長いとフリーズのような挙動になると思います。


#include "kernel32.as" #include "user32.as" ;#include "constant.as" // 割り込んでも待ってくれる命令モジュール #module #deffunc await2 int time StartTick=GetTickCount() repeat await 10 if (GetTickCount()-StartTick) >= time : break loop return #global // モジュール終わり #define global WM_TIMER 0x00000113 screen 0 oncmd gosub *timer,WM_TIMER SetTimer hwnd,1,10,0 ;確認のためにWM_TIMERで割り込む i=0 _cnt=0 repeat /* いろいろな命令 */ _cnt++ await2 1000 ;repeat〜loopは1秒(1000ms)ごとに繰り返す loop stop *timer i++ redraw 0 color 255,255,255 boxf color pos 0,0 mes "repeat-loop回数:"+_cnt mes "割込み回数:"+i redraw 1 return

このように独自の命令を定義してしまえば(詳細な精度は置いといて)望み通りの動作はしてくれますが、なんか無理やり感が…
もう少し利口(?)な方法ってあるんですかね。



この記事に返信する


ham

リンク

2019/8/15(Thu) 22:38:54|NO.88062

単純に


//ピッタリ10秒待つ repeat 100 Sleep 100 await 0 loop

だけでも十分いい感じだと個人的には思いますけどどうなんでしょ。
Sleep中は絶対に割り込まない時間が生まれてawait 0で割り込むのでフリーズしない&戻らない。
タイトルバー掴んだら止まっちゃいますけど..w



とあるプログラマ

リンク

2019/8/16(Fri) 13:49:19|NO.88077

通常のawait利用でもそうなんですが、sleep&awaitのみで待機すると誤差が出るですよね…
redraw 0〜redraw 1等の再描画処理はさむと誤差がかなり目立つ気がするんで、やっぱりawait/waitで時間系の処理はやめたほうがいいですね。

単純にループで使う程度ならawait&sleepでいいのかな。(自分もこの組み合わせで使ってます)



ham

リンク

2019/8/16(Fri) 16:53:39|NO.88080

確かに誤差出ちゃいますよねぇ。
やはり時間を計測して調整してやるのが一番ですね。

ちなみにですがGetTickCountは精度が悪いっぽいので

#uselib "winmm"
#cfunc timeGetTime "timeGetTime"

こっちのほうがいいかもですよ。
自分の環境ではtimeGetと比べてGetTickだと8msくらい誤差がありました。



とあるプログラマ

リンク

2019/8/17(Sat) 18:24:48|NO.88097

なるほど、そういう関数もあるのですね。しかしミリ秒単位だとどうしても誤差は生まれてしまいますよね。自分は時間に厳しいソフトは作っていないので問題はないですが、この辺はHSPに限らず課題なんですかね…(特にゲームだと結構な課題な気が)



MIZUSHIKI

リンク

2019/8/18(Sun) 12:45:58|NO.88116

私も今作っているモジュールに同様の課題があり試行錯誤しています。

「waitした時の時間を記録しておき、onkey等によってwait抜けしたとき残ってる時間はないか計算して残ってたらその分再waitする」
というアプローチをとってみました。


#define ctype _nowTime (gettime(4)*3600000+gettime(5)*60000+gettime(6)*1000+gettime(7)) //標準命令で完結したかったためgettimeを使用 #define waiton(%1) :\ _waitTime_ = _nowTime() :\ while 1 :\ _nokoriTime_=%1-(_nowTime() - _waitTime_)/10 :\ if(%1<_nokoriTime_){ _nokoriTime_-=24*360000 : _waitTime_-=24*360000 } :\ if(_nokoriTime_<=0){ _break } :\ wait _nokoriTime_ :\ wend :\ _awaitTime_=_nowTime() #define awaiton(%1) :\ if(_awaitTime_=0){ _awaitTime_ = _nowTime() } :\ while 1 :\ _nokoriTime_=%1-(_nowTime() - _awaitTime_) :\ if(%1<_nokoriTime_){ _nokoriTime_-=24*3600000 : _awaitTime_-=24*3600000 }:\ if(_nokoriTime_<=0){ _break } :\ await _nokoriTime_ :\ wend :\ _awaitTime_=_nowTime() //以下、テスト onclick gosub *click onkey gosub *key sysfont 17 repeat ,1 awaiton 1000 ntime = _nowTime() mes "" :pos 0, mes strf("%02d: %d(%d)", cnt, ntime, ntime-mae_ntime ),1 mae_ntime = ntime loop stop *click mes " click", 1 return *key mes " key",1 return

うーん、なんとなく問題無いように思うのですがどうでしょうか。
精度的なところは、、、テスト部分のawaitonをawaitにしてもほとんど違いが無いから良い(?)のかな・・・。(その時は当然keyもclickもしないでください)
有識者の判断が必要かもしれません。


whileを使ってるのは#defineのユニークラベル名を利用したgotoループを簡単に使うためです。
詳細はF1ヘルプ(#define)とhspdef.as(while,wend,_break)を参照してください。

また、#defineを利用しておりコメントを入れられませんでした。
各行コメント載せ版を下に書いてみたのでスクリプトの説明はこちらを参照ください。


#define waiton(%1) :\ _waitTime_ = _nowTime():/*waitに入る前に現在の時間を記憶する。*/\ while 1 :/* ユニークラベル名gotoを用いたループにwhileを利用 */\ _nokoriTime_=%1-(_nowTime() - _waitTime_)/10 :/*onKey等によってwaitを抜けたとき戻ってくる。残りを計算して再waitする。*/\ if(%1<_nokoriTime_){ _nokoriTime_-=24*360000 : _waitTime_-=24*360000 }:/* 日をまたいで_nowTimeが0に戻った時補正する。 */\ if(_nokoriTime_<=0){ _break } :/* waitで指定した時間経過していたらループから抜ける */\ wait _nokoriTime_ :/* 1回目は指定した時間waitして、2回目以降は残り時間waitさせる。(例:wait 100 →[0.2秒でonkey抜け]→ wait 80) */\ wend :\ _awaitTime_=_nowTime() /*awaitのためにwait抜けした時間を記憶*/



とあるプログラマ

リンク

2019/8/18(Sun) 13:24:36|NO.88120

>>MIZUSHIKIさん
1秒や0.5秒での待機ならこれでも問題ないとは思うんですけどねー。ただ100ms以下のawaiton 50やawaiton 10だとやはり遅延出てきます。
当方の環境でawaiton 50×200、awaiton 10×1000の10秒待機した結果 それぞれ 0.2秒、1.0秒の遅延が発生しました。再描画等なしのawaitonのみです。

ただもうこれはawait含めプログラム全般の仕様同然なので、時間に厳しいゲームとかでなければ気にするものではない気が…
どうしても遅延を許さない場合なら初回待機時にシステム時間取得して以後はそれを元に待機時間で割るとかしないと無理だと思いますね。(要は通常だと命令ごとに僅かながら遅延が発生するものを、初回時をベースにすることで命令ごとの遅延を取り除く)

ですが前述のとおり、よほど時間を重視するプログラムじゃなきゃMIZUSHIKIさんの命令で十分だと思いますね…



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