origiです。またうまくいかないで困っています。教えてください。
char StageMap[100][100] を宣言し、中には
[1] cca11111111.....
[2] 22aabb22322.....
[3] 32242424222.....
.............
という感じで文字が入っています。
また、
struct DATA
{
char Name[10];
HBITMAP bmp;
}
DATA Data[100];
Data[0].Name[] = "a";//
Data[0].bmp = ○○.bmpのハンドル ;
Data[1].Name[] = "1" ;
Data[1].bmp = ◇◇.bmpのハンドル ;
.......(他にも)
と宣言されています。(適当ですので細かいことは無視してください)
※Nameの変数は不規則です。
そこで、
for(i=0;;i++)
for(j=0;;j+)
for(n=0;;n++)
if ( StageMap[i][j] == Data[n].Name[0])
{
SelectObject(hdc, Data[n].bmp)
}
としているんですが、forが3つも重なって途中で止まってしまいます。
そこで、少しでも処理を軽くしようと、
char bmpName[100] = "abcd12345";(bmpName[n] == Data[n].Name)
と文字列を作ってやったのですが駄目でした。
どうしたら処理を軽くできるか教えてください。
えっと、訂正で、WM_PAINTでこのコードを実行してるんですが、forが三つも
重なっていて、処理が重くて途中から描画されなくなってしまいます。
なにか処理を軽くする方法は無いかと strchr など使ってみたのですが
ダメでした。
なにか処理を軽くできる方法がありましたら、ご教授お願いします。
ゲーム大好きです。(^^;;
考え方を変えて、1つのビットマップから部分画像を切り出して
使うようにすれば、SelectObject も頻繁に呼ばなくていいのでは。
たとえばRPGなら、世界マップ⇒町マップに切り替わるタイミング
位で画像データを差し替える程度で。
DIBの生データをメモリ上で1画面分組み立ててから
ウィンドウに描画するのが早い(軽い)と考えます。
変化のあった部分だけを描画するようにすればもっと早いね。
ちなみに GetPixel や SetPixel は凄く遅いので使わないようにね。
...あ、ゲームでなかったらごめん。
なーめさん、ありがとうございます。
なーめさんの言うとおりRPG(?)を作っています。
そこでステージを描画するときにこのコードを使っています。
今度は Data に HDC hdc; を追加して、起動時にビットマップを選択しておいて、
呼び出すときは hdc のほうを呼び出しているんですがまだ処理が重いようです。なにか方法は無いものでしょうか?
もしかして 16x16 のタイルを 100x100 個表示ですか。
(縦サイズは予備かもしれないが)
結構つらいかもしれない。
解像度妥協して、StretchBlt で2倍にすると、
単純計算で4倍位のスピードで描画できますが、
まあ、DC を使っての描画は遅いので有名なので。
マシンパワーにもよるけど。
640x480のウィンドウに2倍のストレッチがせいぜいじゃないかな。
この辺で妥協できなければ、DirectX ですne。
mingw なんてのもあるようですが。
http://sourceforge.net/
あなたの知っているキーワードでソース検索ですね。
http://pc5.2ch.net/test/read.cgi/gamedev/1008220559/384-483
ここ見てたら面白いキーワードがあったので、探したら
http://sourceforge.net/project/showfiles.php?group_id=5665&package_id=5714
が出てきたよ。
・・・・英語ぜんぜんだめです。翻訳使ってもわかりませんでした。
なんかなーめさんが言っているページへ行ったのですが、これってライブラリファイルですか?
学校じゃあ英語がかなりヤバイ方なので・・・。
あと、なにか描画で負担が軽い方法などないですか?
一応今は BitBlt() で描画するところのみを描画するようにしているんですが、
一つ一つ調べているとやはり処理が重くなるようで・・・。
>> ライブラリファイルですか
>> Allegro game programming library
そうですね。
とりあえず、all403.zip をダウンロードすればいいのでは。
Source.zip って書いてあるからソースのライブラリですね。
>> 英語ぜんぜんだめです
VC++ のマニュアルも肝心なところは英語だしねー。
でもさ、学校の英語とマニュアルの英語とは
全然ちがうじゃない?
>> Below is a list of the files for the selected file package.
>> 下のはリストでファイルので選択したファイルパッケージのだ。
>> 下のは選択したファイルパッケージのファイルリストだよ。
ってな具合。
べつに、仮定法過去とか、いわないじゃない。
# 逆に並べて「の」でつなぐ式。(^^;;
>> あと、なにか描画で負担が軽い方法などないですか?
8ビットでパレット式のカラーにしてみるかな。
#いちばん「しょぼい」手段だな。
VRAM に直接書き込む。
方法は知らないけど。
そもそも Windows が許してくれないかな。
こうなったら、
1. 解像度を妥協する
2. 英語マニュアルを読む。
どちらかしかないのでは。
ところで2chの方は読んだ?
http://irrlicht.sourceforge.net/downloads.html
でさ、(英語だけど)
とりあえず、右側の irrlicht0.7.zip 11.7MB をダウンロードね。
で、解説がさ、
http://www.zgock-lab.net/irrlicht/index.htm
みたいな、日本語サイトがあるよ。
詳しくは見てないけど。
なーめさん、またまた返答ありがとうございます。
2chはと言うと実はまだ読んでないです。内容が長すぎて時間がやばいです・・・。
後日時間があるときに読ませて頂きます。
あと、VisualC++ の本をパラっと見たらイメージリストという便利そうな代物が
あったんですが、BitBlt よりも処理は軽いでしょうか?マスク処理も加えて。
CImageList ですか。
これは主に CListCtrl や CTreeCtrl でアイコンを使う時のためのもの
だと思いますが。遅くなるだけですよ。
マスク処理って透過色を指定するやつですか。
このサイトで、TransparentBlt()の質問をしている人がいますね。
それより
CBitmap::GetObject()
CBitmap::GetBitmapBits();
を調べてみてください。
表示する画像を組み立てるところはメモリ上で自分で
コピー(for ループとmemcpy())するのが一番早いので
GetBitmapBits() で得られる生のRGBデータを操作するします。
hDCとかCDC を使うのは1画面(フレーム)あたり1回だけにします。<- 重要!
StretchDIBits()でね。
>> 表示する画像を組み立てるところはメモリ上で自分で
>> コピー(for ループとmemcpy())するのが一番早いので
>> GetBitmapBits() で得られる生のRGBデータを操作するします。
>> hDCとかCDC を使うのは1画面(フレーム)あたり1回だけにします。<- 重要!
>> StretchDIBits()でね。
このサイトの CDib クラス使えます。
http://home16.inet.tele.dk/hansm/visualc/cdib.htm
google で訳すと変になるので、
http://www.excite.co.jp/world/
これ使って訳してください。
CDib ってMFCですか?
自分は SDK の API を使って、MFCは使っていないので・・・(これは最初に言うべき??)
あと、なーめさんがおっしゃったとおり StretchDIBits() を使ったんですが、
やはり連続して使用すると描画がとまってしまいます。他の機能は動くんですが・・・。
これは自分のプログラムが悪いということでしょうか?
>> CDib ってMFCですか?
そうだね。惜しいね。
これ、StretchDIBits() 使って画面にコピーしてくれるので、
非常に便利なんですが。
>> やはり連続して使用すると描画がとまってしまいます
こちらでは CDibで StretchDIBits() を使っていますが、
Bitmap で作ったパラパラ漫画形式の簡易ムービー
(200ms間隔で次々とビットマップを描画、64枚程度でループ)
に使用していますが、半日動かしても大丈夫でした。
(Win2K/VC++6.0)
取得したメモリやハンドルの解放は大丈夫ですか。
あと、ReleaseDC()/DeleteDC()/DeleteObject() とか。
1箇所でもメモリリークがあると、いずれ落ちます。
とくに大きな画像を扱っているので画像データのメモリリーク
が起きているとすぐに停止してしまうでしょう。
Win2Kなら、タスクマネージャでメモリ使用量を見れるのですが。
そちらのOSは何でしたっけ?
まあ、どんなOSでも可能な方法といえば、
例えば、画像データのアドレスがどんどん増えていっているとか
でもわかると思われます。
いくつかのポイントでアドレスやハンドルの値のログをファイルに
とってみてはどうでしょうか。
>> これは自分のプログラムが悪いということでしょうか?
上のメモリ確認の結果や、プログラム見ないと何ともいえませんね。
大量の画像データを扱うことによりOSのメモリ管理で負荷になって
いるかもしれないので。
1画面 1600*1200*3=5760000バイト
5Mバイトを毎秒20画面とすると、
100Mバイト/秒の転送速度が必要に
なりますよね。
それもVideoCard への転送だけで。
これに画面構成のプログラムが動作するわけですから、
この3〜4倍になるのではないかな。
もちろん、OSや他のアプリも稼動しているわけで。
そうすると、Pentium4 3.2G で FSB800 でも普通に動くかどうか。
それから、5Mバイトですから、画像のための連続メモリが常にとれるかどうか
という問題もあります(メモリのとりかたにもよりますが)。
メモリが正常に取れたかどうかチェックする必要はありますね。
実際のところ毎秒何画面くらいで動きます?
連続で使用すると止まるというのは2回目で止まるということなのかな?
今、Pentium4 1.9 GHz メモリ 512M FSB333 Win2K のマシンで、
1600x1200 の画面に対して、真っ黒から真っ白まで256ステップの
描画をやってみたら、18508ms かかりました。
1枚あたり 72.3ms だから逆数とって毎秒13枚。
思ったより早かったかな、という位。
StretchDIBit()です。
4回ループして1024枚描画したのですが止まっていません。
>> そうすると、Pentium4 3.2G で FSB800 でも普通に動くかどうか。
8ビットじゃないから4倍の余裕がありましたね。
さまざまな情報ありがとうございます。
StrechDIBits() に使った BITMAPINFO のアドレスを確認しましたが、リソースはきちんと消されていました。
移動時間は10秒ほどでとまってしまいました。
一応描画関数を載せておきます。
/* ステージ描画関数 */
void DrawStage(HDC shdc, int PlayerX, int PlayerY)
{
int j , i , n;
int MapDrawPointX , MapDrawPointY ; // 描画するマップ座標値
int DrawMapChipNumX , DrawMapChipNumY ; // 描画するマップチップの数
// 描画するマップチップの数をセット
DrawMapChipNumX = C_width / StageSize + 1 ;
DrawMapChipNumY = C_height / StageSize + 1 ;
// 画面左上に描画するマップ座標をセット
MapDrawPointX = PlayerX - DrawMapChipNumX / 2 ;
MapDrawPointY = PlayerY - DrawMapChipNumY / 2 ;
// マップを描く
for( i = 0 ; i < DrawMapChipNumY ; i ++ )
{
for( j = 0 ; j < DrawMapChipNumX ; j ++ )
{
// 画面からはみ出た位置なら描画しない
if( j + MapDrawPointX < 0 || i + MapDrawPointY < 0 ||
j + MapDrawPointX >= StageWidth || i + MapDrawPointY >= StageHeight ) continue ;
for(n=0; n<3; n++)
if(StageMap[ i+MapDrawPointY][j + MapDrawPointX] == BmpData[n].Name[0])
break;
StretchDIBits(
shdc , j*StageSize , i*StageSize , 40, 40 , 0 , 0 ,
40 , 40 ,
BmpData[n].bPixelBits , BmpData[n].bmpInfo , DIB_RGB_COLORS , SRCCOPY
);
}
}
return;
}
追記で、上の関数を WM_PAINT にセットしておき、矢印キーが押されると
InvalidateRect() で再描画して呼び出しています。
あと、タスクマネージャーを見たところ、描画が止まってしまうのはCPUではなく、
ページファイルの使用量がどんどん増加し、描画が一時止まっても減らないから(っぽい)とわかりました。
でもそこをどうすればいいかわかりません・・・。
言い忘れました。OSはWindowsXP で VisualC++ を使っています。
編集 削除止まるという現象の内容が不明です。
たとえば
0xC0000005 Access Violation。
であるとか、
無限ループに入っているとか。
システムがフリーズしてしまうとか。
無反応になるのか。
いずれにせよ、デバッガ上で動かすべきです。
for 文の前(MapDrawPointY の計算式)と return 文にブレークポイントを貼り、
各パラメタ(変数値)を確認したほうがよいでしょう。
まさか、描画先はデスクトップウィンドウだったりしないですよね。
あと、問題が StretchDIBits() らしいと当たりをつけたなら、
別のプログラムを作成して、このループだけを実行するような
テストプログラムを作成してみるといいでしょう。
その際、マップチップも1つだけにします。
そのほか、気になる点として、2点あります。
ひとつは、BITMAPINFO の内容です。
これを設定している部分はどうなっているでしょうか。
次はこの部分です。
>> for(n=0; n<3; n++)
>> if(StageMap[ i+MapDrawPointY][j + MapDrawPointX] == BmpData[n].Name[0])
>> break;
これ、if 文が一度も成立(true)しないと n == 3 になりますが、
BmpData[n].bPixelBits , BmpData[n].bmpInfo
は、n == 3 で大丈夫ですか。
あと、問題とは関係ありませんが、
>> if( j + MapDrawPointX < 0 || i + MapDrawPointY < 0 ||
>> j + MapDrawPointX >= StageWidth ||
>> i + MapDrawPointY >= StageHeight ) continue ;
横に長くてみずらくないですか。
if(( j + MapDrawPointX ) < 0 ) continue;
..
if(( i + MapDrawPointY ) >= StageHeight ) continue;
演算子も優先順位をあてにせず、括弧で明示する。
仕事ならこういう書き方が要求されますね。
処理速度はコンパイラの最適化で同じですから。
ちなみにウィンドウサイズは 1600x1200 でいいのですか?
私が書いているうちに発言しましたね。(^^;;
>> ページファイルの使用量がどんどん増加し、
メモリに関する項目がいくつかあったと思いますが、
おそらくメモリリークです。
解放されないメモリがどこかにありますね。
>> 描画が一時止まっても減らないから(っぽい)とわかりました。
正常でもすぐには減らないことがあります。
再使用される可能性があるため、メモリが予約された状態に
なると考えてください。
もちろんリークのために増えたままである可能性が大ですが。
>> OSはWindowsXP で VisualC++
さらに、
WinXpPro で VC++6.0
ですか?
あと、マシンスペックも。
メモリサイズ、FSB CPU
がわかるといいですね。
タスクマネージャに関して
[プロセス]タブ。
メモリリークに関係する項目を[表示][列の選択]で選択しましょう。
[v] メモリ使用量
[v] メモリ使用量デルタ (増減分の表示)
[v] 最大メモリ使用量
[v] 仮想メモリサイズ
[v] ハンドルの数
[v] GDIオブジェクト
これを見ながらおおまかにリーク箇所を特定します。
なんかすごいかきこまれてる・・・。
BITMAPINFO の内容ですが、ウォッチで見てみましたが、異常な値はなく、マップも正常に表示されています。
止まる現象とは、正常に動くけど再描画されない(WM_PAINTメッセージgあ送られない?)状態になります。
描画先はクライアントです。
マシンの種類はよくわかりません。なにを見たらよいかもわからなくて・・・。
環境はVisual .NET の C++ です。
タスクマネージャーになーめさんが言ったとおりの項目を選択した結果、GDIオブジェクトが増える一方とわかりました。
これってどう処理をすればいいのですか?
>> なんかすごいかきこまれてる・・・。
(^^;;
>> 正常に動くけど再描画されない(WM_PAINTメッセージが送られない?)状態
リソースが足りなくて描画できなくなったのでは。
この状態で他のアプリを起動しようとすると、たぶんそのアプリも
描画できないのではないかな。
>> GDIオブジェクトが増える一方
Pen/Brush/Bitmap/Font/Region の類ですね。
CreateXXXXX() で作成したものは
DeleteObject() と必ず対になっていなければなりません。
関数内で CreateXXXX() している場合、
途中でも return; するときはそこでも DeleteObject() しなければなりません。
ここのところ確認してみてください。
閑話休題。
こんなの見つけた。(^^;;
http://www.sun-inet.or.jp/~tkosugi/
それから、純粋にSDKのみで StretchBltをforの2重ループで使用する
プログラムを実行してみましたが、(WinXp/VC++6.0)
やはり 10分以上動きつづけました。
> タスクマネージャーになーめさんが言ったとおりの項目を選択した結果、
> GDIオブジェクトが増える一方とわかりました。
このケースでありがちなのは SelectObject の戻し忘れ。
最初にはまる人が多いのでチェックするべし。
>> SelectObject の戻し忘れ
同意。
透明化の話が出ているサイトを見つけました。
http://hp.vector.co.jp/authors/VA016117/assem/alpha.html
一見さん、なーめさん、助言ありがとうございました。
自分のとても情けないミスでした。
このプログラムは裏へ描画してから表へとしているのですが、その裏で使ったビットマップを
DeleteObject し忘れていました。原因はこれでした。
これで解決しました。ありがとうございます。