オブジェクトの更新方法について

このQ&Aのポイント
  • C++でのゲームのオブジェクト更新方法についてアドバイスをお願いします。
  • 現在の更新方法はオブジェクトの数が増えると記述が長くなり、効率が悪くなってくる可能性があります。
  • どのように改善すれば良いのか、効率的なオブジェクトの更新方法を教えてください。
回答を見る
  • ベストアンサー

オブジェクトをどう更新するのが良いのか解りません。

こんばんは、C++でのプログラミングについて質問させて頂きます。 現在DirectXライブラリを使って簡単なゲームを作っているのですが、ゲームのメインループの中で、各オブジェクトをどうやって更新するのが良いのかか解らず、アドバイスをもらえればと思い質問させて頂きました。 現在の更新方法は for(Check_Num=1;Check_Num<PL_Limit;Check_Num++) { PL_Array[Check_Num]->Action();//各オブジェクトを更新 OBJ_Array[Check_Num]->Action(); FIRE_Array[Check_Num]->Action(); } for(;Check_Num<OBJ_Limit;Check_Num++) { OBJ_Array[Check_Num]->Action(); FIRE_Array[Check_Num]->Action(); } for(;Check_Num<FIRE_Limit;Check_Num++) { FIRE_Array[Check_Num]->Action(); } Action関数には、表示を更新する、キー入力で移動する、接触したオブジェクトにダメージを与える、等といった各オブジェクトの動作が書かれています。 最初に定めたオブジェクトを生成できる上限の数まで、片っ端から全て更新していく、といった内容です。 また、オブジェクトの作成は void Player::Shoot_FireBall()//ファイアボール { FireBall* FireBall1 = new FireBall(5,Ref_x(),Ref_y(),charge,"FIREBALL",Ref_angle(),5,0,40,40,4,true,"Action/FireBall1.png",6,3,2,1.0,true,true); if(FIRE_num>=FIRE_Limit) FIRE_num = 1; FIRE_Array[FIRE_num++] = FireBall1; } といった関数を使い、特定のキーが入力された時に呼び出してオブジェクトを作成、上限に到達したら古いオブジェクトが入っていた配列を上書きして作る、といった内容です。 以上のようなソースコードを使ってやっていたのですが、このままオブジェクトの数(弾等の種類や敵の種類)が増えてくると、for文の中にその度に記述を追加していくとあまりにも長くなるような気がするのですが、この方法のままでやっていても大丈夫なのでしょうか・・? 後々効率が悪くなってくるような気がするのですが・・・、どうなのでしょうか、もし今の方法が悪いようでしたら、どう改善すれば良いのかを教えて頂ければと思います、宜しくお願いします。

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

  • ベストアンサー
  • BearCave
  • ベストアンサー率20% (189/909)
回答No.1

FORループですべてのオブジェクトを更新するという考え方は合っています。ただいくつか気になる点があったので書いておきますね。 -更新にAction()という関数を呼び出していますが、一般的には「Update()」という名前を使います。(もちろん個人の自由なんですが、複数人が関わるプロジェクトですと、一般的なネーミングを使ったほうが好ましいので。) -最初のFORループでPL配列を全部更新、2つ目のFORループでOBJ配列を全部更新、3つ目のFORループでFIRE配列を全部更新、と分けた方が良いです。今の方針だとそれぞれのLimit数を後で変更した場合にループの配置を変えたりする必要が出て面倒ですし、バグも出易いです。 -FORループのスタートが「1」になっていますが、「0」の間違いでしょうか?配列は「0」から始まるので。あと「FIRE_num=1;」の部分も同様に1からのスタートになっているので気になりました。 -FireBallクラスに「FireBall1.png」という画像ファイルを渡していますが、このクラスのコンストラクタで画像ファイルを読み込んでいるのでしょうか?だとするとFireBallを打つ度にファイルが読み込みされるので、スピード的にもメモリ的にも非常に効率が悪いです。普通、画像はすべてスタート時に読み込んでおいて、各オブジェクトには画像のポインターだけを渡します。FireBallが無数にあっても、画像は一つだけで済むようにしましょう。 -提示されているコードではFireBallを打つ度に「new」されていますが、使い終わったFireBallは「delete」されていないので、メモリリークが出ます。「FIRE_Array[FIRE_num] = FireBall1;」と上書きしてしまう前に「delete FIRE_Array[FIRE_num];」として上書き前のFireBallを削除しましょう。 もっと確実でシンプルな方法は、ゲームスタート時にFireBallを最大限分すべてnewして用意してしまうやり方です。クラスに「bool bShow;」というフラグを足して、これがtrueの時だけ更新したり描画したりするようにします。使い終わったらbShowをfalseにしてやり、新しいFireBallが必要な時はすでにfalseになっているオブジェクトを探してそれを再利用します。newとdeleteをする必要が無いので、メモリリークが起きません。

bloodfog
質問者

お礼

解答有難うございます、指摘された点の中で改善できそうなものは一通り改善してみました。 最初の3つについては改善できましたが、FireBallの画像とdeleteに関してはちょっとまだ試行錯誤中です、特にdeleteに関してはFireBallを生成するときに前回のFireBallをdeleteすることは出来たものの、プログムの最後に各配列をdeleteしようとするとエラーが出る、という状態ですが・・・、こちらはどうしても解決出来なければ、別途質問を投稿しようと思います。 また、FORループのスタートが1になっているのは、 いずれ列挙体名を使うかもしれないという考えと、列挙体を定義すると1から数字が当てはめられる、という記憶違いが原因です、以前から間違えていたのに気づいてはいましたが、修正しなければと思いつつも放置したままになっていたのでこの機会に直しました、有難うございます。

その他の回答 (2)

回答No.3

これは手厳しいご意見ですがw でもいずれもまっとうなことですので、ためしにどういう形にするかイメージをまとめてみますと 仮に PL_Array→players OBJ_Array→objects (質問文だけだと私にはどういうものかわからないので、もっと適切な名前があったらそうしてください) FIRE_Array→fires Action→Update(あるいはフレーム単位の操作であれば私ならDoFrameとかにするかもわかりませんが) とし PL_Limitとかの型名をT(通常では int や unsigned intなどの事)とすると for( T i=0; i<PL_Limit; ++i ) players[i]->Update(); for( T i=0; i<OBJ_Limit; ++i ) objects[i]->Update(); for( T i=0; i<FIRE_Limit; ++i ) fires[i]->Update(); ループとかの方はこんな感じでしょうか。 で、パフォーマンス(おもに速度)のために、画像の読み込みは1か所 元のコードはNULLチェックを行っていないので FIRE_Limit数分だけ最初っからゲーム終了までは確保しっぱなしにして置くようなコードになってる、と仮に仮定すると Shoot_FireBallでは void Player::Shoot_FireBall(){ for( T i=0; i<FIRE_Limit; ++i ){ if ( fires[i]->IsUsed() ) continue; fires[i]->SetMembersToReuse(更新するものだけの引数リスト); return; } } こういう感じになるかと思います。 ※このコードだと、限界数使用中だったら発生しないとかそういうことになります。 可変長にしたかったらその管理をコード化してください。 ただし >このままオブジェクトの数(弾等の種類や敵の種類)が増えてくると、for文の中にその度に記述を追加していくとあまりにも長くなるような 普通だったら継承を使えば解決出来そうな状況に感じます。 宣言部だけ概要を示すとこんな感じですかね 基底クラス class Object3D_Base { protected: float x, y, z; bool is_used; Object3D_Base(); public: struct REUSE_FUNCDATA { float x, y, z; その他諸々 }; virtual void DoFrame() = 0; //純粋仮想関数にする virtual void SetMembersToReuse( const REUSE_FUNCDATA* ) = 0; //ここは厄介ですがあくまで一例です bool IsUsed() const { return is_used; } virtual ~Object3D_Base(); }; 派生クラス class Player : public Object3D_Base { void DoFrame() override { /* Playerの場合の更新内容 */ } void SetMembersToReuse( const REUSE_FUNCDATA* fd ) override { /* Playerの場合のセット法 */ } public: Player(); ~Player(); }; こうしておけば 生成の時だけPlayer(あるいはその他の派生クラス)でnewして ポインタはObject3D_Baseとして統一できます。 Object3D_Base* p = new Player; p->DoFrame(); delete p; もしここまでで分かんない個所が一つでもあれば C++の入門書を1冊程度は読むことをお勧めします。 ちなみに私が最初読んだのは やさしい C++(高橋 麻奈著)でした。 かなり突っ込んだところまで学びたい場合は足りませんが ある程度の基本的な部分は、非常に分かりやすく書かれていると思います。

bloodfog
質問者

お礼

解答有難うございます。 Forループを一つに纏める方法を知りたかったので非常に助かりました、まだオーバーライドについては何となくしか解っていないので、ゲームを作成しつつ並行して参考書の方も読むようにしてみようと思います。 何時も詳しく教えて頂きとても助かっています、ありがとうございます。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.2

およそ #1 の通りですがちょっと蛇足: ・「オブジェクト指向」では, 一般にメッセージの名前は動詞を使い, オブジェクトの名前には名詞を使います. そうすれば, メッセージを送る式が 名詞 動詞 (以下目的語など) という形になり, 英語の文章とそろうからです. ・for ループの変数ですが, ここははっきり言って意味のある名前は不要です. 特に #1 で指摘されているように「種類ごとに for を分ける」なら, 現状のように「あとで使いまわす」必然性もなくなるので for ループごとで変数を定義する方がかえって読みやすいはずです. ・PL_Array とかも今一つかなぁ. 「配列である」ことは明らかなので, わざわざ「Array」とつけることもないでしょう. まあ, 「OBJ_Array」の適当さに比べればましですが. 「Array」が不要なのは同じだけど, さらに「OBJ」という「一般的すぎて何を意味するのか全く分からない」言葉を使っているのが「ダメさ」加減を増している. 「オブジェクト」というなら, 「すべてのもの」が「オブジェクト」として扱えるよう工夫すべき. ・ちなみに「何を意味するのか分からない」という点では, check とか test とかを使った名前もだいたいダメと思っていい. もっと具体的に「何を『チェック』するのか」とか「どのようなことを『テスト』するのか」とかを考えて名前を付けるべき. あと, こいつらはえてして動詞だと思ってしまうので, 今のプログラムのように変数名として使うのも避けた方がいい.

bloodfog
質問者

お礼

変数名に関してのご指摘有難うございます。 極力何のための変数なのか解りやすい名前を付けるように心がけてみます。

関連するQ&A

  • メンバ関数内でオブジェクトを作成する方法について

    こんばんは、C++のプログラミングについて質問させて頂きます。 現在、クラスを作って簡単なゲームプログラムを組んでいるのですが、メンバ関数内でクラスを宣言すると上手くオブジェクトを作成出来ないのです。 例えば、メンバ関数の外、メイン関数内で Player Player1 = Player(320,240,100,50,32,32,3,true,"Character/Character1_A.png"); PL_Array[1]=&Player1; FireBall FireBall1 = FireBall(PL_Array[1]->Ref_x(),PL_Array[1]->Ref_y(),PL_Array[1]->Ref_angle()); ACT_Array[1] = &FireBall1; とすると上手く動くのですが。 このPlayerクラスPlayer1のコンストラクター内で FireBall FireBall1 = FireBall(Ref_x(),Ref_y(),Ref_angle()); ACT_Array[1] = &FireBall1; とすると、FireBall1オブジェクトが生成されないのです。 この状態を改善するためにはどうすればいいのでしょうか、そもそもクラス内で他のクラスを作成する、といった動作自体があまり良く無いことなのでしょうか・・・ お時間がありましたら、お答え頂けると助かります、宜しくお願いします。

  • クラス内で、親ではない他のクラスへアクセスしたい。

    こんばんは、C++のプログラミングに関しての質問です。 現在、勉強がてら作っているゲームプログラミングで、躓いている部分があります。 現在、プレイヤーではないオブジェクトの描画を以下のソースコードで描画しています。 for(Check_OBJ=1;Check_OBJ<OBJ_num;Check_OBJ++) OBJ_Array[Check_OBJ]->Draw_Graph(PL_Array[1]->Ref_x(),PL_Array[1]->Ref_y()); OBJ_ArrayはObjectクラス、PL_ArrayはPlayerクラス、Ref_変数名 は変数名の値を返す関数です。 このソースコードを、Objectクラスのメンバ関数Action()の中に入れたいのですが、エラーが出てしまうのです、ソースコードは以下の通りです。 void Object::Action() { Draw_Graph(PL_Array[1]->Ref_x(),PL_Array[1]->Ref_y()); } エラー内容は error C2227: '->Ref_y' : 左側がクラス、構造体、共用体、ジェネリック型へのポインターではありません。 error C2027: 認識できない型 'Player' が使われています。 です。 Playerクラスが定義されてないと認識しているのかと思い、前方宣言もしてみたのですが改善できず、どうすれば良いのか解りません。 クラス内で他のクラスへアクセスするためには何か他の処理が必要なのでしょうか・・・? 解決方法をご存知の方がいたら教えて頂けると助かります、よろしくお願いします。

  • 画像をクラスにどうやって渡すべきかが解りません

    こんにちは、今回もC++のプログラミングについて質問させて頂きます。 現在勉強がてら作っている簡単なゲームで、処理を軽くするたびに画像ファイルを読み込む回数を減らそうとしています。、 プレイヤーのように最初から生成されているオブジェクトの場合はどうにか出来たのですが、プログラムの途中で生成するオブジェクトの場合、画像ファイルをどう読み込めば良いのか解らず困っています。 現在、炎の弾を作り出すメンバ関数 void Object::Shoot_FireBall(int Angle)//ファイアボール { FireBall* FireBall1 = new  FireBall(true,5,Ref_x(),Ref_y(),charge,"FIREBALL",Angle,5,0,40,40,4,true,Image_FireBall,1.0,true,true); if(Objects_Numbers>=Actions_Limit) { Objects_Numbers = Actions_Lower; delete Objects[Objects_Numbers]; } Objects[Objects_Numbers++] = FireBall1; } 上記のコードの FireBall(true,5,Ref_x(),Ref_y(),charge,"FIREBALL",Angle,5,0,40,40,4,true,Image_FireBall,1.0,true,true); の中の Image_FireBall をどこで定義すれば良いかが解らず困っています 以前までは void Player::Shoot_FireBall()//ファイアボール { FireBall* FireBall1 = new FireBall(5,Ref_x(),Ref_y(),charge,"FIREBALL",Ref_angle(),5,0,40,40,4,true,"Action/FireBall1.png",6,3,2,1.0,true,true); if(FIRE_num>=FIRE_Limit) FIRE_num = 1; FIRE_Array[FIRE_num++] = FireBall1; } といったように画像ファイルの名前を渡し、FireBallのコンストラクター内で画像を取得していたのですが、それでは処理が重くなると聞き、修正しようとしています。 最初は画像ファイルをメインループに入る前に読みこめば良いのかと思ったのですが、それでは有効範囲が違うので定義されていない識別子とコンパイラに認識され使用することが出来ません。 グローバル宣言を使えばなんとかなるとは思うのですが、グローバル宣言はあまり多用しない方が良いようなので、他の方法を考え現在はObjectクラスの基底クラスGraphの中で class Graph { public: protected: int *Image_FireBall; }; Graph::Graph(bool Sub_bShow,int sub_x,int sub_y,int XSize , int YSize, double Sub_ExtRate, double sub_Angle,int Sub_Trans_Flag,int Sub_Turn_Flag,int* sub_Image) { Image_FireBall = new int[6]; LoadDivGraph("Action/FireBall1.png", 6, 3 , 2 ,40 , 40 , Image_FireBall); } /* 関係の無いメンバやその初期化については省いて載せています */ のようにしていますが、コレでは結局FireBallオブジェクトが生成される度に読み込まれているので処理は変わらないと思い、他の方法を探している所です。 一体どうやって定義するのが良いのでしょうか、良い方法があれば教えて頂けると助かります、宜しくお願いします。

  • クラス内で作成したクラスへのアクセス方法。

    こんにちは、C++のプログラミングについて質問させて頂きます。 現在クラスを使って簡単なゲームを作ってみようと勉強中なのですが、クラスの扱いについて質問があります。 void Object::FireBall() {    //初期化の内容は読まなくても大丈夫です。 Action FireBall = Action(10,Ref_x() + cos(angle * PI / 180.0f)*3,Ref_y() + cos(angle * PI / 180.0f) *3 ,5,100,40,40,4,true,"Action/FireBall1.png",6,3,2,1.0,Ref_angle(),true,false); } といったように、Objectクラス内でActionクラスを作成したとします。 このとき、メイン関数内でFireBallの各メンバにはどうやってアクセスすれば良いのでしょうか? Objectクラスのオブジェクトの名前がPlayer[1]だとしたとき、 Player[1]->FireBall.Draw_Graph() のようにやるのかと思っていたのですが、上手くいかず、調べてもイマイチ解らず行き詰まっているのです(内部クラスとはまた別ですよね?)。 もし宜しければ、教えて頂けると助かります。

  • 【C++】オブジェクト配列の動的確保

    Objectがクラスとして、以下のようなプログラムを書きました Object **obj; obj = new Object*[100]; for(i=0;i<100;i++){     obj[i] = new Object(a,b,c);     obj[i]->Run();     delete obj[i]; } delete [] obj; するとdeleteの所でエラーになります。 (確保していない領域をdeleteしようとしたときのような) 何か間違っているのでしょうか。 何故かデバッガでは通ってしまいます。 環境:XP、VisualStudio2005 何卒よろしくお願いいたします。

  • ActionScriptで配列をクリアする方法を教えてください。

    ActionScriptで、オブジェクトを格納した配列をクリアする正しい方法を教えてください。 具体的には、 ・new Array() で配列を生成する。 ・new Object() で生成したオブジェクトを配列に格納する。 ・配列の中身を処理したあと、配列を一度クリアする。 ・配列に別のオブジェクトを格納する。 ような処理をしたいのですが、メモリリークを起こさずに配列をクリアするにはどうしたらいいでしょうか? 例えば、イメージはこんな感じです。(意味のないコードですが) // 配列の生成 var arr = new Array(); // 配列にオブジェクトを格納 for (var i = 0; i < 5; i++) { var obj = new Object(); obj.num = i; } // 配列のクリア arr.length = 0; // クリアした配列に別のオブジェクトを格納 for (var i = 0; i < 5; i++) { var obj = new Object(); obj.num = i * 2; } 上記のコードで、arr.length = 0; で配列をクリアしていますが、これで配列に格納されたオブジェクトが確保したメモリがすべて解放されるでしょうか? あるいは、配列の内容がなくなるまで pop() 等で取り出す等の処理をする必要があるでしょうか? 以上よろしくお願いします。

    • ベストアンサー
    • Flash
  • マウスの基準点について

    www.i-tv.in/test/shop_menu.swf 上記のものスクリプトの参考書を見ながら作りました。マウスが摩擦をおく基準が上の方になっています。 これを中央にもっていきたいと思いましたが、このような単純なことは調べても出てきません。どなかた教えて頂ければ幸いです。宜しくお願いします。 function start_me(success:Boolean):Void { if (success == true) { for (var i = 1; i<=menu_num; i++) { var menu_str:String = "menu"+i; _root.attachMovie("menu",menu_str,i); obj_array[i] = eval(menu_str); obj_array[i].num = i; obj_array[i].mctext = i; obj_array[i]._y = 125*(i-1); obj_array[i]._x = 10; obj_array[i]._alpha = 40; var jpg_str:String = obj_xml.firstChild.childNodes[i-1].firstChild; obj_array[i].jpg_mc.loadMovie(jpg_str); obj_array[i].onEnterFrame = scrollphoto_me; obj_array[i].onPress = press_me; obj_array[i].onRollOver = roll_me; obj_array[i].onRollOut = rollout_me; } } else { seltext = "xml read error"; } }

    • ベストアンサー
    • Flash
  • SetとNothingの存在意味?

    数値とオブジェクトの変数セットはそれぞれ、 Dim num as integer num = 5 Dim obj as Object set obj = new From set obj = Nothing ですが、 オブジェクトの場合でインスタンスを生成しない、単なる参照?別名? の場合はNothingは不要でしょうか? Dim obj as Object For x = 1 to 10 set obj = Form2 ' newとインスタンスを生成するのではなく、単なる参照  obj.Caption = str(x) & "回目" 'set obj = nothingは不要? Next x

  • オブジェクトの削除や更新クエリ時に確認画面がでない

    Sub メッセージ表示() DoCmd.SetWarnings True End Sub を実行したり、 ツール→オプション→編集/検索→確認 ・レコードの変更 ・オブジェクトの削除 ・アクションクエリ 全てにチェックを入れているのに確認画面がでてきません。 OS:2000、Access2003です。 何かわかる方よろしくお願いします。

  • documentオブジェクトのlengthが取れない

    以下のプログラムでobjのlengthが「undefined」となる原因がわからない <script language="JavaScript"><!-- function Check() {alert("スタート"); var obj = document.frm.elements['check']; var i,j =0;alert("length="+ obj.length); for (i = 0; i < obj.length ; i++ ) { if ( obj[i].checked == true ) {j++;} } alert( 'チェックボックスの数='+ i); alert( 'チェックした数='+ j); } //--></script> <form name="frm" method="POST"><input type="checkbox" name="check" value="1"></form> <a href="javascript:Check();">チェック</a> formの中を以下のように変更すると正しく表示される <form name="frm" method="POST"><input type="checkbox" name="check" value="1"><input type="checkbox" name="check" value="2"></form> どなたか、ご教授くださいませ。