C++OpenSiv3D入門講座 Vol. 03 vectorの基本
今回は動的配列vectorの基本について学ぶ。 演習では、OpenSiv3Dを用いて複数の敵を生成する。
vectorの基本的な使い方
C言語の配列は固定長だったが、C++には必要に応じて自動的にサイズを変更してくれる配列、vectorがある。 vectorの要素には配列と同じように[]でアクセス出来る。 vectorを使うときはvectorヘッダをインクルードする。(vector.hでないことに注意!)
#include <iostream> #include <vector> int main(){ std::vector<int> vec;//int型の動的配列 // 10個の要素を追加していく for(int i = 0; i < 10; i++ ){ vec.emplace_back(i); } //出力 for(int i = 0; i < vec.size(); i++ ){ std::cout << vec[i] << " "; } std::cout << std::endl; std::cout << "現在のサイズ : " << vec.size() << std::endl; return 0; }
書き方
std::vector<動的配列の中に入れる型> 動的配列の変数名
のように書く。
例:double型の動的配列ary1を作成する場合 std::vector<double> ary1;
追加の方法
動的配列名.emplace_back(追加するデータ)
のように書く。
例:int型の動的配列aryに 1を入れるとしたら ary.emplace_back(1);
また、クラス名{ コンストラクタ引数 }
でクラスの実体を作成できることを利用して、以下のように書くこともできる。
例:Vector2型の動的配列aryに(1, 3)のVector2を入れるとしたら ary.emplace_back(Vector2{ 1, 3 }); (Vector2{ 1, 3 }でVector2型の実体を作成し、それをaryに入れている。)
配列のサイズの取得
動的配列名.size()
クラスのvector例
#include <iostream> #include <vector> class MyClass{ public: int x; MyClass(int _x) : x{ _x } { } }; int main(){ std::vector<MyClass> vec; //要素を追加 for(int i = 0; i < 10; i++){ vec.emplace_back(MyClass{ rand()%10 }); } //要素MyClassのxを表示 for(int i = 0; i < vec.size(); i++){ std::cout << vec[i].x << " "; } std::cout << std::endl;//改行 return 0; }
Tips:インスタンスの作成と同時に関数を呼ぶ例
以下の例では、色を表すMyColorクラス、円を表すMyCircleクラスを用意し、インスタンスを作成しそのまま関数の引数に入れる例と、インスタンスの作成と同時に関数を呼ぶ例を示している。このように、C++はCに比べ、エレガント(変態的ともいう)な表記が可能になる。
#include <iostream> // 色を表すクラス (赤成分,緑成分,青成分):(r,g,b) class MyColor { public: int r, g, b; MyColor(int _r, int _g, int _b) : r{ _r }, g{ _g }, b{ _b } { } }; // 円を表すクラス 座標:(x,y)、半径:r class MyCircle { public: int x, y, r; MyCircle( int _x, int _y, int _r ) : x{ _x }, y{ _y }, r{ _r } { } // 自分の情報と、引数にとったMyColorの情報を出力する関数 void showSelfAndColor(const MyColor& color) { std::cout << "Circle:(x, y, r) = (" << x << "," << y << "," << r << ")" << " Color:(r, g, b) = (" << color.r << "," << color.g << "," << color.b << ")" << std::endl; std::cout << std::endl; } }; int main() { // MyCircleのインスタンスがmyCircleA変数に、MyColorのインスタンスがmyColorA変数に入り、それをshowSelfAndColorで表示する MyCircle myCircleA{ 10, 10, 5 }; MyColor myColorA{ 70, 80, 90 }; myCircleA.showSelfAndColor(myColorA); // MyCircleのインスタンスがmyCircleB変数に入り、MyColorのインスタンスは生成とともにshowSelfAndColorの引数となり表示される MyCircle myCircleB{ 20, 20, 6 }; myCircleB.showSelfAndColor(MyColor{ 100, 110, 120 }); // MyCircleのインスタンスの生成と同時にshowSelfAndColor関数を呼ぶ。 // またMyColorのインスタンスも生成とともにshowSelfAndColorの引数となり表示される MyCircle{ 30, 30, 7 }.showSelfAndColor(MyColor{ 200, 210, 220 }); return 0; }
まとめ
クラス名{ コンストラクタ引数 }
でクラスの実体を作成できるstd::vector<動的配列の中に入れる型> 動的配列の変数名
のようにしてvectorを作成する動的配列名.emplace_back(追加するデータ)
のようにしてvectorに要素を追加する。動的配列名.size()
のようにしてvectorのサイズを取得する。
演習問題(コンソール)
- int型のvectorを用意し、ランダムに0~9の数を10個入れて、すべて表示せよ。 ヒント:ランダムな0~9の数を生成するにはrand()%10とすればよい。
-
以下のようなクラスMyClassを用意した。MyClassのvectorを用意し、vectorにMyClassを10個追加し、すべての要素のshow()関数を呼び出せ。xの値はランダムな数でよい。
class MyClass{ public: int x; MyClass(int _x) : x{ _x } { } void show(){ std::cout << "xは:" << x << std::endl; } };
演習問題(OpenSiv3D)
今回は、前回のプロジェクトからそのままEnemyクラスを書き換えてもいいし、1から作り直しても良い。
-
以下の様なEnemyクラスを用意した。Enemyクラスのインスタンスを作り、動作を確認せよ。
#pragma once #include <Siv3D.hpp> class Enemy { public: Vec2 pos; Vec2 velocity; Enemy(Vec2 _pos); void update(); void draw(); };
#include "Enemy.h" Enemy::Enemy(Vec2 _pos): pos{ _pos }, velocity{ 0.0, 50.0 } { } void Enemy::update() { pos += Scene::DeltaTime() * velocity; } //エネミー(円)を描画 void Enemy::draw() { Circle{ pos, 30.0 }.draw(Color{ 255, 0, 0 }); }
- Enemyのvectorを作り、Enemyが複数出られるようにせよ。
-
Zキーが押されたらランダムな座標に敵が出るようにせよ。
ヒント:以下のようにしてシーン内のランダムな座標を取得することができる。
RandomVec2(Scene::Size().x, Scene::Size().y)
- Enemyを管理するEnemyManagerクラスを作る。
現在、メインループ内でEnemyのvectorをfor文で回していると思うが、メインループ内はできるだけすっきりさせたい。そこで、Enemyのvectorをメンバに持ち、updateを呼ぶとメンバのEnemyのupdateをすべて呼ぶEnemyManagerクラスを作り、メインループ内ではEnemyManagerのupdateを呼ぶだけでEnemyすべてが更新され、EnemyManagerのdrawを呼ぶだけですべての敵が描画されるようにする。また、add関数を持ち、それが呼ばれると敵を追加する。
EnemyManagerクラスを作り、敵を管理せよ。#pragma once #include <vector> #include <Siv3D.hpp> #include "Enemy.h" class EnemyManager { public: std::vector<Enemy> enemies; void update(); void draw(); void add(Vec2 pos); };
#include "EnemyManager.h" void EnemyManager::update() { // ここを実装 } void EnemyManager::draw() { // ここを実装 } void EnemyManager::add(Vec2 pos) { // ここを実装 }
Tips
OpenSiv3Dにはvectorと同じように使える動的配列、Arrayがある。本資料ではC++とSTLしか使えない極限の環境を想定して今後もvectorを使うが、Arrayはvectorより要素の削除が簡単に行えるので、Arrayも確認してみよう。