コンピュータプログラミングII (8)

モノをひとまとまりとして捉える

このスライドの使い方

テキスト等がはみ出した場合は、フォントサイズを小さくして調整する。 「S」 キーで小さく、「B」 キーで大きくなる。また、 下記のキーボード操作が使用できる。(一部キー操作は IE のみ対応)

「←」 or 「Page Up」 前のスライドに戻る
「→」 or 「Page Down」 or「スペース」 次のスライドに進む
「Home」 and 「End」 先頭(Home)または最後(End)のスライドへ移動
「C」 or 「contents?」 をクリック スライド一覧の表示
「F11」 or 「Ctrl+Shift+F (Win)」, 「Cmd+Shift+F (Mac)」 フルスクリーン表示と通常表示の切り替え
「F」 フッタの表示と非表示の切り替え
「A」 全スライド表示に切替 (印刷時に使う)
「S」「-」/「B」「+」 フォントサイズの大(B) 小(S)

本日のゴール

復習: 第6回 演習問題: 問題6

ボールの現在位置と速度の入った4要素の配列を用意する。 その配列を与えると、その内容を次時刻のものに変更する関数 boundBall(float[] ball) を定義し、 第6回(前々回) 問題5を書き換えなさい。

ファイル名: ball_02.pde

復習: 第6回 演習問題: 問題6

1つのボールについての情報を1つの配列で保持。

final int X  = 0;
final int Y  = 1;
final int VX = 2;
final int VY = 3;

float[] ball0;                      // ボールの x, y, vx, vy を要素とする配列を参照する変数

  ball0 = new float[4];                         // ボールの x, y, vx, vy を要素とする配列
  ball0[X]  = random(d / 2, width - d / 2);     // ボールの中心のx座標
  ball0[Y]  = random(d / 2, height / 5);        // ボールの中心のy座標
  ball0[VX] = random(vx0min, vx0max);           // ボールの速度のx成分
  ball0[VY] = random(vy0min, vy0max);           // ボールの速度のy成分

復習: 第6回 演習問題: 問題6

1つのボールについての情報を1つの配列で保持。

配列を使う問題点 (1)

float[] ball0;                      // ボールの x, y, vx, vy を要素とする配列を参照する変数

  ball0 = new float[4];                         // ボールの x, y, vx, vy を要素とする配列
  ball0[X]  = random(d / 2, width - d / 2);     // ボールの中心のx座標
  ball0[Y]  = random(d / 2, height / 5);        // ボールの中心のy座標
  ball0[VX] = random(vx0min, vx0max);           // ボールの速度のx成分
  ball0[VY] = random(vy0min, vy0max);           // ボールの速度のy成分

同じ型の変数しかまとめることができない

配列を使う問題点 (2)

float[] ball0;                      // ボールの x, y, vx, vy を要素とする配列を参照する変数

  ball0 = new float[4];                         // ボールの x, y, vx, vy を要素とする配列
  ball0[0]  = random(d / 2, width - d / 2);     // ボールの中心のx座標
  ball0[1]  = random(d / 2, height / 5);        // ボールの中心のy座標
  ball0[2]  = random(vx0min, vx0max);           // ボールの速度のx成分
  ball0[3]  = random(vy0min, vy0max);           // ボールの速度のy成分

添え字が数値のままでは中身が何だかわからない

可読性が低い ... プログラムを書いた本人にしかわからない (しばらく経つと本人にもわからない)。

解決法 ... クラス

クラス (class)

ある対象に関する複数の変数をまとめて、1つの型とする。

class Ball {
  float x;          // 中心のx座標
  float y;          // 中心のy座標
  float vx;         // 速度のx成分
  float vy;         // 速度のy成分
}

このようにクラス定義をしておくことにより、 独自の型 Ball が使えるようになる。

この場合、クラス Ball に4つの変数が属している。 クラスに属しているものをメンバ(member)、 特に変数であるメンバをフィールド(field)と呼ぶ。

クラス - 配列との比較

new 演算子で float[] 型の配列を生成。

float[] ball0;

  ball0 = new float[4];                        // ボールの x, y, vx, vy を要素とする配列
  ball0[0] = random(d / 2, width - d / 2);     // ボールの中心のx座標
  ball0[1] = random(d / 2, height / 5);        // ボールの中心のy座標
  ball0[2] = random(vx0min, vx0max);           // ボールの速度のx成分
  ball0[3] = random(vy0min, vy0max);           // ボールの速度のy成分

new 演算子で Ball 型のボールを生成。クラスは自作の型として使える。

Ball aBall;

  aBall = new Ball();
  aBall.x  = random(d / 2, width - d / 2);     // ボールの中心のx座標
  aBall.y  = random(d / 2, height / 5);        // ボールの中心のy座標
  aBall.vx = random(vx0min, vx0max);           // ボールの速度のx成分
  aBall.vy = random(vy0min, vy0max);           // ボールの速度のy成分

クラス - モノの持つ性質をまとめる仕組み

モノの種類が何かによって、持つ性質(属性)が決まっている。

種類(分類)を表す class という構造により、 モノの属性をまとめることができる。

class Ball {
  float x;          // 中心のx座標
  float y;          // 中心のy座標
  float vx;         // 速度のx成分
  float vy;         // 速度のy成分
}

どんなボールであっても持つべきボール一般の性質を表す情報を、 ブロックの中にまとめている。

Ball はクラス名で、好きに付けてよい。 ただし大文字で始めるのがマナー。

クラスとオブジェクト

Ball aBall = new Ball();

右辺で生成された Ball オブジェクトを変数 aBall が参照している。

オブジェクトの変数の型宣言と生成

変数の型宣言

int[] a;      // 変数 a は int[] 型の変数であることの宣言
Ball aBall;    // 変数 aBall は Ball 型の変数であることの宣言

配列と同じ参照型。変数が用意されても参照先(実体)はまだない。

オブジェクトの生成

Ball ball0, ball1;                  // Ball 型の変数を2つ宣言

ball0 = new Ball();                // Ball オブジェクトが1つ生成され、
                                    // それが変数 ball0 の参照先になる。
ball1 = new Ball();

オブジェクトはいくつでも生成することができる。

クラス Ball の定義は、プログラムのどこかにあればよい (別のファイルにすることもできる)。

練習: 2次元ベクトルを表すクラス Vector2D

2次元ベクトルを表すクラス Vector2D を定義し、以下のプログラムが動作するようにしなさい。

void setup() {
  Vector2D v1 = new Vector2D();
  Vector2D v2 = new Vector2D();
  Vector2D v3 = new Vector2D();

  v1.x = 1.0;
  v1.y = 2.0;

  v2.x = 3.0;
  v2.y = 4.0;

  v3.x = v1.x + v2.x;
  v3.y = v1.y + v2.y;

  println("v1 + v2 = (" + v3.x + ", " + v3.y + ")");
}

// ここに Vector2D クラスの定義を記述する

...

練習: 複数枚のタイル

四角いタイルを表すクラス Tile を定義し、複数枚のタイルを表示するプログラムを作成しなさい。 タイルの属性として、中心座標(x,y)、一辺の長さl、色相hue を用意すること。 これ以外の属性を追加してもよい。 各 Tile オブジェクトの属性(フィールド)に適当な値をセットし、 描画時にそれらの値を取り出して、タイルを画面に描画すること。 画面サイズは適当に決めてよい。

復習: rectMode(CENTER) とすると、rect(中心座標x, 中心座標y, 幅, 高さ) で四角形を描画することができる。

例: 重力で落下するボール

プログラミング入門の第3週「さまざまなアニメーション: 重力」において、 重力で落下するボールのアニメーションがあった。 また、第6週の問題1で、 1秒間あたりのフレーム数 fps、ボールの直径 d、 ボールのx座標 x、ボールのy座標の初期値 y0、 ボールの初速度 vy0 を変数化した。

ファイル名: ball_y1.pde

例: 重力で落下するボール

// constants
final float fps = 60;               // 1秒間あたりのフレーム数
final float gravity = 9.8 / fps;    // 1フレームあたりの重力加速度
final float d = 40;                 // ボールの直径
final float x = 100;                // ボールのx座標
final float y0 = d / 2;             // ボールのy座標の初期値
final float vy0 = 4;                // ボールの初速度

// variables
float y;                            // ボールのy座標
float vy;                           // ボールのy方向の速度

void setup() {
  frameRate(fps);
  size(200, 480);
  noStroke();
  fill(0, 0, 0);
  y = y0;
  vy = vy0;
}

void draw() {
  background(255, 255, 255);
  vy += gravity;                    // 速度に重力加速度を加算する
  y += vy;                          // ボールが速度ぶん移動する
  ellipse(x, y, d, d);
}

例: 重力で落下するボール

クラスの導入

ボールの性質として、まずは直径に着目する。

class Ball {
  float d;    // ボールの直径
}

このクラスを利用するように、プログラムを書き換える。

例: 重力で落下するボール

// constants
final float fps = 60;               // 1秒間あたりのフレーム数
final float gravity = 9.8 / fps;    // 1フレームあたりの重力加速度
final float d = 40;                 // ボールの直径
final float x = 100;                // ボールのx座標
final float y0 = d / 2;             // ボールのy座標の初期値
final float vy0 = 4;                // ボールの初速度

// variables
float y;                            // ボールのy座標
float vy;                           // ボールのy方向の速度
Ball aBall;                         // ボールを参照する変数を1つ用意

void setup() {
  frameRate(fps);
  size(200, 480);
  noStroke();
  fill(0, 0, 0);
  aBall = new Ball();              // ボールの実体を1つ生成
  aBall.d = d;                      // ボールの直径をセット
  y = y0;
  vy = vy0;
}

void draw() {
  background(255, 255, 255);
  vy += gravity;                    // 速度に重力加速度を加算する
  y += vy;                          // ボールが速度ぶん移動する
  ellipse(x, y, aBall.d, aBall.d);
}

関数 setup の中で変数 aBall の型宣言をすると、変数 aBall が関数 draw で使えなくなる。

注: もしアニメーションでない場合には、setup の中でしか aBall を使わなくなるので setup の中で型宣言してよい。 変数のスコープを考えて判断すること。

練習: 重力で落下するボール (属性: 直径, y座標, 速さ)

直径(d)だけでなく、中心のy座標(y)、y方向の速さ(vy) についてもボールの属性として扱いなさい。

オブジェクトの配列

int などの他の型と同じ書き方で、Ball 型変数の配列をつくることができる。

Ball[] balls = new Ball[10];                 // ボールへの参照を要素とする配列を生成

for(int i = 0; i < balls.length; i++) {
  balls[i] = new Ball();
}

オブジェクトの配列

配列の1要素 balls[i] が1つのボールを表している。 その各フィールド(変数)にアクセスするには、その後ろに「.フィールド名」をつける。

balls[0].x = 4;      // 0番目のボールの中心のx座標への代入
balls[0].y = 7;      // 0番目のボールの中心のy座標への代入

まとめ: クラスとオブジェクト

モノ(object)に着目してプログラムを作成するやり方を オブジェクト指向プログラミング(OOP: Object Oriented Programming)と呼ぶ。