【4】Siv3Dで複数のオブジェクトを処理する
こんばんは、AdventCalendar4日目です。なんとか3日坊主にならずに済みました(まだ安心できない)。
私事ですが、昨日Siv3D開発者であるReputeless様よりお誘いいただき、Siv3D公式のAdventCalendar2016に参加することになりました。お声をかけていただきありがとうございました、この場をあげてお礼申し上げます!
Siv3D Advent Calendar 2016 - Qiita
私の担当は21日とさせていただきました。すでにいくつか上がっている記事や、昨年のAdventCalendarの記事を拝見させてもらいましたがとてもレベルが高い。はい。
…あと2週間強あるので、それまでに、満足いただける記事が書けるように頑張ります…!
というか、何らかしら公式のAdventCalendarに参加するという目標が達成できたの嬉しいw
今回は前回の記事でも言ったように、複数のオブジェクトを扱うときに必須となる手法を紹介させていただきます。知っている人にとってはあまりに当たり前な知識なのですが、基本的な知識として紹介させていただきます。
1つのオブジェクト(図形)を動かしたいとき
1つのオブジェクト、今回は円としましょうか、を動かしたい時、このようなプログラムが書けるでしょう。
# include <Siv3D.hpp> void Main() { Vec2 place(0,240); while (System::Update()) { Circle(place, 20).draw(Palette::Red); place += Vec2(1,0); } }
これはこれでいいのですが、オブジェクトを増やしていくことになるとプログラムの記述量がこんな感じに増えてしまいます。
# include <Siv3D.hpp> void Main() { //悪い例 Vec2 place1(0,240); Vec2 place2(0, 280); Vec2 place3(0, 200); while (System::Update()) { Circle(place1, 20).draw(Palette::Red); place1 += Vec2(1,0); Circle(place2, 20).draw(Palette::Green); place2 += Vec2(2, 0); Circle(place3, 20).draw(Palette::Blue); place3 += Vec2(3, 0); } }
そこまで記述量は増えてない気がしますが、仮にシューティングゲームを作るとして、オブジェクトが数百・数千となってしまうと大変なことになってしまいます。さらに例えば、オブジェクトに加速度を付けようなどとオブジェクトの機能を拡張しようとしたとき、コードの修正をオブジェクト毎にしなければならず、大変なことになってしまいます。
複数のオブジェクトを管理する
それでは、何をすれば複数のオブジェクトを管理しやすくできるのでしょうか。結論から言うと、構造体とリストを使えばコード管理が楽になります。
# include <Siv3D.hpp> using namespace std; struct MyCircle { private: int radius; Vec2 place; int color; public: MyCircle(int r, Vec2 p, int c) { radius = r; place = p; color = c; } void setRadius(int r) { radius = r; } void setPlace(Vec2 p) { place = p; } void setColor(int c) { color = c; } int getRadius(void) { return radius; } Vec2 getPlace(void) { return place; } int getColor(void) { return color; } }; void Main() { vector<MyCircle> vec; int radius = 20; Vec2 place(0,240); int count = 0; while (System::Update()) { if (count % 20 == 0) { MyCircle cir(radius, place, count); vec.push_back(cir); } int size = vec.size(); for (auto i = 0; i < size; ++i) { Circle(vec[i].getPlace(), vec[i].getRadius()).draw(HSV(vec[i].getColor())); if (i % 2 == 1) { vec[i].setPlace(vec[i].getPlace() + Vec2(2, 1)); } else { vec[i].setPlace(vec[i].getPlace() + Vec2(2, -1)); } } count++; count = count % 360; } }
今回はMyCircleという描写の為の変数を持った構造体を用意しました。こいつをvectorの中に追加していき、1つずつfor_loopで処理しています。vectorはstd内部の型のため、using namespace stdすることを忘れずに。
追加した後は、while (System::Update()){}内で好きなように処理してあげればおkです。特定のタイミングでvector内にpushするのもよし、pullするのもよし、値をいろいろいじるのもよし。なんだかシューティングゲームなんかが作れそうな気がしてきますね。
今回はvectorを使用しましたが、listを使っても同じように処理することができます。この辺は好みの問題でしょうか。アクセスの仕方によってどちらが優れているとかもありますが、基本的に扱いやすい方法を用いれば良いかと思います。listだと添え字が使えないことには注意ですね。
こんな感じで、ひとまずはシューティングゲームの完成を目標にしてみようかなと考えています。当たり判定であったりエフェクトであったりメモリ管理であったり、まだまだやることは盛りだくさんです。特に今のプログラムだと、一度生成したオブジェクトが画面の外にいった後でも永遠と処理を続けてしまっているので、メモリが足りなくなります。ずっと動かしているといつかフリーズしてしまうでしょう。
というわけで、次回はオブジェクトの消滅に関して書こうかなと思います。