Objectを大量に描画したいが…重い。なにかが悪い。

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
やっくる

Objectを大量に描画したいが…重い。なにかが悪い。

#1

投稿記事 by やっくる » 5年前

FC風の横スク2Dシューティングを製作しているのですが
FCグラディウスで有名な、こちらの様な
「障害物オブジェクトが多数設置されたステージ」を作ろうものなら
http://download1.getuploader.com/g/9%7C ... efault.jpg
すぐに画面のfpsが遅延状態になってしまいます。

現在、障害物オブジェクトは雑魚敵と同じクラスで扱っておりますが、
画面内に入ったらインスタンス化する様にして
なるべくギリギリまで生成されない様にしています。
その障害物のサイズも8×8と小さいです。
が、画面内に15個〜20個作られた時点で
画面が相当重くなってしまうのです。

今の時代で画面内にたかが20個や30個、小さなオブジェクトが
生成されたくらいでここまでfpsが下がるということに疑問を抱きますが、
なぜ自分は今、FC時代に出来ていることの再現が出来ないのでしょう?

画面内にオブジェクトを大量に作る場合
なんかしらテクニックが必要なのでしょうか?

想像で話しますが、
インスタンス化したクラスの中の処理数が重要なのでしょうか?
例えば、私の障害物オブジェクトはプレイヤーが壊せることから、
雑魚敵と同じクラスで作っています。
なので、処理の中には他の雑魚敵の処理も連ねていて、
大量のif文で実行処理を分けています。
本来そこは障害物オブジェクト用に、
座標とHPだけを持ち、HPがゼロになったら消えるって処理だけの
非常にシンプルな内容のクラスを
雑魚敵用のクラスとは別で作り、
インスタンス化する方が良いのでしょうか?

また、雑魚敵のクラスといっても
自分は癖で何種類もの雑魚敵をそのひとつのクラスで
回しています。
動きなどを、処理の中で細分化させて
すべての雑魚敵がそのクラスでインスタンス化するだけで
動く様に記載してしまっています。
こういうのはヤメた方がいいのでしょうか?

ですが、何種類もいる敵に合わせて
個別にクラス名.cppを作っていたら
それこそ、中の処理数は短いが、ファイル数は敵の種類だけあるので
膨大な数になると思うんですが、、、でも
重くなることを考えれば、そっちのが良いのでしょうか??

その辺の知識が独学の自分には欠けていますし、
得る方法がわかりませんので、こちらで質問させて頂きました。

昔pc-98でファーストクイーンとゲームがありました。
ここの回答者サマたちならご存知かと思います。
http://download1.getuploader.com/g/9%7C ... ss03-2.jpg
こんなのを将来作ってみたいとも思っていますので
今回の質問は重要かと考えます。
ぜひ、よろしくお願い致します。

というか、8bitのグラディウスや
パソコンとは言え旧時代のゲームのファーストクイーンですよ?
あんだけオブジェクト作っても処理速度は落ちてなかった(画像はちらついてましたが)のに
一体、どんな工夫をしていたのでしょう????
それとも、ただ自分が
あまりにもヘタクソな作り方してるだけなのでしょうか?

せと

Re: Objectを大量に描画したいが…重い。なにかが悪い。

#2

投稿記事 by せと » 5年前

さすがにオブジェクトの20個や30個で、fpsがガタ落ちするようなことは普通ではありません。
プログラムにもよりますが、負荷が少なめのオブジェクトであれば、
1000個~2000個は軽く処理できるはずです。

ただし、大量のグラフィックスを同時に描画したい場合は、
描画処理のちょっとした知識と工夫がないと若干重くなりますが、
それでも100枚程度までなら問題ありません。

何か無駄な処理を繰り返してしまっているのではないでしょうか?
例えば、初回だけ実行すれば済む比較的重い処理を、毎フレーム実行してしまっている、とか。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 9年前
住所: 東海地方
連絡を取る:

Re: Objectを大量に描画したいが…重い。なにかが悪い。

#3

投稿記事 by softya(ソフト屋) » 5年前

たぶん、どこかで間違っています。
実証したいのなら、超簡単なプログラムで画面内に15個〜20個表示してみてください。
それで問題ないのなら、ロードが無駄に発生している、画面内に15個〜20個つもりが数千個表示している、どこかで過剰な負荷のかかる処理をしているのいずれかです。
自分のプログラムが間違っている発想でデバッグしないとデバッグできるものもできなくなりますよ。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
spaaaark・∀・
記事: 62
登録日時: 6年前
住所: 京都のどこかの学寮
連絡を取る:

Re: Objectを大量に描画したいが…重い。なにかが悪い。

#4

投稿記事 by spaaaark・∀・ » 5年前

皆さんが色々な意見を投稿しておられますが、僕は処理に関する気になる所を攻めてみます。

まず、すべての敵・障害物で同じクラスを使っている件について。
これはいろいろとまずいです。比較で処理が遅くなってしまったり、あるいはコードが読みにくくなってしまったり。
これはポリモーフィズムを用いて、同じような機能を持ちながら別の処理を行なえるような処理にするべきです。
これを用いれば少なくとも障害物と敵との差別化は図れるはずです。
あとは画像の違いなど、軽い変更なら汎用化して外部データ化するのもありです。例えばcsvのような。
これでファイル数の増加も抑えることができます。
なお、どっちの方が見やすく、修正しやすいかは現状のコードと、分けて書いたときのコードを想像すれば一目瞭然のはずです。
[キーワード]ポリモーフィズム,インターフェイスクラス,継承,オーバーライド etc...

次に、インスタンスの管理方法です。
これはコードがないのではっきりとしたことが言えないのですが、もしC++でstd::vectorを使って管理している場合、
eraseの時の処理で時間がかかっている可能性が高いです。この場合、挿入と削除に強いstd::listを使う事を推奨します。
ただしこちらの場合基本的に添字ではなくイテレータを用いてアクセスすることになるので注意してください。
[キーワード]std::list イテレータ etc...

あとはソフト屋さん・せとさんが言うような、処理のミスを地道に探していくことが大切です。
デバッグしてみれば自分が思わないような処理をしていることが多々あるので、ゆっくりたどっていくことが大切になります。
クリエイティブな生活で刺激的な毎日を!

ISLe()

Re: Objectを大量に描画したいが…重い。なにかが悪い。

#5

投稿記事 by ISLe() » 5年前

いまどきの弾幕シューティングだと同時に4000くらいのオブジェクトでも少ないほうだと思います。

投稿に書かれている内容についてわたしなりの回答をします。
以下はより高速にするための提案になります。
質問者さんのプログラムが重い原因は別にあるはずです。
それはコードを晒してもらわないことには分かりません。


FCのBGと同様に背景の描画はタイルマップを採用したほうが良いと思います。
破壊できる障害物については個別にインスタンスを作るのではなく、タイルマップを直接(あるいは内部に確保した状態マップを)チェックして『ある状態からある状態に変化させる』処理を対応させると良いでしょう。
そうすれば描画はタイルマップというひとつのオブジェクトで一括して行え、対応する処理は障害物の種類(且つ状態変化のあるものだけ)分で済みます。
スクロールに応じてタイルマップの画面に入ってくる端の分を更新していきます。
同時に障害物と状態変化処理の対応表も(必要であれば)更新していきます。


ゲームプログラムでコンテンツとしてのキャラの種類に応じてクラスを継承するようなことはむしろ非推奨なので、その辺は安心してください。
コンテンツとしてのキャラの種類は、動きを定義したデータを再生する処理(いわゆるスクリプト)を組んだりして実装します。

ただし何種類ものキャラの動きを直にコーディングしてifやswitchで分岐するようなものではありません。
どんなキャラにでもなれるクラスであって、いくつものキャラをまとめたクラスではありません。
#どんなキャラにでもなれるクラスとして、機能を継承するといったことはよくあります。

やっくる

Re: Objectを大量に描画したいが…重い。なにかが悪い。

#6

投稿記事 by やっくる » 5年前

せと さんが書きました:さすがにオブジェクトの20個や30個で、fpsがガタ落ちするようなことは普通ではありません。
1000個~2000個は軽く処理できるはずです。
何か無駄な処理を繰り返してしまっているのではないでしょうか?
例えば、初回だけ実行すれば済む比較的重い処理を、毎フレーム実行してしまっている、とか。
1000個~2000個は軽く、、ですか、良いですね、素晴らしい。
今回の問題が自分のせいだとわかり、安心しました。
がしかし、1000個~2000個は軽く処理出来るところを
20個やそこらでガタ落ちさせる処理に気付けない自分って
かなり致命的ですね。
もちろん初回だけ実行すれば済む処理を
毎フレーム処理している更新関数内には入れてないつもりなんですけど、、
でも今回のことでもう一度しっかり確認したいと思います。
(ソースを載せたいのはやまやまですが、更新関数内の量もハンパ無いので、、)

softya(ソフト屋) さんが書きました:たぶん、どこかで間違っています。
実証したいのなら、超簡単なプログラムで画面内に15個〜20個表示してみてください。
それで問題ないのなら、ロードが無駄に発生している、画面内に15個〜20個つもりが数千個表示している、どこかで過剰な負荷のかかる処理をしているのいずれかです。
→ロードが無駄に発生している
ロードの類いの処理は、コンストラクタか、INITIALIZE関数にしか記載しておらず、
一応その辺は守っているつもりです。

→画面内に15個〜20個つもりが数千個表示
自分はまだまだ知識が浅いので、そういうミスをたまに犯している場合もあります。
ので、注意をしています。発生処理については色々と確認しておりますので
今回に限っては、この辺も大丈夫なはずです。

なので、ソフト屋さんのご指摘で私が自信の持てない項目は
過剰な負荷のかかる処理ですね。
これは、先のせとさんのご指摘と同じかと思います。
やはり何か無駄な処理を繰り返してしまっているのかもしれません。

とりあえず、その雑魚敵用のクラスで作った
オブジェクトを画面内に増やせば、
すぐに画面が遅延するのでとても怪しいです。
思えば、アイテム類を構成する別のクラスで作った
オブジェクトは画面内に20個いくつ作ろうが、画面は遅延しませんでしたし。
雑魚クラスの更新関数が怪しくなって来ましたので
確認したいと思います。

せとさん、ソフト屋さん、ありがとうございます。

やっくる

Re: Objectを大量に描画したいが…重い。なにかが悪い。

#7

投稿記事 by やっくる » 5年前

spaaaark・∀・ さんが書きました: もしC++でstd::vectorを使って管理している場合、
eraseの時の処理で時間がかかっている可能性が高いです。
この場合、挿入と削除に強いstd::listを使う事を推奨します。
これは大丈夫です。
要素の追加や削除が頻繁に行われるものはlistで、と
勉強しましたので、ゲーム中に消去される様なオブジェクトはすべて
listで管理しております。
spaaaark・∀・ さんが書きました: これはポリモーフィズムを用いて、同じような機能を持ちながら
別の処理を行なえるような処理にするべきです。
そうですよね、実はこの辺が自分は弱いです。
多分、ISLe()さんも言う「どんなキャラにでもなれるクラス」も
同じ意味だと思うんですが

以前、プログラムの本で、雑魚敵をインスタンス化する際に、
毎回move関数をどっかから繋げてインスタンス化させるというのがありました。
うろ覚えですが、確か、別のところに様々な動きの処理の関数が用意されていて
それを各雑魚のmove関数にポリモフィズムで繋げて、
その雑魚の更新中はその処理をさせる、みたいな感じでした。
その時は自分が初歩の初歩でしたのでいまいち理解していませんでしたが
自分はそれを理解しなければならないところまで進んだようですね。
要は、今の私の処理では、ひとりひとりが処理を持ち
動いているわけですが、
ポリモフィズムをすることにより、ひとつの処理をみんなで使っている
といった感じでしょうか。
しかし、残念ながらポリモフィズム、すなわち多態性を利用した敵の生成を
解説しているサイトなんて、見当たりません。
ので、その本をもう一度探してみることにします。
ISLe() さんが書きました: FCのBGと同様に背景の描画はタイルマップを採用したほうが良いと思います。
はい、現在自分のシューティングの背景はタイルマップを採用しております。
それこそステージ開始時に描画タイル座標と描画タイル画像Noを持つ構造体で
vectorに詰めて描画しております。
確かに、そこにflgでも加え、背景が壊れる前、背景が壊れた後、で
描画する画像を設定する、のは良いかもしれません。
ただ現在、オブジェクト同士の判定と
背景とオブジェクトとの判定とは別のところで処理していまして、
背景vectorと動いてるオブジェクトたちとの衝突判定関数では
ぶつかると移動停止(押される等)の処理をやっているのですが、
そこにオブジェクト同士の判定と同じ様な判定内容も作る必要性が出て来ますから
なんだか、ややこしくなりそうで怖いです。

オブジェクトがタイルマップの上下左右それぞれのどの面に当たっただのの判定関数と、
オブジェクト同士の矩形領域の重なり判定とは別の箇所で処理しています。
なんだか、グラディウスでいうところの生き物みたいな障害物は
オブジェクト同士の矩形領域の重なり判定の方でやりたくなるんですが
ナンセンスなんでしょうか?
また、たまたま今回はマップのタイルサイズと障害物のサイズが同じなので
問題無いですが、障害物のサイズがタイルより小さい場合
タイル構造体がその障害物の座標なりサイズなり別で持つようにしなければならず
さらにややこしくなりそうな気がします。
あくまで想像ですが。

そもそも、ひとつのゲーム内で
キャラと背景に対しては上下左右の面との判定、
キャラ同士なら矩形のみで判定、とをしたい場合
判定関数を二種類作る、は正解なのでしょうか?
ISLe()さんなら、そこはキャラ同士でも上下左右の面との判定してやれば
ひとつの判定関数で済むじゃん、なんでしょうか?

とりあえず、
まずはクラス内で無駄な処理をしている可能性が大なので
そこを確認してみますが
ISLe()さんの言う、「より高速にするための提案」は
今後のために勉強すべきだとは思っています。いや、勉強したいので
ISLe() さんが書きました: コンテンツとしてのキャラの種類は、動きを定義したデータを再生する処理
についてもう少し詳しく教えて頂けませんか?
ただ、自分はC++以外は扱えないのでスクリプトを組むのは無理ですので
そこはポリモフィズムを利用して動きを定義したデータを再生する処理、
で御願い出来ればと思いますが、、、

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 9年前
住所: 東海地方
連絡を取る:

Re: Objectを大量に描画したいが…重い。なにかが悪い。

#8

投稿記事 by softya(ソフト屋) » 5年前

本当に問題ないのか、グローバル変数で良いので初期化と描画にカウント処理を埋め込んでみてください。
そのカウントを1フレームの最初でクリア、フレームの終わりでDrawFormatStringなどで描画します。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 9年前
住所: 東海地方
連絡を取る:

Re: Objectを大量に描画したいが…重い。なにかが悪い。

#9

投稿記事 by softya(ソフト屋) » 5年前

それと処理の時間計測をされていない様なので、まとまった単位で時間計測を行ってください。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

やっくる

Re: Objectを大量に描画したいが…重い。なにかが悪い。

#10

投稿記事 by やっくる » 5年前

softya(ソフト屋) さんが書きました:グローバル変数で良いので初期化と描画にカウント処理を埋め込んでみてください。
そのカウントを1フレームの最初でクリア、フレームの終わりでDrawFormatStringなどで描画します。
DrawFormatStringで確認するのはよくやりますが、
「そのカウントを初期化と描画に埋め込む。1フレームの最初でクリア、フレームの終わりで描画」
についてもう少し詳しくお願いします。

Initialize関数{
count=0;
count++;

DrawFormatString(割愛 count) 
}

Draw関数{
count=0;
count++;

DrawFormatString(割愛 count)
}

ってことでしょうか?
だとすれば自分は頭が悪く、これで一体なにを調べられるのか検討がつきません。
1フレームだけの変動ではDrawFormatStringで描画しても
0と1が凄いスピードで繰り返されるだけだと思うのですが、、、
ぜひこれによってなにがわかるのか、教えて頂けませんか?

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 9年前
住所: 東海地方
連絡を取る:

Re: Objectを大量に描画したいが…重い。なにかが悪い。

#11

投稿記事 by softya(ソフト屋) » 5年前

一番外側のwhileループ
while( ScreenFlip()==0 && ProcessMessage()==0 && ClearDrawScreen()==0 ){
この直後にcount=0;ですね。
DrawFormatString(割愛 count) は上のwhileのとじカッコ}の直前に入れます。
なので、InitializeとDrawは別カウント変数です。

こんな感じです。

コード:

#include "DxLib.h"

int WINAPI WinMain( HINSTANCE, HINSTANCE, LPSTR, int ) {
	ChangeWindowMode( TRUE ), DxLib_Init(), SetDrawScreen( DX_SCREEN_BACK ); //ウィンドウモード変更と初期化と裏画面設定

	int x = 0;
	int Handle;     // 画像格納用ハンドル
	Handle = LoadGraph( "画像/キャラクタ01.png" ); // 画像のロード

	// while( 裏画面を表画面に反映, メッセージ処理, 画面クリア )
	while( ScreenFlip() == 0 && ProcessMessage() == 0 && ClearDrawScreen() == 0 ) {
		initCount = 0;
		drawCount = 0;
		
		ほにゃらら。中でカウント。
		
		
		DrawFormatString(割愛 initCount,drawCount);
	}

	DxLib_End(); // DXライブラリ終了処理
	return 0;
}
1フレーム間に何回初期化と描画されたかを調べます。
変化が多すぎる場合は、平均とmaxを取る方向で。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

ISLe()

Re: Objectを大量に描画したいが…重い。なにかが悪い。

#12

投稿記事 by ISLe() » 5年前

わたしが思っているFCのBGと同様のタイルマップというのは、単にグラフィックをセットしてタイル状に描画する仕組みであって、いわゆるタスク的なオブジェクトのかたまりではないです。
判定処理は別のところでまとめて行うという考え方は良いと思いますが、わたしが考えているのと同じなのかどうかは分かりません。
キャラクタがタイルマップのサイズと合わないのならそれはもうタイルマップではないのですが。

やっくるさんの説明を読んだ感想は、既にかなり面倒くさいことをやっていそうだなあということですね。
ちょっとした特殊化とパラメータ調整で実装できそうなことを大量にコードを書いて実装しているようです。
根本的に異なる考え方でプログラムを組み上げているようなので互いにいくら説明を繰り返しても噛み合わない気がします。


スクリプトというのは単なる数値の並びを命令と解釈して実行するものも含みます。
一般的な話となると書籍が一冊できるくらいのボリュームの内容になるので、
・CPUが命令を実行する仕組みのうちメモリとレジスタとプログラムカウンタあたりのごく基本部分
・アセンブラ
・Javaや.NETなどのバイトコードを実行する仮想マシンの仕組み
あたりを勉強してください。
言語としての完成度を上げるのならインタプリタやコンパイラについても勉強してください。

やっくる

Re: Objectを大量に描画したいが…重い。なにかが悪い。

#13

投稿記事 by やっくる » 5年前

softya(ソフト屋) さんが書きました: 1フレーム間に何回初期化と描画されたかを調べます
手ほどきを示して頂き、ありがとうございます。
やってみましたが、結果は
数値、その推移等、多分正常だと思います。

とにかく、一部?のクラスを利用したオブジェクトを
増やすと遅くなってしまいます。
例えば、オブジェクトのENEMYを作る際に利用するクラスがそうで、
エネミーを画面内に20も作れば、もうスクロールがかなり遅くなります。
その敵を倒して消して行けば、スクロールはもとのスピードに戻ります。
で、その敵のクラスなんですが、インスタンス化する際に
引数で敵の種類を渡します。
そして、初期化、更新、描画のすべての関数が
switch文でわけられていまして、
その敵専用の初期化、更新、描画を処理し続ける、といった感じにしてます。
この方法自体は問題ないんですよね?
もちろん、ソースが大量になるわけでミスを誘発することになったり、
本来なら委譲を利用したりと、もっと合理的な方法があるとは思うんですが
自分はまだ初心者ですので、、、これが精一杯でして。
で、そんな内容のクラスなんで敵によっては、
更新の内容がスカスカになってる敵もいるんです。
(地面から動かない敵とか)
で、その敵を20個ほど作っても同じ遅延状態になってしまうんです。
つまり振り分けている敵の各処理云々では無く、
このクラス自体の内容に問題がありそうです。

ちなみに、アイテムも同じようにインスタンス化させて
ステージに表示しているんですが、
こちらは20個作ろう(アイテム専用のクラスで作ってます)と
スクロールに遅延は置きませんので
オブジェクトを放り込んでいるlist等を扱う処理が
問題では無いと思われます。

しかし、ENEMYクラスをどれだけ眺めても
原因が突き止められません。
独学の自分が成長するかどうかかなり
重要なところな気がします。
恥を承知でそのENEMYクラスのcppをアップしますので
一度確認して頂けませんでしょうか?
怪しいところとか、意味不明なところがありましたら
ご指摘頂ければ幸いです。
http://u9.getuploader.com/nikoman/downl ... cEnemy.txt

やっくる

Re: Objectを大量に描画したいが…重い。なにかが悪い。

#14

投稿記事 by やっくる » 5年前

ISLe() さんが書きました: ちょっとした特殊化とパラメータ調整で実装できそうなことを大量にコードを書いて実装しているようです。
はい、まだプログラム歴の短い駆け出しなので、
無駄なことを大量にしていると思います。
非常に見苦しいソースですが、こちらを見て頂き、
アドバイスを頂けるとありがたいです。
http://u9.getuploader.com/nikoman/downl ... cEnemy.txt
このクラスのENEMYを画面内に20個も生成すると
遅延してしまいます。。。
処理が多過ぎるのでしょうか?
ISLe()さんがおっしゃいました、
ISLe() さんが書きました: どんなキャラにでもなれるクラスであって、いくつものキャラをまとめたクラスではありません。
の意味を理解出来れば、改善するのかな、と思うのですが
今の自分にはまだ理解出来ません。
しかし、自分は"委譲"というものに触れたことがあります。
それが近いものではないでしょうか?
で、調べてみると、ISLe()さんが
http://dixq.net/forum/viewtopic.php?t=14077
のトピックにてまさにこういう会話をしていらっしゃいます。
質問者 さんが書きました: SLeさんのおっしゃる翻訳単位で分割とは分割コンパイルのことで、移動用の関数のパターンはそれ専用のmovePattern.cppなどにまとめておけばいいということでしょうか。
ISLe() さんが書きました: 翻訳単位での分割はそのとおりです。
Enemyクラスがインターフェースとして働くことになるのでそれに関する部分は自ずと共通化されることになります。
各キャラクタの固有部分が分離されて、オブジェクト別に状況に応じた柔軟な設計が可能になります。
まさに、ここで触れています"委譲"を自分は覚えたいのです。
委譲とは、その部分の仕事を他のクラスに丸投げすること、と聞いてます。
とすれば、今回の自分の話で言えば、
ENEMYクラスの更新内のすべてがスカスカになるわけですよね?
つまり、ENEMYオブジェクトを増やそうと、重くなるはずがないと。
委譲を利用すれば、今回の様なことは起きないのかなと思っているのですが
そういうわけではないのでしょうか?
自分のENEMYクラスのこの状態ってISLe()さんから見たらどうなんでしょうか、、

アバター
へにっくす
記事: 628
登録日時: 7年前
住所: 東京都

Re: Objectを大量に描画したいが…重い。なにかが悪い。

#15

投稿記事 by へにっくす » 5年前

やっくる さんが書きました:アドバイスを頂けるとありがたいです。
http://u9.getuploader.com/nikoman/downl ... cEnemy.txt
リンクは正しくはってください。
http://u9.getuploader.com/nikoman/downl ... cEnemy.txt
一応見てみましたが、見にくいです。
それぞれの関数の行数が、
CEnemy::Initialize (474行)
CEnemy::Update (878行)
CEnemy::Draw (276行)
と多すぎですので、それぞれの関数が100行以内におさまるように修正するのが先だと思います。
これじゃどこに問題があるかすぐに探せないでしょうねえ・・・
written by へにっくす

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 9年前
住所: 東海地方
連絡を取る:

Re: Objectを大量に描画したいが…重い。なにかが悪い。

#16

投稿記事 by softya(ソフト屋) » 5年前

もう一つ提案した時間計測の話はどうなってますか?

コードについては、問題点を拾い出しづらいです。
C言語で書かれた関数ポインタ方式の龍神録のほうがスッキリしている状態だと思いますので、switch case多用でC++の良さを失っています。

【補足】
気になるのは、弾一つ一つがクラスみたいなのでnew/deleteなどが重そうな印象。
あとnewでコンストラクタで画像ロードしていたら重そうですね。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

やっくる

Re: Objectを大量に描画したいが…重い。なにかが悪い。

#17

投稿記事 by やっくる » 5年前

へにっくす さんが書きました: 一応見てみましたが、それぞれの関数の行数が、
CEnemy::Initialize (474行)CEnemy::Update (878行)CEnemy::Draw (276行)
と多すぎですので、それぞれの関数が100行以内におさまるように
修正するのが先だと思います。
すみません、それはつまり
「そりゃ関数が100行を超えて来る内容のクラスをポコポコ作ってると
フレームレートはすぐ遅延状態になるよ」ってことでしょうか?
それとも、ただ単に「行数多くなると、ミスしやすい、
こうやってミスを探しにくいから
100行以内に収まる様にした方がいいよ」ということでしょうか?
気になります。
なぜなら、自分の実力ではこれらの動作を盛り込んで
各関数を100行以内に収めるのは正直不可能です。
もちろん、無駄な部分があるのは認めますが、
それを無くしても300、400行を縮めるのは無理です。
てっとり早く処理の行数を減らす方法として、
"委譲"を利用したいのですが、委譲を利用するには
自分自身勉強が必要ですので、今は無理です。
ただ、別の方法として、
ENEMYクラスを別に増やし、作る敵によって利用するクラスを分散させてやることにすれば、
ひとつのクラスの関数の行数を抑えるのは可能です。
「そうした方がいいんだよ」との結論でしたらそうしたいので
どちらの意味でアドバイスして頂けたのか、知りたいのです。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 9年前
住所: 東海地方
連絡を取る:

Re: Objectを大量に描画したいが…重い。なにかが悪い。

#18

投稿記事 by softya(ソフト屋) » 5年前

後者ですね。
バグるから・メンテが困難だから・バグが探せないから。
と言うことです。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

やっくる

Re: Objectを大量に描画したいが…重い。なにかが悪い。

#19

投稿記事 by やっくる » 5年前

softya(ソフト屋) さんが書きました:もう一つ提案した時間計測の話は
すみません、
softya(ソフト屋) さんが書きました:処理の時間計測をされていない様なので、
まとまった単位で時間計測を行ってください
これについては、やり方がわかりません。
処理の時間計測というのは、どういう方法で行うのでしょうか、、、。
初心者なので、処理の時間を計る、ということがどういうことなのかも
ちょっと想像つかない状態でして、、、お恥ずかしいです。
申し訳ありませんが、出来ましたら、処理時間計測をする重要性と
カウント処理の時の様に、簡単な手順を示して頂ければ非常に助かります。
softya(ソフト屋) さんが書きました:C言語で書かれた関数ポインタ方式の
龍神録のほうがスッキリしている状態だと思います
その言い様だと、龍神録は"委譲"を使われているのでしょうね。。。
softya(ソフト屋) さんが書きました: 弾一つ一つがクラスみたいなのでnew/deleteなどが重そうな印象。
あとnewでコンストラクタで画像ロードしていたら重そうですね。
もちろん弾もひとつひとつのクラスで作っています。
あ、確かに大量に弾を出す敵を作ると、
画面が遅延します。つまり、遅延する原因は同じみたいですね、、、、
ちなみに、画像ロードは各クラス内で個別に読み込んだりはしていません。
ゲームを最初に実行した時の初期化関数内でゲームに利用する画像はすべて読ませています。

弾を増やせば遅延のこともありますし、
結局、自分のフレームレート遅延は、ミスというものでは無く
クラスに処理を詰め込み過ぎ等の、
処理量が多いというものなんでしょうか?

しかし、自分は、ゲームで出現させる敵から弾から
各オブジェクトをクラスで作り、listに放り込んでいくことを
ある熟練プログラマからアドバイスされました。
(皆様から見て、どのくらいのレベルの方かは判断つきませんが)学びました。
これは、間違いなんでしょうか?
実際は、そうする方が、すごくラクなんですが。。。
softya(ソフト屋) さんが書きました: switch case多用でC++の良さを失っています。
しかし、自分の思った
「敵NO(15種類)を受け取り、そのキャラ専用の初期化、更新、描画の処理を実行させるクラス」
を実行するには、中でswitch caseを多用する他ありません。
"委譲"を利用すれば新しい考え方が出来る様な気がしますが
今はこれが自分の知識の限界です。
例えば、ソフト屋さんなら
「15種類の敵に、そのキャラ特有の初期化、更新、描画をさせる」
を実行しようとしたら、どう作られますか?
しかもそれぞれの弾にもまったく違う初期化、更新、描画を要します。
弾も敵もソフト屋さんはクラスでは作らないのでしょうか?
熟練の方のやり方を学びたいです。ぜひ教えて下さいませんか?

やっくる

Re: Objectを大量に描画したいが…重い。なにかが悪い。

#20

投稿記事 by やっくる » 5年前

softya(ソフト屋) さんが書きました:後者ですね。
バグるから・メンテが困難だから・バグが探せないから。
と言うことです。
やはり、そうですよね。
15種類の敵の処理をひとつのクラスでさせようと
今の時代、重くなるはずないですよね?
だとすれば、クラス内の処理では無く
もっと根本が、自分の構成の仕方に問題があるんでしょうか?

オブジェクトの管理について自分は現在、
ゲーム中、オブジェクトlistをひとつだけ作り、
そこにプレイヤーや敵、それらの弾、アイテムなど
すべてのオブジェクトをインスタンス化しては放り込んで
消滅するとflgを立て、毎フレーム最後に
list内から一括で削除しています。
衝突判定や、存在数を確認したりする場合
各オブジェクトが持つ変数の"ID"で判別、
イテレーターにて抽出して処理しています。

ちなみに、listを一つだけにしているのも
いちいちプレイヤーの弾用のlistとかエネミーの弾用のlistとか作る方が大変で
IDつけとけば判別出来るんだから
ひとつのlistで回せばいいとのアドバイスを受けたからです。

上記(赤フォント)のやり方なんですが
問題ありますか?

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 9年前
住所: 東海地方
連絡を取る:

Re: Objectを大量に描画したいが…重い。なにかが悪い。

#21

投稿記事 by softya(ソフト屋) » 5年前

>申し訳ありませんが、出来ましたら、処理時間計測をする重要性と
>カウント処理の時の様に、簡単な手順を示して頂ければ非常に助かります。

遅延して困っているわけですから、遅延している原因を探さないと行けませんよね。
それには、処理のあちこちで時間計測をして何処ネックか探さないといけません。
よほど経験を積まない限り、憶測だけで色々やってみても一向にケリが付きませんよ。
こちらもソースコードを見ていないので、想像で原因を書いてますが的中率はもちろん低いです。
それとやっくる さんの情報が正しいとも限りません。
思い込みを排除できない以上は、やっくる さんの報告は誤情報である可能性が常に付きまといます。

と言うことで、時間計測して現銀原因を探りましょう。
処理にかかっている時間で問題点を探します。
GetNowCount();
http://homepage2.nifty.com/natupaji/DxL ... .html#R7N1
で「Windows が起動してから経過 時間をミリ秒単位」で得られますので、処理開始と処理後の時間を得て差分を処理時間とします。
これで顕著に遅い部分を探しましょう。
最初はmain内のループなど大きな単位で、だんだん細かくメソッド関数まで降りてきます。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 9年前
住所: 東海地方
連絡を取る:

Re: Objectを大量に描画したいが…重い。なにかが悪い。

#22

投稿記事 by softya(ソフト屋) » 5年前

> その言い様だと、龍神録は"委譲"を使われているのでしょうね。。。

龍神録のC言語のコードを見られたことがないのでしょうか?
これです。
http://dixq.net/rp/12.html

【補足】
コードを直す件は、別途やりましょうか。
遅延バグ潰しとコードのリファクタリングの2つを一度にやるとろくな事になりません。
まず遅延の原因を探すことです。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

やっくる

Re: Objectを大量に描画したいが…重い。なにかが悪い。

#23

投稿記事 by やっくる » 5年前

softya(ソフト屋) さんが書きました: まず遅延の原因を探すことです。
そうですね、わかりました。
GetNowCount() で
関数の処理にかかっている時間を
調べてみたいと思います。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 9年前
住所: 東海地方
連絡を取る:

Re: Objectを大量に描画したいが…重い。なにかが悪い。

#24

投稿記事 by softya(ソフト屋) » 5年前

アルゴリズムの二分探索をご存知ですか?
これに似た考え方ですが、最初に関数まで絞り込むのではなく前半処理・後半処理と分けて時間計測で調査します。
それで前半が重ければ、更に前半の半分で調べます。
これを繰り返して絞り込んでいきます。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

うずら

Re: Objectを大量に描画したいが…重い。なにかが悪い。

#25

投稿記事 by うずら » 5年前

開発環境になにを使ってるか知りませんが素直にプロファイラ使えばいいんじゃないでしょうか・・・

プロファイラを使わないほうがいい理由とかあったら教えてください。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 9年前
住所: 東海地方
連絡を取る:

Re: Objectを大量に描画したいが…重い。なにかが悪い。

#26

投稿記事 by softya(ソフト屋) » 5年前

うずら さんが書きました:開発環境になにを使ってるか知りませんが素直にプロファイラ使えばいいんじゃないでしょうか・・・

プロファイラを使わないほうがいい理由とかあったら教えてください。
expressなら初めからついていないってところでしょうか。
【補足】VC++Express系に無料で容易に導入できて、初心者でも使いやすいプロファイラがあったら教えてください。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

ISLe()

Re: Objectを大量に描画したいが…重い。なにかが悪い。

#27

投稿記事 by ISLe() » 5年前

委譲といっても対象はいろいろあるので、けっきょくはどういうふうに実装するかの問題に帰結します。

複数の種類の敵をどうやって互いに影響し合うことなく実装するか、については簡単なサンプルコードを書いてみようと思います。

気長にお待ちください。

ソースコードのほうも。


ひとつのリストに放り込んで回すのはふつうだと思いますよ。
ただ、ヒープメモリの確保・解放はけっこう時間を食います。
フレームレートがひどく落ち込むほどではありませんが。

うずら

Re: Objectを大量に描画したいが…重い。なにかが悪い。

#28

投稿記事 by うずら » 5年前

オフトピック
ソフト屋さん、回答ありがとうございます。

やっくるさんがご利用できるかはわかりませんが、もし学生ならMSのDreamSparkに入ってProfessional版を使えるようにするといいんじゃないでしょうか。
処理落ちの原因を探るとき、重い処理を探すときなんかはクリティカルに役に立ちます。いちいちデバッグ出力する手間が省けるうえに、
思ってもみなかったところの

国際学生証云々とか書いてありますが

マイクロソフトDreamSparkの学生向けページ
アカウント作成中の学生認証の設問「アカデミック資格アカウントをどの方法で検証しますか」で、「学校を経由して検証を受けます」を選択し、学校名で検索、提供された電子メール アドレスを入力して学生認証を受けて下さい。学校名が表示されない場合は「自分の学校が見当たりませんか」をクリックすることで市町村や機関別で検索ができます。それでも見つからない場合は「手動検証リクエスト」から手動で行って下さい。この場合、学生証のコピーと現在の成績証明書または課程一覧のコピーの両方を添付する必要があります。

僕は高校の学生証をスキャナで取り込んで送ったら認証が通りました。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 9年前
住所: 東海地方
連絡を取る:

Re: Objectを大量に描画したいが…重い。なにかが悪い。

#29

投稿記事 by softya(ソフト屋) » 5年前

うずら さんが書きました:
オフトピック
ソフト屋さん、回答ありがとうございます。

やっくるさんがご利用できるかはわかりませんが、もし学生ならMSのDreamSparkに入ってProfessional版を使えるようにするといいんじゃないでしょうか。
処理落ちの原因を探るとき、重い処理を探すときなんかはクリティカルに役に立ちます。いちいちデバッグ出力する手間が省けるうえに、
思ってもみなかったところの

国際学生証云々とか書いてありますが

マイクロソフトDreamSparkの学生向けページ
アカウント作成中の学生認証の設問「アカデミック資格アカウントをどの方法で検証しますか」で、「学校を経由して検証を受けます」を選択し、学校名で検索、提供された電子メール アドレスを入力して学生認証を受けて下さい。学校名が表示されない場合は「自分の学校が見当たりませんか」をクリックすることで市町村や機関別で検索ができます。それでも見つからない場合は「手動検証リクエスト」から手動で行って下さい。この場合、学生証のコピーと現在の成績証明書または課程一覧のコピーの両方を添付する必要があります。

僕は高校の学生証をスキャナで取り込んで送ったら認証が通りました。
やはり、DreamSparkで入手できるVisualStudioPro版が前提なのですね。
DreamSparkで入手した物では商用ソフト販売禁止(同人ソフトを含む)ですので、それさえ理解されていれば良いかと思います。
学生でない場合はDreamSparkが利用できません。

【補足】 正式認可を受けた教育機関の課程 (高等学校・専門学校・専修学校・高等専門学校・大学) となっているので小学生・中学生が対象外です。職業訓練校もかな?
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

たいちう
記事: 418
登録日時: 9年前

Re: Objectを大量に描画したいが…重い。なにかが悪い。

#30

投稿記事 by たいちう » 5年前

色々アドバイスが寄せられていますが、質問者の当初の目的に合っているか疑問に思いましたので、
少し別の視点から書いてみます。ゲーム作成は未経験なので、プログラミングの一般論からです。


[まず前提の確認]

色んな実装方法があって、効率面でも一長一短あるでしょうけど、
やっくるさんの「遅い」という症状は、そんなレベルではないですよね?
何百行のメソッドがあろうが、1つのクラスで管理しようとしていようが、
そのこと自体が直接引き起こす性能上の問題は微々たるものだと思います。


[考えられる原因]

現状で遅くなっている原因については、ざっくりと2通り考えられます。
1つは、何らかの単純な不具合が直接の原因であり、プログラムが煩雑であるため、
やっくるさんがまだ原因を見つけることができていないという可能性。
この場合、原因を特定できさえすれば、今のプログラムからほとんど変えずに、
劇的に改善されるかもしれません。

もう1つは、プログラムの構造自体が遅い原因であるという可能性。
この場合は、改善は比較的手間がかかります。


[解決への方針(1)]

可能ならば、プログラム全体をアップロードしてみてはいかがでしょう。
実際に症状が発生しているプログラムが手元にあれば、
デバッグしてくれる上級者もいると思います。
逆に、プログラムの断片と、やっくるさんの説明のみでは、上級者でも原因特定は困難です。
(softyaさんが書いていますね。)

掲示板というものの性質上、望む回答が得られない可能性は理解しておいてください。
(期日までに絶対解決が必要なら、信頼できる友人や有償のサービスを探してください。)

アップロードする場合は、中間ファイルを削除したり圧縮したり、
ファイルサイズに気を付けましょう。方法がわからない場合は、質問すると良いでしょう。


[解決への方針(2)]

プログラムを公開できない場合、上級者がやってくれたかもしれないデバッグ作業を
やっくるさんが自分ですることになりますが、自分でデバッグできなかったからこそ、
ここで質問しているのだと思います。つまり、今のままでは不可能な方法です。

デバッグの一番の敵は、先入観だと思います。
自分を信用するのではなく、信頼できる手順を身に着けてください。
また、「よくわからないけど、こう変えたら治った」というのも禁物です。
学習の機会を逃しますし、より複雑になって再現することも多いです。

デバッグツールを使いこなせれば、速やかに鮮やかに原因特定することもできるかもしれませんが、
単純だけど強力な方法を紹介します。

『「不具合が発生する」という状態を保ったまま、プログラムをどんどん小さくする』です。
しっかりバックアップを取って、不具合と関係ない部分のプログラムを削除していきます。
例えば、BGMとか、2面以降とか、無関係と思われる部分を削除して、不具合が再現するか確認します。

不具合が再現したら、さらに別の部分を削ります。
再現しなくなったら、そのとき削除した部分を検討してみてください。
不具合の直接の原因かもしれないし、別の個所で発生していた不具合が表面化するきっかけだったりします。
少し考えてわからなければ一旦諦めて、プログラムの別の部分を削りましょう。

この過程で不具合の原因が判明することも多いし、
ある程度小さくなった段階で、公開可能になるかもしれません。


[解決への方針(3)]

「頭痛がするから痛み止めを飲む」のではなく、「規則正しい生活をする」という方法です。
ご自分でも自覚があるようですが、今のプログラムはあまり良くありません。
優秀な初心者が陥りやすい、といったら慰めになるでしょうか。
もっと楽をする方向に精進してください。
この方針を選ぶ場合に限り、色々ある実装方法のどれを選ぶか、
どの順番に改善していくかを考える必要があります。

最初に書いたとおり、これが不具合の直接の原因ではないと思いますが、
より良い、より管理のしやすいプログラムに書き換えることで、
直接の原因に気づいたり、不具合が自ずと解決したりすることも多いです。

ただし、深刻な頭痛だったら生活習慣を改める前に医者に行くように、
プログラムについても、明日提出(納品)とかいう場合には、悠長なことは言っていられません。



解決への方針を3案書きました。その他の案や複合的な案もあると思いますが、
行き詰った時などに、どの方法がベストか考えてみてはどうでしょうか。

閉鎖

“C言語何でも質問掲示板” へ戻る