• ベストアンサー

描画のカクカクについて

描画された画像が移動中たまに(動画を見ながら実行すると多く)カクカクするのですが、どうしたらこのカクカクをなくすことができるでしょうか? 多分ここだろうなと言うところを載せます。 //グローバル変数 static HDC g_hdcOff,g_hdcImg[MAX_IMAGE]; static HBRUSH g_hbrBg = NULL; // 背景用ブラシ static HBITMAP g_hbmImg[IMG_MAX]= {NULL}; // 画像のビットマップ //================================================================================ // オフスクリーンへ画像表示 // idx : 画像データの添え字 // x,y : 表示位置x,y // sx,sy : 画像切り出し位置x,y // w,h : 画像切り出し幅、高さ // src : 転送モード指定。通常はSRCCOPY,SRCPAINT,SRCANDなど //================================================================================ void DrawImg(int idx,int x,int y,int sx,int sy,int w,int h,DWORD src) { BitBlt(g_hdcOff,x,y,w,h,g_hdcImg[idx],sx,sy,src); } //================================================================================ // オフスクリーンへ画像表示。マスク表示→画像表示 // idx : 画像データの添え字 // x,y : 表示位置x,y // sx,sy : 画像切り出し位置x,y // w,h : 画像切り出し幅、高さ // mx,my : マスク切り出し位置x,y //================================================================================ void DrawImgMask(int idx,int x,int y,int sx,int sy,int w,int h,int mx,int my) { BitBlt(g_hdcOff,x,y,w,h,g_hdcImg[idx],mx,my,SRCPAINT); BitBlt(g_hdcOff,x,y,w,h,g_hdcImg[idx],sx,sy,SRCAND); } void Draw() { // 画面消去 HBRUSH oldbr = (HBRUSH)SelectObject(g_hdcOff, g_hbrBg); Rectangle(g_hdcOff, -1, -1, CLIENT_WIDTH+1, CLIENT_HEIGHT+1); SelectObject(g_hdcOff, oldbr); //---------------------------------------------------------- // ↓ここから描画処理 // キー状態の更新 KeyUpdate(); // プレイヤー移動 if (KeyIsHold(KC_LEFT)) { g_Player.x -= PL_VELX; if (g_Player.x < PL_LIMITL) g_Player.x = PL_LIMITL; } if (KeyIsHold(KC_RIGHT)) { g_Player.x += PL_VELX; if (g_Player.x > PL_LIMITR-g_ImgData[g_Player.type].w) g_Player.x = PL_LIMITR-g_ImgData[g_Player.type].w; } // プレイヤーの表示 DrawChr(g_Player.type, g_Player.x, g_Player.y); // ↑ここまで描画処理 //---------------------------------------------------------- // 再描画 InvalidateRect(g_hWnd,0,FALSE); UpdateWindow(g_hWnd); } // キャラクタ描画関数 // type : キャラタイプ(eChrType) // x : 表示位置X // y : 表示位置Y void DrawChr(int type,int x,int y) { DrawImgMask(g_ImgData[type].idx, x,y, g_ImgData[type].sx, g_ImgData[type].sy, g_ImgData[type].w, g_ImgData[type].h, g_ImgData[type].mx, g_ImgData[type].my); } int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) { ・・・ ・・・ ・・・ // メッセージループ while(true) { if (PeekMessage(&msg, NULL, 0,0,PM_REMOVE)) { if (msg.message == WM_QUIT) break; TranslateMessage(&msg); DispatchMessage(&msg); } else { static DWORD before_time; DWORD now_time; DWORD ideal_time=0; static int frame=0; TCHAR buff[100]; DWORD team=0; static long surplus_time=0; now_time=timeGetTime(); if(!before_time) before_time=timeGetTime(); surplus_time=(long)(now_time-before_time); ideal_time=(DWORD)(frame*1000)/60; wsprintf(buff,TEXT("%d %d %d\n"),ideal_time,surplus_time,(long)ideal_time-surplus_time); OutputDebugString(buff); if((long)ideal_time-surplus_time>0) Sleep((long)ideal_time-surplus_time); team=timeGetTime(); team-=before_time; if(team>=1000) { before_time=timeGetTime(); frame=0; } frame++; } Sleep(1); } ・・・ ・・・ ・・・ return (int)msg.wParam; } 環境はVC++2005、C言語とWINAPIっだけです。 説明が足りないのなら補足します。

  • 79562
  • お礼率68% (164/239)

質問者が選んだベストアンサー

  • ベストアンサー
回答No.4

そういえば、透過とかできないからマスクしてなんてこともしてたなぁとか すこしずつ思い出しつつ んで、昔作ったソース探したのですが見つからないものですね(笑 今じゃもう、ビルド環境すらないのでどうにもならない今日この頃なのですけど やっていることはあっている感じですねぇ でも全画面転送にWM_PAINTは経由していなかった気がする なんとなく、GetDC()で取ってきて直接書き込んでいたと思います。 ためしに UpdateWindow() を呼び出さない形にしてみてはいかがでしょう? UpdateWindow() はWindow作成時の一度しか明示的な呼び出しは不要だったはずです あとWM_SIZEとかリサイズ周りの時? Zバッファというは、スプライトの描画を後ろから書いていくことで 重なったときに全面のスプライトを表示するという意味です。 WIN32APIでも、マスク画像を準備してあげることで透過スプライトを出力できるのでてっきりやっているものだと。。

79562
質問者

お礼

返信ありがとうございます。 なんとか出きるようになりました。 TransparentBlt()関数使っていたりしてたらいつの間にか出来てました。 お騒がせしました!点数は配分するので!

その他の回答 (3)

回答No.3

ダブルバッファリングの基本は、書き直しと一括転送です。 気になったのは >オフスクリーンはorで切り取って、 >次にandで切り取ったのを背景に貼り付けるような作りになっています。 >そしてPAINTで表画面に貼り付ける感じになっております。 の部分ですね。 プライマリの画面から一瞬でもスプライトが消えていたりしませんか? 消えちゃうとカクカクとした感じになったりします。 あとは、単純に描画回数が多すぎて処理落ちしているケースもありますけど。。 コーディングしていたのはずっと昔なので関数とか用語は忘れちゃいましたけど、 ダブルバッファリングの流れを簡単にかくとこんな感じです。 1. 初期化用の背景バッファ作成 2. バックバッファ(オフスクリーン)とプライマリバッファ(普通のDC)作成 3. オフスクリーンを背景バッファで塗りつぶし 4. オフスクリーンにすべてのスプライト書き直し 5. プライマリバッファに全画面転送 6. 「3.」へもどる 昔はVGAが遅かったのとスプライトが少なかったため、 オフスクリーンの部分的に消し描きなんてのもしていましたが 今のご時世ならスプライトも多いでしょうから一度クリアして書き直した方が早いです。 (Zバッファが効くので都合もよい)

79562
質問者

お礼

>プライマリの画面から一瞬でもスプライトが消えていたりしませんか? >消えちゃうとカクカクとした感じになったりします。 >あとは、単純に描画回数が多すぎて処理落ちしているケースもありますけど。。 画面からは消えていません。カクカクしたままです。

79562
質問者

補足

返信ありがとうございます。 >1. 初期化用の背景バッファ作成は、 // 背景表示ブラシ作成(背景色の設定) g_hbrBg = CreateSolidBrush(RGB(255,255,255)); >2. バックバッファ(オフスクリーン)とプライマリバッファ(普通のDC)作成は、 // オフスクリーンの作成 hdc = GetDC(hWnd); g_hdcOff = CreateCompatibleDC(hdc); hbmp = CreateCompatibleBitmap(hdc,CLIENT_WIDTH,CLIENT_HEIGHT); ReleaseDC(hWnd, hdc); SelectObject(g_hdcOff,hbmp); DeleteObject(hbmp); >3. オフスクリーンを背景バッファで塗りつぶしは、 // 画面消去 HBRUSH oldbr = (HBRUSH)SelectObject(g_hdcOff, g_hbrBg); Rectangle(g_hdcOff, -1, -1, CLIENT_WIDTH+1, CLIENT_HEIGHT+1); SelectObject(g_hdcOff, oldbr); で >4. オフスクリーンにすべてのスプライト書き直し void DrawImgMask(int idx,int x,int y,int sx,int sy,int w,int h,int mx,int my) { BitBlt(g_hdcOff,x,y,w,h,g_hdcImg[idx],mx,my,SRCPAINT); BitBlt(g_hdcOff,x,y,w,h,g_hdcImg[idx],sx,sy,SRCAND); } 、>5. プライマリバッファに全画面転送は、 case WM_PAINT: hdc = BeginPaint(hWnd,&ps); // オフスクリーンからウィンドウに転送 BitBlt(hdc,0,0,CLIENT_WIDTH,CLIENT_HEIGHT,g_hdcOff,0,0,SRCCOPY); EndPaint(hWnd,&ps); return 0; ですよね?これでもカクカクが取れません。なにか間違っているのでしょうか? >今のご時世ならスプライトも多いでしょうから一度クリアして書き直した方が早いです。 (Zバッファが効くので都合もよい) は多分DirectXですよね。WIN32APIにもあるのでしょうか? 回答お待ちして居ります。

回答No.2

g_hdcOffはどのように得ていますか? g_hdcOff=CreateCompatibleDC(NULL); のように、バックバッファ用にデバイスコンテキストを作成しているのであれば、良いと思うのですが、コールバック関数の引数のウインドウハンドルから得ていると、直接windowに表示してしまっていることになります。その場合、画面がちらつきます。

79562
質問者

お礼

コールバック関数のWM_CREATEは次のようになっております。 case WM_CREATE: // オフスクリーンの作成 hdc = GetDC(hWnd); g_hdcOff = CreateCompatibleDC(hdc); hbmp = CreateCompatibleBitmap(hdc,CLIENT_WIDTH,CLIENT_HEIGHT); ReleaseDC(hWnd, hdc); SelectObject(g_hdcOff,hbmp); DeleteObject(hbmp); SetTimer(hWnd,NULL,1000,NULL); return 0; これは、 >コールバック関数の引数のウインドウハンドルから得ていると、直接windowに表示してしまっていることになります。その場合、画面がちらつきます これは、hdc = GetDC(hWnd); こういうことを指しているのでしょうか? 回答お待ちしております。

79562
質問者

補足

g_hdcOff = CreateCompatibleDC(hdc); の所をhdcをNULLに変えてやりましたが、カクカクしたままです。

回答No.1

コードはほとんどみていません。 一昔前の2Dゲームとかだと、「ダブルバッファリング」という手法を 利用していました。 これは、表示しようとする画面を裏で作成しておいて いっぺんに表示するという方法です。 こうすることで、ちらつきや目に残る残像を減らすことができます。 オフスクリーンという単語が入っているので 使っているようにも見えたのですけど なんか、オフスクリーンを直接表にしていたりしませんかね? 本当なら、オフスクリーンの出力がメッセージループの アイドル処理部分とかにあるはずなんだけど見あたらないですね。 あと余談ですが キャラの座標移動と描画はイベントを分けた方がいいと思いますよ。

79562
質問者

お礼

if(team>=1000) { before_time=timeGetTime(); frame=0; } frame++;    <ーーここ のところにDraw();が入ります。入れるのを忘れました。 それでもカクカクが消えません >オフスクリーンという単語が入っているので >使っているようにも見えたのですけど >なんか、オフスクリーンを直接表にしていたりしませんかね? void DrawImgMask(int idx,int x,int y,int sx,int sy,int w,int h,int mx,int my) { BitBlt(g_hdcOff,x,y,w,h,g_hdcImg[idx],mx,my,SRCPAINT); BitBlt(g_hdcOff,x,y,w,h,g_hdcImg[idx],sx,sy,SRCAND); } オフスクリーンはorで切り取って、 次にandで切り取ったのを背景に貼り付けるような作りになっています。 そしてPAINTで表画面に貼り付ける感じになっております。 なにか間違っているのでしょうか? 直すことはできないのでしょうか? 回答お待ちしております。

関連するQ&A

  • 再帰呼び出しについて

    C言語について質問があります。 平成13年春の基本情報処理技術者試験に出ていた問題なのですが, このプログラムの流れが分かりません。 void DrawCurve(int sx, int sy, int x1, int y1, int x2, int y2, int ex, int ey, int len){ int p1x, p1y, p2x, p2y, p3x, p3y; int p4x, p4y, p5x, p5y, p6x, p6y; /** 途中省略 **/ DrawCurve(sx, sy, p1x, p1y, p4x, p4y, p6x, p6y, len); /* ↑を(1)と名付けさせて頂きます */ DrawCurve(p6x, p6y, p5x, p5y, p3x, p3y, ex, ey, len); /* ↑を(2)と名付けさせて頂きます */ return ; } (1)の処理に入ったらDrawCurve関数の先頭に行くと思うのですが, そうすると(2)の処理とreturnは絶対行われない気がするのです。 それとも(1)の後(2)の処理に行くのでしょうか? 再帰を間違って解釈してるのだと思います。 ご存知の方どうか教えてください。よろしくお願いします。

  • C#でPictureBoxが二重に表示される

    碁盤を描画し、その上にマウスを合わせたとき付近の交点にPictureBox(サイズは21x21)でカーソルを表示しようと考え、次のように書きました。 int sx = (int)Math.Floor((double)(e.X - 12) / 25) + 1; int sy = (int)Math.Floor((double)(e.Y - 12) / 25) + 1; imgCsObj.Location = new Point(sx*25-21, sy*25); しかし盤上でマウスを動かすと画像のように二重にカーソルが表示される現象が起きました。 おそらく内部的にはカーソルを表示した途端にフォーカスが盤から離れPictureBoxを移動する作業が中断されたせいでは、と思い imgCsObj.Enabled = false; imgCsObj.Location = new Point(sx*25-21, sy*25); imgCsObj.Enabled = true; 一度無効にしてフォーカスがあたらない状態で位置だけ変更しようと思いましたが同じ結果でした。 どうやって以前の位置のカーソルを消せば良いのでしょうか

  • 3点を通る放物線の求め方を教えてください。

    3点を通る放物線の求め方を教えてください。 (x1,y1), (x2,y2), (x3,y3)をこの順番で通り、頂点を(x2,y2)とする放物線を考えます。 3点が直線上になければ、ただ一つの放物線が定まると思います。 x=x2 を対称軸と仮定すれば、 a(x-x2)^2+y-y2=0 が放物線の式になります。 回転を考慮し、c^2+s^2=1 の変数を加えて書きなおせば、 a(cx-sy-cx2+sy2)^2+sx+cy-sx2-cy2=0 となりますが、X=x-x2, Y=y-y2 と置けば、 a(cX-sY)^2+sX+cY=0 となります。 この先、x1, y1 などを代入し、連立方程式にして解けば…と思いましたが上手くいきませんでした。

  • actionscript2.0で動的な位置変化

    actionscript2.0を使い、bg0~bg9と配列で並んだムービークリップに対して、hit判定があった場合、hitしたムービークリップの数の分だけ、位置がずれていくという動的なムービークリップの移動と配置をさせたいと考えています。(画像参照) また、「hitBar」を外した際に、元いた位置に戻ってほしいです。 ムービークリップの配置の配列は、下記のスクリプトを使用しています。 objA = new Array(2* 5); D = 0; sy = 112; for (y = 0; y < 2; y++) { sx = 70; for (x = 0; x < 5; x++) { objA[D] = _root.attachMovie("bg" , "bg" + D, D + 100); objA[D]._x = sx; objA[D]._y = sy; D++; sx += 36; } sy += 36; } actionscriptはまだまだ未熟なもので、いろいろと試してみたのですが、どうにも上手くいかずに困っています… どうかお知恵を貸していただけたら幸いです。 画像のような動きができれば、上記のスクリプトを用いらずともまったくかまいません。 よろしくお願いいたします。

    • ベストアンサー
    • Flash
  • C言語のプログラムなのですが、2点の座標(SとR)を指定して片方からも

    C言語のプログラムなのですが、2点の座標(SとR)を指定して片方からもう片方への道筋を座標でcsvファイルに記述していくプログラムを作りたいのですが、うまく動きません。 参照渡しの所が間違っている様な気もするのですがわかりません。わかる方いましたらご指摘ください! エラー内容:Segmentation Fault(core dumped) #include<stdio.h> #include<stdlib.h> #include<math.h> #include<time.h> int Fugo(int); void Flooding(int *,int *,int *,int *); typedef struct{ int id; int dX; int dY; }NodeStates; NodeStates Node1[50]; NodeStates Node2[50]; int main(void) { int SX1,SY1; int RX1,RY1; int SX2,SY2; int RX2,RY2; int i=1; int j=1; FILE *fp; fp=fopen("test.csv","w"); if(fp!=NULL){ printf("S1 Node PointX>>"); scanf("%d",&SX1); printf("S1 Node PointY>>"); scanf("%d",&RX1); printf("R1 Node PointY>>"); scanf("%d",&RY1); Node1[0].id = 1; Node1[0].dX = SX1; Node1[0].dY = SY1; printf("S2 Node PointX>>"); scanf("%d",&SX2); printf("S2 Node PointY>>"); scanf("%d",&SY2); printf("R2 Node PointX>>"); scanf("%d",&RX2); printf("R2 Node PointY>>"); scanf("%d",&RY2); Node2[0].id=1; Node2[0].dX=SX2; Node2[0].dY=SY2; srand((unsigned int)time(NULL)); fprintf(fp,"First\n"); fprintf(fp,"NodeID,X,Y\n"); fprintf(fp,"%d,%d,%d\n",Node1[0].id, SX1, SY1); while(!(SX1==RX1 && SY1==RY1)){ Flooding(&SX1, &SY1, &RX1, &RY1); Node1[i].dX=SX1; Node1[i].dY=SY1; Node1[i].id=i+1; fprintf(fp,"%d,%d,%d\n",Node1[i].id,Node1[i].dX,Node1[i].dY); i++; } fprintf(fp,"Second\n"); fprintf(fp,"NodeID,X,Y\n"); fprintf(fp,"%d,%d,%d\n",Node2[0].id, SX2, SY2); while(!(SX2==RX2 && SY2==RY2)){ Flooding(&SX2,&SY2,&RX2,&RY2); Node2[j].dX=SX2; Node2[j].dY=SY2; Node2[j].id=j+1; fprintf(fp,"%d,%d,%d\n",Node2[j].id,SX2,SY2); j++; } } fclose(fp); return 0; } void Flooding(int *a,int *b,int *c,int *d){ int *e,*f,r; *a=*a-*c; *b=*b-*d; *e=Fugo(*a); *f=Fugo(*b); *a=abs(*a); *b=abs(*b); r=rand()%3; if(*a!=0&&*b!=0){ if(r==0){ *a=*a-1; } else if(r==1){ *a=*a-1; *b=*b-1; } else *b=*b-1; } else if(*a==0)*b=*a-1; else if(*b==0)*a=*b-1; *a=*a**e+*c; *b=*b**f+*d; } int Fugo(int a){ int n; if(a>=0){ n=1; } else{ n=-1; } return n; }

  • 連立方程式が解けない

    SX=S/((S^2)+1)-Y SY=1/((S^2)+1)+X をXとYの式に直したいのですが、うまくいきません、途中式を教えてください。 ちなみに答えはX=((S^2)-1)/(S^2+1)^2 Y=2S/((S^2)+1)^2 よろしくお願いします。

  • 計算がよくわかりません

    ξi=Xi-x/Sx, ηi=Yi-y/Sy (i=1,2,3…,n) (2)→二乗 ただし、Sx,Syはxおよびyの標本の標準偏差とする。  rxy=1/nΣξiηi=Sxy/SxSy Σξ=Ση=0、Σξ(2)=Ση(2)=n→なぜnになるのかが計算があいません。計算の過程を教えてほしいです。

  • 連立方程式を解いてくださいっ!

    以下の連立方程式が解けなくて困っています、誰か助けてください。 -- | (x - y) × (CW / 2) = SX | |SH - ((x + y) × (CH / 4)) = SY -- この連立方程式で、x と y を導き出す式が欲しいのですが 数学が苦手なため、自力で解くことができません。 何卒、よろしくお願い致します。

  • tabにdrawImageで画像を描画したい

    tabにdrawImageで画像を描画したい 以下のソースでtabbedpaneに対して、ImageIconを指定して画像をのせるではなく、 drawImageメソッドで描画したいです。 ご存知の方がおられましたら教えてください。 import javax.swing.*; import java.awt.BorderLayout; import java.awt.Graphics; import java.awt.Graphics2D; public class JTabbedPaneTest4 extends JFrame { Zoom zoom = null; public static void main(String[] args) { JTabbedPaneTest4 frame = new JTabbedPaneTest4(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setBounds(10, 10, 300, 200); frame.setVisible(true); } JTabbedPaneTest4() { JTabbedPane tabbedpane = new JTabbedPane(); JPanel tabPanel1 = new JPanel(); tabPanel1.add(new JButton("button1")); ImageIcon icon1 = new ImageIcon("img1.jpg"); zoom = new Zoom(icon1, 0, 0, 50, 50); //tabbedpane.addTab("tab1", icon1, tabPanel1);//ok //tabbedpane.add(zoom, tabPanel1);//ng(パネルに書き込まれてしまう) getContentPane().add(tabbedpane, BorderLayout.CENTER); } class Zoom extends JComponent { private static final long serialVersionUID = 1L; private ImageIcon icon = null; private int x = 0; private int y = 0; private int h = 0; private int w = 0; private double scale = 1.0d; public Zoom(ImageIcon icon, int x, int y, int w, int h) { super(); this.icon = icon; this.x = x; this.y = y; this.w = w; this.h = h; } public void paintComponent(Graphics g) { Graphics2D g2 = (Graphics2D) g; g2.scale(scale, scale); g2.drawImage(icon.getImage(), x, y, w, h, this); } } }

    • ベストアンサー
    • Java
  • JAVAのプログラミングについて質問です。

    現在、あるサイトを参考にして class AppCanvas extends Canvas{ BufferedImage image;//ウィンドウに表示するイメージ //コンストラクタ AppCanvas(){ image=loadImage("input.jpg");//表示用のイメージを取り込み } //イメージをファイルから取り込む BufferedImage loadImage(String name){ try{ FileInputStream in=new FileInputStream(name);//FileInputStreamを作る BufferedImage rv=ImageIO.read(in);//イメージを取り込む in.close();//閉じる return rv;//戻り値に読み込んだイメージをセット }catch(IOException e){ //エラー時の処理(エラーを表示)しnullを返す System.out.println("Err e="+e);//エラーを表示 return null;//null を返す } } //表示の必要があれば実行されるメソッド public void paint(Graphics g){ int x2=getWidth();//画面の幅を取得 int y2=getHeight();//画面の高さを取得 int iw=image.getWidth(this);//イメージの幅を取得 int ih=image.getHeight(this);//イメージの高さを取得 int x0=0;//画面左位置 int y0=0;//画面上端位置 int x1=x2/2;//画面中央位置(横) int y1=y2/2;//画5面中央位置(縦) int sX=300;//画像表示開始位置(水平方向) int sY=300;//画像表示開始位置(垂直方向) int eX=600;//画像表示流量位置(水平位置) int eY=600;//画表示終了位置(垂直位置) g.drawImage(image, x0,y0,x1,y1,this);//画面の左上にイメージを縮小して表示 g.drawImage(image,x1,y1,x2,y2,sX,sY,eX,eY,this);//画面右下に一部を拡大して表示 } } // //WindowAdapterは、WindowListerの機能を持ちますが、 //必要なメソッドだけを書けば、WindowListenerとして使えます // class Adapter extends WindowAdapter { //右上の「×」クリックされると呼び出される public void windowClosing(WindowEvent e){ System.exit(0);//アプリの終了 } } というプログラムを書き、添付した画像のように左上に取りこんだinputの画像、 その右下にその一部を拡大したものを表示、というものになっています。 これを用い、inputの画像を魚眼レンズで撮ったような画像にし、右下に表示される拡大されたものを、 魚眼レンズで撮ったような画像から、一般のレンズで撮ったような画像へ変換して表示したいと考えています。 その場合、どのようにソースコードを書き換えればいいでしょうか? ほとんどプログラミング経験がなく、困っています。

    • ベストアンサー
    • Java