- ベストアンサー
MFCのタイマーのつかい方を教えてください
タイマーのつかい方が今ひとつ分かりません。 MFCでタイピングのゲームを作成しているのですが、 25問を解き、正解だった場合もしくは制限時間を超えてしまった場合、次の問題を表示したいと思っております。 下記がプログラム内容です。 void CProgramView::Loop1(CDC* pDC) { CProgramDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); if((m_nRight<26)|(0<m_nRight)) { m_nRight=0; //正解数 m_nQuestion=1; //問題数 } Haikei(pDC); //問題表示領域の枠表示 Tokei(pDC); //時計の秒針が表示される枠表示 Moji(pDC); //問題文表示 OnChar(ch, count,flags); //入力 if(m_fTimer==FALSE) { SetTimer(123,250,NULL); //タイマーをセット if(m_nQuestion<26) //25問以上問題を解いていないケース { m_fTimer = FALSE; } else m_fTimer = TRUE; //全問解いた場合 } Loop0(pDC); //秒針の描画クラス if(m_nx==715) //タイムアウトだった場合 { KillTimer(123); NGPaper(pDC); m_nQuestion++; //問題をカウント m_sAnser.Empty(); //回答文字列をクリア pDoc->GetNextSet(); //次の問題を取得する InvalidateRect(NULL); } if(m_nQuestion<m_nCount) //正解だった場合 { KillTimer(123); //タイマーを切る Tokei(pDC); //秒針の画像を消すために時計の画面を再描画 Right(pDC); //正解した場合の画像を描画 Haikei(pDC); //問題文・回答を消すために問題表示領域の枠を再描画 PartsPaper1(pDC); //正解した場合の壁紙を表示 m_nQuestion++; //問題数をカウント m_sAnser.Empty(); //回答文字列をクリアする pDoc->GetNextSet(); //次の問題を取得する InvalidateRect(NULL); } } そして、この動作を25問、解くまでループさせる関数として以下の関数を作成しました。 void CProgramView::Loop2(CDC* pDC) { if(m_fTimer==FALSE) { Loop1(pDC); } } //タイマーの内容 void CProgramView::OnTimer(UINT nIDEvent) { // TODO: この位置にメッセージ ハンドラ用のコードを追加するかまたはデフォルトの処理を呼び出してください if(nIDEvent == 123) { InvalidateRect(NULL); } CView::OnTimer(nIDEvent); } しかし、実際にこのプログラムを実行すると入力し、正解する間は次の問題が表示されるのですが、タイマーが切れません。 そして、制限時間になるとそこまで解いていた問題から凄い勢いで描画が始まり、止まりません。 おそらくタイマーが正常に使えていないという可能性が考えられるのですが・・・。 希望としては、25問を順次解き、解き終わった後は画像を描画し、次の問題を表示したいのですが、どうしたら良いか教えてください。
- みんなの回答 (3)
- 専門家の回答
質問者が選んだベストアンサー
Windowsアプリの場合 WM_何とかといったメッセージをWindowsから受け取って動作する仕組みになっています ボタンを押したのであれば WM_COMMANDとどのボタンかのパラメータ 再描画なら WM_PAINTといった具合です このメッセージポンプは CWinAppの派生クラスに実装されています ですからボタンを押した際の処理を OnButton1 などのイベントハンドラと メッセージ振り分けようのメッセージマップでといった具合でMFCは構成されています このポンプは1つ(または複数)の関数から抜け出た後に戻ることになっています 関数内ではポンプ機能は働きません void OnButton1() { CString ss; for( int n = 0; n <= 100; n++ ) { ss.Format( "%d", n ); m_Static1.SetWindowText( ss ); } } といった関数があった場合 作者は ラベルの表示を 0から100まで変化させたかったとしても 実際には 100しか表示されないということになります なぜなら コントロールの再描画がこの関数の中では一切そのチャンスがないからです // ボタンの押した時は カウンタをリセット、フラグをセット、タイマーをセットするだけ void OnButton1() { if ( m_bflag == FALSE ) { m_myCnt = 0; SetTimer( 100, 500, NULL ); } m_myFlag = TRUE; } //ここでメッセージポンプへ返る // 500msごとに呼び出されるタイマーイベントでラベルへの設定を変更する void OnTimer( UINT nIDEvent ) { if ( nIDEvent == 100 ) { CSTring ss; ss.Format( "%d" , m_myCnt ); // ラベルへデータを設定 m_Static1.SetWindowText( ss ); myCnt++; if ( myCnt == 100 ) { // カウンタが終了値になったなので タイマーを止め // フラグをリセット KillTimer( 100 ); m_bFlag = FALSE; } } } //次のイベント発生のためここでメッセージポンプへ返る といった具合に作成します
その他の回答 (2)
- redfox63
- ベストアンサー率71% (1325/1856)
Windowsのタイマーを使うのであれば メッセージポンプを動作させないといけないと思います メッセージポンプとはウィンドウズの基本システムです GetMessageやPeekMessage、DiapatchMessage/TranslateMessage などで構成します MFCを使うなら AfxGetApp()->PumpMessage(); でいいようです 流れとしては 1) 問題文の選定 1-2) タイマーを設定 SetTimer 2) 入力値のチェック 2-1) 正解/不正解時のカウント 2-2) タイマーの停止 2-3) 25問未満の場合 1)へ戻って処理続行 3) タイマーイベントで時間切れの処理 3-1) 25問未満の場合 1)へ戻って処理続行 といった関数を用意して、それぞれの処理後関数から抜けるようにしましょう
- Oh-Orange
- ベストアンサー率63% (854/1345)
★よく読んでいませんが…。 ・単純に経過時間を計測したいのなら GetTickCount() 関数を使います。 (他にもGetTickCount64、timeGetTime、QueryPerformanceCounterなど) サンプル: DWORD m_dwStart; ←メンバ変数に加える // 問題の出題開始時 m_dwStart = GetTickCount(); // 問題の出題終了時 DWORD dwDiff = (GetTickCount() - m_dwStart); if ( dwDiff < (3 * 60 * 1000) ){ 3分以内ならこちらで処理 } else{ 3分以上ならこちらで処理 } このような感じで分岐すれば良いでしょう。
お礼
ご解答ありがとうございます。 言葉が足りなく申し訳ございません。 時間計測がしたいというよりも、時間経過後の再度、最初の動作に戻り、ループさせたいのです。 お手数をおかけし申し訳ございません。
補足
ご解答ありがとうございます。 メッセージポンプ・・・!?根本的な問題が解決していないのだと気付かされました。 そこで大変、申し訳ないのですが、具体的なメッセージポンプの利用方法を教えていただけないでしょうか。