#pragma twice

KAB-studio > プログラミング > #pragma twice > 138 Version 7.18 タイマーで止めない!

#pragma twice 138 Version 7.18 タイマーで止めない!

前のページへ 表紙・目次へ 次のページへ

 Version 7.18
タイマーで止めない!

今回からは、アニメーションをするための方法を見ていきます
そーだね、今までは一枚絵だったもんね。それを動かすわけだ
あ、でも注意。今回から紹介するのは〈ちょっと劣る方法〉
劣るってどゆこと?
ちゃんとアニメーションさせたい場合には DirectX って機能を使わない
とダメ。今回から教えるのだと、たいしたアニメーションはできないから
ぶー、ならなんで教えるのよー
アニメーションが分かると、ウィンドウズのメッセージの仕組みとかが分
かるんだよね
つまりアニメーションってのは勉強のためのエサね
う、そういう言い方されると……
はいはい。で、どうする?
まずはそれを考えてみましょう。まず一番簡単な方法として、時間が経つ
に連れて IDC_S_CANVAS の色が少しずつ変わる、ってしていきたいんだけ

それだけなら簡単だよね。塗りつぶしは Ver 7.07 ( No.127 ) の 
FillRect() 使えばいいし、色変えるのはその時使うブラシの RGB の値変え
ればいいだけだし
そうそう。一番難しいのは少しずつ色を変える、つまりアニメーションさ
せてく部分
そりゃそうよね、それができたらもう教わってるってことだし
で、どうすればいい?
うーん……アニメーションってことは、繰り返し塗り潰すわけだから……
あ!  for を使う!
ぶっぶー
げ!
ま、実際にやってみましょう。いつものボタン押したら呼ばれるメンバ関
数に

void CAnimeDlg::OnBDraw() 
{
    CRect cRect;
    ::GetClientRect( m_cCanvasStatic.GetSafeHwnd(), &cRect );
    HDC hDC = ::GetDC( m_cCanvasStatic.GetSafeHwnd() );
    HBRUSH hBrush;
    int iRed = 0;
    
    for( int iF1 = 0; iF1 < 10000; ++iF1 )
    {
        hBrush = ::CreateSolidBrush( RGB( iRed, 0, 255 ) );
        ::FillRect( hDC, &cRect, hBrush );
        ::DeleteObject( hBrush );

        iRed += 1;
        if( iRed == 256 )
        {
            iRed = 0;
        }
    }
    ::ReleaseDC( m_cCanvasStatic.GetSafeHwnd(), hDC );
}

うん、 for でループして、中で iRed の値を増やしていって、それでブ
ラシ作るから少しずつ赤くなってく、って感じね
 iRed == 256 のところは、 RGB に渡せるのは 255 が最高だからね
そんなの分かるって。実行実行!
(……大丈夫、だよね……)
ビルドして実行してボタンを
……


……
あれ? 何も起きないよ
……やっぱり……ちゃんとアニメーションしてたんだけどね
うっそだー! なんともなってないもん
火美ちゃん、クロックタイムとかおかしくない?
え?……あれ? 処理時間と実時間が違う!
火美ちゃんその間寝てたからね
ね、寝てたぁ!? あたしいきなり昼寝してたの!?
そういうわけじゃないんだけどね。じゃ、このノートパソコンで試してみ
ようか。 Anime.exe をコピーして、カメラを向けてと。見える?
うん、ノート見えるよ
じゃ、実行!
げ、ホントに色が変わってる! すげー早さだけど。あ、終わった。でも
なんであたし気付かなかったの? 寝てたの?
寝てたっていうかねぇ。もう一度実行してみるね、で、その間に他のウィ
ンドウをクリックしてみると
あれ、反応しない!
これは、 for ループの中で処理してる間は、他のことができないから。
パソコンの中の CPU あるでしょ
考えるとこだね
そこが for ループにかかりっきりになっちゃうから、他が反応しなく
なっちゃうから、こうなっちゃうんです
かかりっきり!? つまり、他のウィンドウの面倒見なくなっちゃう?
他のウィンドウだけじゃないよ。ほら
げ、 Anime ダイアログが閉じない!
いろんなアプリが同時に動くことを〈マルチタスク〉って言うんだけど、
それだって CPU はひとつ。だから、 CPU は順番にアプリを見てまわってる
んです
それを for で独り占めしたら、他を見て回れなくなっちゃうわけね。で
もじゃあどうすればいいの?
それは、描く時だけ独り占めすればいいだけ。 for って実はすごく早く
回るんだよね
うん、さっきすごく早かった
でも実際のアニメーションにはそれほど早さは必要ないから。たとえば、
for が1秒間に1億周するとします
それすごい……
でもアニメーションは1秒間に60回も更新できれば十分
1億とはえらい違いね。あ、そっか、じゃあ1億−60の間は CPU に他
のアプリ見られるようにすればいいんだ
そういうこと! そこで、ある一定時間毎に CPU に見てもらうようにし
て、それ以外は CPU が他を見られるようにします。そのための仕組みが
〈タイマー〉っていうもの
タイマーは普通のタイマーよね、時間計ったりする
そうそう。では実際に使ってみましょう。えっと、まず下準備。
AnimeDlg.h の方に次のように追加します。

class CAnimeDlg : public CDialog
{
    int m_iRed; // 赤色。
// 以下略。

 Ver 7.16 ( No.136 ) のと同じとこね。あ、 m_bDrawOk は削除しちゃっ
ていいの?
 CAnimeDlg::OnPaint() の追加したとこを削除かコメントアウトしてくれ
ればオーケー。 CAnimeDlg::OnInitDialog() はこれから追加するから。つ
いでに書いちゃおうかな

BOOL CAnimeDlg::OnInitDialog()
{
// 略。
    // TODO: 特別な初期化を行う時はこの場所に追加してください。
    // 以下から追加。
    m_iRed = 0;
    ::SetTimer
        ( GetSafeHwnd()
        , 100   // タイマー ID 。
        , 125   // タイマーのインターバル。
        , NULL
        );
    // 追加ここまで。
    return TRUE;  // TRUE を返すとコントロールに設定した(略)。
}

 SetTimer() ってのがそうね
そう、この関数はタイマーをセットします。簡単にこの仕組みを言うと、
たとえばボタンを押されると呼ばれる CAnimeDlg::OnBDraw() や、描画する
時に呼ばれる CAnimeDlg::OnPaint() 、そういうのと同じように、一定時間
毎にメンバ関数が呼ばれるようセットするのがこの SetTimer() 
その時間ごとに呼ばれるんだ
そゆこと。この関数の第3引数に 125 って指定してるけど、これはその
時間。単位はミリ秒、つまり1秒の千分の1
 125 ってことは、4分の1秒ね。それって1秒に4回って事でしょ? 
さっき言ってた1秒間に60回ってのとえらい違いじゃない
そう。実はタイマーってあんまり機能良くないから、それほど早いのは無
理なんだよね。だからこのくらい
ふーん
次はこのタイマーを終了させる部分

void CAnimeDlg::OnBDraw() 
{
    ::KillTimer( GetSafeHwnd(), 100 );
}

 SetTimer() したのを、もう呼ばなくていいってするための関数がこの 
KillTimer() 
いつもの後片付けね。この 100 は?
これはタイマー ID って言って、 SetTimer() の第2引数。ひとつのウィ
ンドウに複数のタイマーをセットすることができるから、そのタイマーごと
にこの ID を付けて識別するんです
 KillTimer() で個別に止めることができるわけね
あ、ちなみにこの 100 はこういうふうに直打ちしない方がいいからね
前にそんなこと言ってたね
詳しくは Ver 4.09 ( No.059 ) を参照。こういうふうに直打ちすると、
SetTimer() を 101 にしたけど、 KillTimer() で 101 にし忘れてて、タイ
マーが終了しない〜
間抜けねー
そういうことにならないためにも、ね。じゃ、描画部分。まずはタイマー
で呼ばれるメンバ関数を作ります。前回、 Ver 7.17 ( No.137 ) と同じよ
うに作ります
同じ性質のものって事ね。メニューの【表示】−【 ClassWizard 】のダ
イアログの【メッセージマップ】ページで【クラス名】は CAnimeDlg にし
て【オブジェクト ID 】も CAnimeDlg にして、【メッセージ】は?
 WM_TIMER にして
【関数の追加】と【コード編集】をぽん

void CAnimeDlg::OnTimer(UINT nIDEvent) 
{
    CDialog::OnTimer(nIDEvent);
}

ってメンバ関数が出た。
この中に次のように追加して

void CAnimeDlg::OnTimer(UINT nIDEvent) 
{
    CRect cRect;
    ::GetClientRect( m_cCanvasStatic.GetSafeHwnd(), &cRect );
    HBRUSH hBrush = ::CreateSolidBrush( RGB( m_iRed, 0, 255 ) );
    HDC hDC = ::GetDC( m_cCanvasStatic.GetSafeHwnd() );
    ::FillRect( hDC, &cRect, hBrush );
    ::DeleteObject( hBrush );
    ::ReleaseDC( m_cCanvasStatic.GetSafeHwnd(), hDC );

    m_iRed += 2;
    if( m_iRed == 255 )
    {
        m_iRed = 0;
    }

    CDialog::OnTimer(nIDEvent);
}

さっきの for のとだいたい同じね
じゃ、実行してみて
……今度は眠らない?
大丈夫大丈夫
んじゃ。おお! すこーしずつ色が変わってく!! カッコイイ……
それにちゃんとダイアログも動かせるし
閉じることもできるし、あたしも見られる!
あ、閉じる前にボタン押さないと
これでタイマー止めるのね
というわけで、こういうふうにすれば他のアプリを邪魔することなくアニ
メーションできるわけです
おお!

/*
    Preview Next Story!
*/
今回のはかっこいい!
この前のオーナードローも良かったでしょ
うんうん。こういうのばっかだとプログラミングも楽しいかも
じゃあ来週はつまらないかも
げげ!
というわけで次回
< Version 7.19 ランダマイズとのべ秒 >
につづく!
金の延べ棒とかだと面白いんだけどねぇ
水希ちゃんそれ笑えないし痛いよ……
 
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
RSSに登録
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
 
このページは、Visual C++ 6.0を用いた C++ 言語プログラミングの解説を行う#pragma twiceの一コンテンツです。
詳しい説明は#pragma twiceのトップページをご覧ください。