画面のチラツキを防ぐには

[戻る]
Beginner [HomePage] 2003/02/07(金) 07:54:21
Win2K + VC++6.0 + MFCの練習のつもりでブロック崩しを書いています。
Delphiで書くと画面はちらつかないのですが、どうしても画面がちらつきます。
どうすれば良いか色々なスレッドを調べたのですが解決できません。ご教授お願いします。
void CBlockHitCPPDlg::OnMouseMove(UINT nFlags, CPoint point) 
{
    // TODO: この位置にメッセージ ハンドラ用のコードを追加するかまたはデフォル
トの処理を呼び出してください
    if (point.y>400)
    {
        CDC* pDC=GetDC();
        CDC mDC;
        CBitmap bmpRacket, *bmpOld;
        CBitmap bmpRacket1, *bmpOld1;
        bmpRacket.LoadBitmap(IDB_RACKET);
        bmpRacket1.LoadBitmap(IDB_RACKET1);
    //ラケットの前の位置を消去する
        mDC.CreateCompatibleDC(pDC);
        bmpOld1=mDC.SelectObject(&bmpRacket1);
        pDC->BitBlt(oldMousePoint.x-80, oldMousePoint.y-30, 
oldMousePoint.x+79, oldMousePoint.y+29,&mDC,0,0,SRCCOPY);
        bmpOld=mDC.SelectObject(&bmpRacket);
        pDC->BitBlt(point.x-80,point.y-
30,point.x+79,point.y+29,&mDC,0,0,SRCCOPY);
        mDC.SelectObject(bmpOld);
        currentMousePoint.x=point.x;
        currentMousePoint.y=point.y;
        oldMousePoint.x=point.x;
        oldMousePoint.y=point.y;
    }

    
    CDialog::OnMouseMove(nFlags, point);
}

nasu 2003/02/07(金) 09:39:27
「ちらつき」で過去ログ検索

Beginner [HomePage] 2003/02/08(土) 00:57:51
nasu様、

ご指摘ありがとうございました。
この検索は既に行い、試しましたがDelphiで書いたプログラムのようには
上手にちらつき防止は出来ません。
その故に恥を忍んでの質問です。
何かヒントでもご教授下さい。

Beginner 2003/02/08(土) 00:57:55
nasu様、

ご指摘ありがとうございました。
この検索は既に行い、試しましたがDelphiで書いたプログラムのようには
上手にちらつき防止は出来ません。
その故に恥を忍んでの質問です。
何かヒントでもご教授下さい。

nasu 2003/02/08(土) 01:30:38
>この検索は既に行い、試しましたが
具体的にどう試したのですか?
そのコードをここに載せてみては?

>上手にちらつき防止は出来ません。
上手にちらつき防止が出来ないとは、具体的にどうなったのですか?

出来ない出来ないと言ってるだけでは、こっちは何もわかりませんよ。

ナオーバ 2003/02/08(土) 09:13:29
違っていたらすみません
CBlockHitCPPDlgからしてダイアログベースとして進めます

まずBOOL型のメンバ変数 m_bDrawを追加
コンストラクタでFALSEに設定しておいて
OnMouseMoveの if (point.y>400)をif( m_bDraw )に変更してOnPaintのelse内に移して

OnMouseMoveを↓のように変更します
void CBlockHitCPPDlg::OnMouseMove(UINT nFlags, CPoint point) 
{
    if(point.y>400)    m_bDraw = TRUE;
    else               m_bDraw = FALSE;
    
    CDialog::OnMouseMove(nFlags, point);
}

WM_ERASEBKGNDのハンドラを追加して以下のようにする
BOOL CBlockHitCPPDlg::OnEraseBkgnd(CDC* pDC) 
{
    return TRUE;
}

WM_ERASEBKGNDのハンドラ追加方法
ウィザードバーの一番右の▼を押して"Windowsメッセージハンドラの追加"を選択
ここで左側にWM_ERASEBKGNDがあれば選択して"追加と編集"ボタンを押す
無い場合は右下の"クラスで使用可能なメッセージ用フィルタ"からウィンドウを選択
これで表示されるので選択して"追加と編集"ボタンを押す

で解決するハズです
チラツキ防止はOnEraseBkgndのオーバーライドで解決する場合が多いですよ。
っていうか私はこれしか知りません
詳しい事はこれらの内容からキーワードを探して調べてください

Beginner 2003/02/08(土) 17:13:51
nasu様、

申し訳ありません。小生がトライしたのは、compatible memery DCを作成して、
そこに書き込んでからBitBltする方法、背景の書き直しをFillRectで
小範囲のみにする方法、背景の書き直しについて背景と同等のbmpを作成して
BitBltする方法でしたが、画面のちらつきは抑制できませんでした。
今、出先なのでコードをお送りできません。
申し訳ありません。

ナオーバ様、

ご親切にありがとうございます。
今、遠くに出先なのでトライできませんので、帰ってから早速トライしてみます。
また、ご指導の如く、キーワードで検索してみます。

結果できましたら、うまくいくいかないに関わらずご報告いたします。

Beginner [E-Mail] 2003/02/09(日) 12:39:40
nasu様、

まず、以下のように前の位置のラケット消去と現位置のラケット書き込みを一旦、mDC2に
行い、合成し、それをmDCに転送したのち一挙に画面DCに転送を試みましたが、マウスの移動に
伴いちらつくままでした。

void CBlockHitCPPDlg::OnMouseMove(UINT nFlags, CPoint point) 
{
    // TODO: この位置にメッセージ ハンドラ用のコードを追加するかまたはデフォル
トの処理を呼び出してください
    if (point.y>400)
    {
        CDC* pDC=GetDC();
        CDC mDC, mDC2;
        CBitmap bmpRacket, *bmpOld;
        CBitmap bmpRacket1, *bmpOld1;
        bmpRacket.LoadBitmap(IDB_RACKET);
        bmpRacket1.LoadBitmap(IDB_RACKET1);
    //ラケットの前の位置を消去する

//        pDC->FillSolidRect(oldMousePoint.x-80, oldMousePoint.y-30, 
oldMousePoint.x+79, oldMousePoint.y+29,RGB(0,0,0));

        mDC.CreateCompatibleDC(pDC);
        mDC2.CreateCompatibleDC(pDC);//ラケット消去とあらたな書き込みの
合成用
        bmpOld1=mDC2.SelectObject(&bmpRacket1);
        pDC->BitBlt(oldMousePoint.x-80, oldMousePoint.y-30, 
oldMousePoint.x+79, oldMousePoint.y+29,&mDC2,0,0,SRCCOPY);

        bmpOld=mDC2.SelectObject(&bmpRacket);
        pDC->BitBlt(point.x-80,point.y-
30,point.x+79,point.y+29,&mDC2,0,0,SRCCOPY);
        mDC2.SelectObject(bmpOld);
        pDC->BitBlt(0,0,1000,800,&mDC2,0,0,SRCCOPY);
        pDC->BitBlt(0,0,1000,800,&mDC,0,0,SRCCOPY);
        currentMousePoint.x=point.x;
        currentMousePoint.y=point.y;
        oldMousePoint.x=point.x;
        oldMousePoint.y=point.y;
    }

    
    CDialog::OnMouseMove(nFlags, point);
}

ナオーバ様の方法も順次、試みます。
なお、小生は"初心者"氏とは別人です。

Beginner 2003/02/09(日) 15:38:13
nasu様、

試してみました。具体的には以下のようです
OnPaintのelse以下に持ってきました。mousePointをメンバー変数のcurrentMousePointに
OnMouseMoveで代入して、それを用いています。
    else
    {
        if (m_bDraw==TRUE)
        {
            CPoint point;
            CDC* pDC=GetDC();
            CDC mDC;
            CBitmap bmpRacket, *bmpOld;
            CBitmap bmpRacket1, *bmpOld1;
            bmpRacket.LoadBitmap(IDB_RACKET);
            bmpRacket1.LoadBitmap(IDB_RACKET1);
            //ラケットの前の位置を消去する
            point.x=currentMousePoint.x;
            point.y=currentMousePoint.y;

            mDC.CreateCompatibleDC(pDC);
            bmpOld1=mDC.SelectObject(&bmpRacket1);
            pDC->BitBlt(oldMousePoint.x-80, oldMousePoint.y-30, 
oldMousePoint.x+79, oldMousePoint.y+29,&mDC,0,0,SRCCOPY);

            bmpOld=mDC.SelectObject(&bmpRacket);
            pDC->BitBlt(point.x-80,point.y-
30,point.x+79,point.y+29,&mDC,0,0,SRCCOPY);
            mDC.SelectObject(bmpOld);
            pDC->BitBlt(0,0,1000,800,&mDC,0,0,SRCCOPY);
//            currentMousePoint.x=point.x;
//            currentMousePoint.y=point.y;
            oldMousePoint.x=point.x;
            oldMousePoint.y=point.y;
        }

        CDialog::OnPaint();
    }
}
そして、ハンドラを追加し、
BOOL CBlockHitCPPDlg::OnEraseBkgnd(CDC* pDC) 
{
    // TODO: この位置にメッセージ ハンドラ用のコードを追加するかまたはデフォル
トの処理を呼び出してください
    return TRUE;
//    return CDialog::OnEraseBkgnd(pDC);
}
としました。

また、OnMouseMoveについては
    if (point.y>400)
        m_bDraw = TRUE;
    else 
        m_bDraw = FALSE;
    
    CDialog::OnMouseMove(nFlags, point);
}
としました。
しかし、今度は真っ白なDialog boxが出来てしまい(多分全く書き込みがされていない)、勿論
ラケットも書かれません。色々と試してみます。

Beginner 2003/02/09(日) 15:57:48
ナオーバ様、失礼しました。

先の状況報告は誤りでした。
OnMouseMoveに伴うチラツキをはっきりさせるために、OnTimerによるボールの動き部分を完全
にコメントアウトしていました。このコメントアウトを戻すと、ちゃんと画面が書かれ、またボ
ールも動きます。しかし、ボールの動きに伴い、画面がちらつきます。
しかし、今度はラケットが書かれませんでした。
自分で解決できるよう頑張ります。
きっとOnTimerで書きにいっているのもOnPaintに持って来れば良いのかも知れません。

nasu 2003/02/09(日) 23:52:27
>まず、以下のように前の位置のラケット消去と現位置のラケット書き込みを一旦、mDC2に
>行い、合成し、それをmDCに転送したのち一挙に画面DCに転送を試みましたが

どう見ても画面に直接BitBltしてるようにしか見えないんですけど・・・

Beginner 2003/02/10(月) 05:54:07
nasu様、

ヒントありがとうございます。
とんでもない間違いをしているのかも知れません。
検討します。

ナオーバ 2003/02/10(月) 10:41:47
間違っていたらすみません。
まだまだ修行不足のVC暦1年の言うこととして聞いて下さい。

メモリDCで全て作成して画面に一気に出力したいのですよね?

pDC->BitBlt(転送先, 転送元DC, 転送元, コード);のとき
pDCに転送先DCが割り当てられます。
なので、BeginnerさんのソースではpDC->BitBltが3回あるので
1回の描画命令で3回画面に書くことになると思います。

それとチラツキと関係ないのですが
mDC.SelectObject(bmpOld);で元に戻されていませんよね?
mDC.SelectObject(bmpOld1);ではないでしょうか
最初に保存しといて、使いまわした後に元に戻すのが正解では?

OnTimerの部分も書いた方がレスがつきやすいかも。

Beginner 2003/02/10(月) 14:44:56
ナオーバ様、

詳しくありがとうございます。
OnTimerの部分はこのように書きました。
ご指摘のようにpDCに対して何回もBitBltしています。
これを修正している間にコードがぐちゃぐちゃになってしまい、始めに立ち返ってやり直しま
す。すみません。
SelectObjectについてもぐちゃぐちゃになって混乱していました。

void CBlockHitCPPDlg::OnTimer(UINT nIDEvent) 
{
    // TODO: この位置にメッセージ ハンドラ用のコードを追加するかまたはデフォル
トの処理を呼び出してください

          if (nIDEvent==1) {
        CDC* pDC=GetDC();
        CDC mDC;
        CBitmap blockImg, *oldImg;
        CBitmap ballImgOr, ballImgAnd, *oldImgOr, *oldImgAnd;
        mDC.CreateCompatibleDC(pDC);
        pDC->FillSolidRect(Ball.BallPosition.x-
15,Ball.BallPosition.y-15,
            Ball.BallPosition.x+14,Ball.BallPosition.y+14,RGB
(0,0,0)); //前の位置のボールを消去

        blockImg.LoadBitmap(IDB_BLOCK);
        ballImgOr.LoadBitmap(IDB_BALLOR);
        ballImgAnd.LoadBitmap(IDB_BALLAND);

        oldImg=mDC.SelectObject(&blockImg);
        for (int i=0; i<10; i++) {
            for (int j=0; j<3; j++) {
                int k=i + j*10;
                if (Block[k].LiveOrDead==Living) {
                    pDC->BitBlt(Block
[k].Position.left, Block[k].Position.top,
                        Block[k].Position.right, 
Block[k].Position.bottom,&mDC,0,0,SRCCOPY);
                    if (Block[k].Position.PtInRect
(Ball.BallPosition)) { //ボールがブロックにあたった
                        Block
[k].LiveOrDead=Dead; //ブロックを消す
                        MessageBeep
(MB_ICONASTERISK);
                        Ball.YSpeed=-Ball.YSpeed;
                    }
                }
                if (Block[k].LiveOrDead==Dead)
                    pDC->FillSolidRect(Block
[k].Position.left, Block[k].Position.top,
                        Block[k].Position.right, 
Block[k].Position.bottom, RGB(0,0,0));
            }
        } 

        mDC.SelectObject(oldImg); 

        Ball.BallPosition.x=Ball.BallPosition.x+Ball.XSpeed;
        Ball.BallPosition.y=Ball.BallPosition.y+Ball.YSpeed;
        if (Ball.BallPosition.x>=965) Ball.XSpeed=-Ball.XSpeed; //壁の
判定
        if (Ball.BallPosition.x<=5) Ball.XSpeed=-Ball.XSpeed;
        if (Ball.BallPosition.y>=765) Ball.YSpeed=-Ball.YSpeed;
        if (Ball.BallPosition.y<=5) Ball.YSpeed=-Ball.YSpeed;

        CRect rc;
        rc.SetRect(currentMousePoint.x-80,currentMousePoint.y-
30,currentMousePoint.x+79,currentMousePoint.y+29);
        if (rc.PtInRect(Ball.BallPosition)) {  //ボールがラケットにあた
った
            Ball.YSpeed=-Ball.YSpeed;
            MessageBeep(MB_OK);
        }

        oldImgAnd=mDC.SelectObject(&ballImgAnd); //ポールの描き込み
        pDC->BitBlt(Ball.BallPosition.x-15, Ball.BallPosition.y-15,
            Ball.BallPosition.x+14, 
Ball.BallPosition.y+14,&mDC,0,0,SRCAND);
        mDC.SelectObject(oldImgAnd);
        oldImgOr=mDC.SelectObject(&ballImgOr);
        pDC->BitBlt(Ball.BallPosition.x-15, Ball.BallPosition.y-15,
            Ball.BallPosition.x+14, 
Ball.BallPosition.y+14,&mDC,0,0,SRCCOPY);
        mDC.SelectObject(oldImgOr);
    } 
    

    
    CDialog::OnTimer(nIDEvent);
}

Beginner 2003/02/10(月) 15:20:41
コードがぐちゃぐちゃになってしまいましたので、物事をはっきりさせるために、新たにOff 
Screenを用いた簡単な動画を作成しました。

void COffScreenTestDlg::OnTimer(UINT nIDEvent) 
{
    // TODO: この位置にメッセージ ハンドラ用のコードを追加するかまたはデフォル
トの処理を呼び出してください
    if (nIDEvent==1) {
        CDC *pDC = GetDC();
        CDC mDC, oDC;
        CBitmap bmpBlock, *bmpOld;
        mDC.CreateCompatibleDC(pDC);
        oDC.CreateCompatibleDC(pDC);
        bmpBlock.LoadBitmap(Racket);
        bmpOld=mDC.SelectObject(&bmpBlock);
        oDC.BitBlt(point.x++,point.y++,(point.x++)+32,(point.y++)
+32,&mDC,0,0,SRCCOPY);
          mDC.SelectObject(&bmpOld);
        pDC->BitBlt(0,0,1000,800,&oDC,0,0,SRCCOPY);
        
        if (point.x>950) point.x=0;
        if (point.y>750) point.y=0;
    }

    
    CDialog::OnTimer(nIDEvent);
}
しかしながら、画面に動画が現れません。一旦、BitmapをmDCに選択し、それをoDCに転送して
から、pDCに転送するというものです。何が悪いでしょうか。ご教授下さい。

dairygoods 2003/02/10(月) 15:38:33
>何が悪いでしょうか。ご教授下さい。

oDCに画面の内容を描画するためのビットマップが設定されていません。

それと、GetDC()でゲットするばかりで、ReleaseDC()しないのも
後々問題となるでしょう。


ところで、ビットマップを描きたいだけなら、
mDCを用意しなくても、oDC.DrawState()で可能です。

Beginner 2003/02/10(月) 17:14:24
dairygoods 様、
ご教授ありがとうございます。
以下のようにして何とかなりましたが、
@off screenを用いるのであれば、前のブロック位置を消去する必要が無いのでしょうか。
実際にその操作をせずとも移動していきます。
A背景をSolidBrushで設定しているつもりなのですが、真黒のままなのは何故でしょうか。

void COffScreenTestDlg::OnTimer(UINT nIDEvent) 
{
    // TODO: この位置にメッセージ ハンドラ用のコードを追加するかまたはデフォル
トの処理を呼び出してください
    if (nIDEvent==1) {
        
        point.x+=5;
        point.y+=3;

        CDC *pDC = GetDC();
        CDC mDC;
        CBitmap bmpBlock;
        CBitmap bmpBlockBlue;
//        CBitmap *bmpOld1, *bmpOld2;
        
        mDC.CreateCompatibleDC(pDC);
        bmpBlock.LoadBitmap(IDB_BLOCK);
        bmpBlockBlue.LoadBitmap(IDB_BLOCKBLUE);
        mDC.SelectObject(&bmpBlock);
        // off Screenの準備
        CBitmap bmpOff;
        bmpOff.CreateCompatibleBitmap(pDC,1000,800);
        CDC oDC;
        oDC.CreateCompatibleDC(pDC);
        CBrush offBrush;
        offBrush.CreateSolidBrush(RGB(100,50,255));
        CRect r;
        r.top=0;
        r.left=0;
        r.bottom=800;
        r.right=1000;
        oDC.FillRect(r,&offBrush);
        oDC.SelectObject(&bmpOff);
        //
        oDC.BitBlt
(point.x,point.y,point.x+32,point.y+32,&mDC,0,0,SRCCOPY);
        mDC.SelectObject(&bmpBlockBlue);
        oDC.BitBlt
(point.x*2,point.y*2,point.x*2+32,point.y*2+32,&mDC,0,0,SRCCOPY);
//        mDC.SelectObject(bmpOld1);
//        oDC.SelectObject(bmpOld2);
        pDC->BitBlt(0,0,1000,800,&oDC,0,0,SRCCOPY);
        
        if (point.x>500) point.x=0;
        if (point.y>400) point.y=0;
        oldPoint=point;
        ReleaseDC(pDC);
    }

    
    CDialog::OnTimer(nIDEvent);
}

dairygoods 2003/02/10(月) 17:29:18
>@off screenを用いるのであれば、前のブロック位置を消去する必要が無いのでしょうか。

毎回off screenを新しく作っている(CreateCompatibleBitmap)ためです。
1つのoff screenを使いまわす場合は、消去しなければなりません。


>A背景をSolidBrushで設定しているつもりなのですが、真黒のままなのは何故でしょうか。

offBrushで塗りつぶした後でoff screenをセットしているためです。


ビットマップの読込みや作成は、塗ったりコピーしたりに比べると
相当時間のかかる処理です。現在の方法が理解できたのでしたら、
ビットマップを使いまわすように工夫することをお勧めします。

Beginner 2003/02/10(月) 17:33:05
[[解決]]
dairygoods様、

懇切丁寧にありがとうございました。
ここ数日の悩みがやっと解決した気分です。
ご指摘のように「使い回し」について試みたいと思います。
また、他の方々もご指導有難うございました。

n [E-Mail] 2003/02/10(月) 17:33:58
1 は、広いビットマップをウィンドウ全体にBitBlt()している為
2 は、bmpOffをoDCに選択する前にFillRect()している為

だと思われます。

n [E-Mail] 2003/02/10(月) 17:37:16
外しました。

Beginner 2003/02/10(月) 17:40:14
n様、

1と2は互いに関連しているものと推測します。
多分、off screenでその部分のみ書き込めば良いのですね。
ありがとうございます。

Beginner 2003/02/10(月) 18:02:56
既に解決済みですが、あえて追加します。Aについて解決しました。
文の順番を以下のように置き換えたのみです。

        CDC oDC;
        oDC.CreateCompatibleDC(pDC);
        CBrush offBrush;
        offBrush.CreateSolidBrush(RGB(100,50,255));
        CRect r;
        r.top=0;
        r.left=0;
        r.bottom=800;
        r.right=1000;
        oDC.SelectObject(&bmpOff);    //ここの順番を変更しました
        oDC.FillRect(r,&offBrush);    //これにより背景色がちゃんと維持され
ました
        //
        oDC.BitBlt
(point.x,point.y,point.x+32,point.y+32,&mDC,0,0,SRCCOPY);
        mDC.SelectObject(&bmpBlockBlue);
        oDC.BitBlt
(point.x*2,point.y*2,point.x*2+32,point.y*2+32,&mDC,0,0,SRCCOPY);
//        mDC.SelectObject(bmpOld1);
//        oDC.SelectObject(bmpOld2);

        pDC->BitBlt(0,0,1000,800,&oDC,0,0,SRCCOPY);
        
        if (point.x>500) point.x=0;
        if (point.y>400) point.y=0;
        oldPoint=point;
        ReleaseDC(pDC);
    }

あいちゅん 2003/02/11(火) 20:01:03
メモリDCを使うと
読み込んだデータの流れはどうなってるのですか?
最初の段階でメモリ上のビットマップに描かれるのですか?
1画素ずつ、順番にデータが反映されるのでしょうか?
メモリDCの役割がいまいち、しっくりきません
どうか、教えていただけないでしょうか?

古いスレッドには追加発言できません。