次はオブジェクト指向言語プログラミングObject Oriented Programming (以下OOP)です。オブジェクト指向プログラミングとは、「オブジェクト単位によって設計された」プログラム、という意味です。
オブジェクト単位について、もう少し説明しましょう。例えば、人間は「呼吸する」、「食べる」、「寝る」などの機能を備えたオブジェクトです。また、車は「走る」、「止まる」、「曲がる」などの機能を備えたオブジェクトだといえます。また、これらの機能を「メソッド」といいます。
この考え方をプログラミングで設計することが、オブジェクト指向言語プログラミングの考え方です。
5.3.1 クラス、インスタンス、メソッド
クラスとは「ひな型」です。例えば、人間は必ずDNAの情報を引き継ぎ、親と似た形で生まれてきます。しかし、個性はそれぞれ兄弟で違いますね。プログラミングの場合には、まず人クラスを設計し、そしてそのクラスから必要に応じて実際の人間(Aさん、Bさん)を作り出します。この実際の人間のことをインスタンス(実体)と呼びます。このクラスを作ってしまえば、何人でも作り出すことができます。
そして、インスタンスはそれぞれメソッドを持っています。メソッドとは、関数(function)とほぼ同じで、「特定の機能をまとめたもの」です。人間だと「呼吸する」、「食べる」などです。
ここで注意点があります。一般的にはオブジェクトとインスタンスは同義語のように使われることが多いのですが、「オブジェクト」は広義的に使用されることも多く、人によって少しずつ定義が違う場合が多いので、混同を避けるために実際のプログラミングでは「クラス」、「インスタンス」、「メソッド」を使用します。
5.3.2 クラスの定義
それでは、実際のプログラムでは、クラスはどのように定義すればいいのでしょう。
次は、横方向に設定されたスピードで移動する円を作成するサンプルです。
まずは、クラスを定義します。ここで注意点です。クラス名の先頭は必ず大文字にしてください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | class Circle { float x, y, eSize, velocity; //初期化用メソッド(コンストラクタ) Circle (float _y, int _eSize, float _velocity) { x = 0.0; //x座標の初期値は0.0 y = _y; //y座標の初期値はインスタンスの引数 eSize = _eSize; velocity = _velocity; //velocityの初期値はインスタンスの引数 } //円の位置を更新するメソッド void update() { x += velocity; //velocity分のx座標を移動 if (x > width) { //円のx座標がウィンドウの幅を超えたら、 x = 0; //位置を0に戻す } } void display() { ellipse(x, y, eSize, eSize); //円を描く } } |
クラスを作成する際には、必ず初期化用のメソッド(コンストラクタ)を定義してください。
updateおよびdisplayメソッドを実行するたびに、円のx座標であるxPosの値にvelocityが足されていきます。最終的なコードは次の通りです【リスト5.3-b】。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | //Circleクラスのインスタンスを宣言 Circle c1, c2; void setup() { size(400, 400); noStroke(); fill(0); //黒で描画 //インスタンスを作成 c1 = new Circle(100, 10, 1.0); c2 = new Circle(300, 40, 3.5); } void draw() { background(255); //背景は白 //c1インスタンスのクラスであるCircleクラスのupdate(), //display()メソッドを実行 c1.update(); c1.display(); c2.update(); c2.display(); } //Circleクラスの宣言 class Circle { float x, y, velocity; int eSize; //初期化用メソッド(コンストラクタ) Circle (float _y, int _eSize, float _velocity) { x = 0.0; //x座標の初期値は0.0 y = _y; //y座標の初期値はインスタンスの引数 eSize = _eSize; //velocityの初期値はインスタンスの引数 velocity = _velocity; } //円の位置を更新するメソッド void update() { x += velocity; //velocity分のx座標を移動 if (x > width) { //円のx座標がウィンドウの幅を超えたら、 x = 0; //位置を0に戻す } } void display() { ellipse(x, y, eSize, eSize); //円を描く } } |
どうでしょうか?c3、c4を追加し、いくつもインスタンスを増やしてみましょう。
5.3.3 配列にインスタンスを入れる
さて、このように性質の違うインスタンスを作成することに成功したら、もっと数を増やしたくなるのが次の段階でしょう。この場合には配列を使います。
手順はすでに学習済みの配列の方法と同じなのですが、配列のそれぞれの箱にインスタンスを入れるのだと理解してください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | //40個のインスタンスを作成 Circle[] circles = new Circle[40]; void setup() { size(400, 400); noStroke(); fill(0); //黒で描画 for (int i = 0; i < circles.length; i ++) { circles[i] = new Circle(i*10, 5, i/10.0); } } void draw() { background(255); //背景は白 //c1インスタンスのクラスであるCircleクラスのupdate(), //display()メソッドを実行 for (int i = 0; i < circles.length; i ++) { circles[i].update(); circles[i].display(); } } //Circleクラスの宣言 class Circle { float x, y, velocity; int eSize; //初期化用メソッド(コンストラクタ) Circle (float _y, int _eSize, float _velocity) { x = 0.0; //x座標の初期値は0.0 y = _y; //y座標の初期値はインスタンスの引数 eSize = _eSize; //velocityの初期値はインスタンスの引数 velocity = _velocity; } //円の位置を更新するメソッド void update() { x += velocity; //velocity分のx座標を移動 if (x > width) { //円のx座標がウィンドウの幅を超えたら、 x = 0; //位置を0に戻す } } void display() { ellipse(x, y, eSize, eSize); //円を描く } } |
y座標も変化する円の集合を作ります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | //40個のインスタンスを作成 Circle[] circles = new Circle[40]; void setup() { size(400, 400); fill(0); //黒で描画 //インスタンスを作成 for (int i = 0; i < circles.length; i ++) { circles[i] = new Circle(random(width), random(height), 5, random(1.0, 10.0), random(1.0, 10.0)); } } void draw() { background(255); //背景は白 //インスタンスのupdateメソッドを実行 for (int i = 0; i < circles.length; i ++) { circles[i].update(); circles[i].display(); } } //Circleクラスの宣言 class Circle { float x, y, xVel, yVel; int eSize; //初期化用メソッド(コンストラクタ) Circle (float _x, float _y, int _eSize, float _xVel, float _yVel) { x = _x; //x, y座標の初期値はインスタンスの引数 y = _y; eSize = _eSize; xVel = _xVel; //xVel, yVelの初期値もインスタンスの引数 yVel = _yVel; } //円の位置を更新するメソッド void update() { x += xVel; //xVel, yVel分の座標を移動 y += yVel; if (x > width) { //円のx座標がウィンドウの幅を超えたら、 x = 0; //位置を0に戻す } else if (y > height) { //円のy座標がウィンドウの幅を超えたら、 y = 0; //位置を0に戻す } } void display() { ellipse(x, y, eSize, eSize); //円を描く } } |
5.3.4 メソッドの追加
次に、Circleクラスをもう少し書き換えてみましょう。ドラッグして円を投げると、投げた方向とスピードを記憶して、円がそのまま画面内をバウンスし続ける機能を追加します。それぞれ作成された円がマウスの投げるスピードによって、その円自体のスピードを決定するメソッドをCircleクラスに実装します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | int count = 0; //マウスクリックによって生まれる円の順番 //Circleクラスのcirclesインスタンスを宣言 Circle[] circles = new Circle[40]; //20個のインスタンスを作成 void setup() { size(400, 400); background(255); noStroke(); fill(0); // circlesインスタンスの作成と配列の初期化 for (int i = 0; i < circles.length; i++) circles[i] = new Circle(); } void draw() { background(255); //常にcircles[]を更新 for (int i = 0; i < circles.length; i++) { circles[i].update(); circles[i].display(); } } void mouseDragged () { circles[count].updatePos(mouseX, mouseY); } //マウスドラッグによるイベント void mouseReleased () { circles[count].updateVelocity(mouseX - pmouseX, mouseY - pmouseY); count ++; //順番に更新 if (count >= circles.length) count = 0; } // Circle クラスを作成 ////////////////////////////////////////////////////////// class Circle { float x; //円のx座標 float y; //円のy座標 float radius; //円の直径 float xVel; //x軸方向のスピード float yVel; //y軸方向のスピード //初期化用メソッド(コンストラクタ) Circle() { xVel = 0.0; //スピードの初期値は0.0 yVel = 0.0; x = width / 2; //x,yは画面の中心に設定 y = height / 2; radius = 20.0; //円のサイズを設定 } //円の描画更新用メソッド void update() { // velocityで設定された値を足す x += xVel; y += yVel; if (x > width || x < 0 ) xVel = -xVel; if (y > width || y < 0 ) yVel = -yVel; } //円の位置を更新するメソッド void updatePos(float _x, float _y) { x = _x; y = _y; } //円のスピードを更新するメソッド void updateVelocity(float _xVel, float _yVel) { xVel = _xVel; yVel = _yVel; } void display() { ellipse(x, y, radius, radius); //円を描画 } } |
Pingback: processing : オブジェクト指向についてメモ | メディアアートメモ | Media art memo