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

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

このスライドの使い方

テキスト等がはみ出した場合は、フォントサイズを小さくして調整する。 「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)

本日のゴール

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

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

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

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

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

復習: 床でバウンドするボール

第6週の演習問題: 問題2 で作成した(ball_y2.pde)。 このときはまだクラスを導入していない。

第6週 演習問題: 問題2 - 解答例

// constants
final int fps = 60;                 // 1秒間あたりのフレーム数
final float gravity = 9.8 / fps;    // 1フレームあたりの重力加速度
final float elas = 0.98;            // 反発係数
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;                  // ボールが速度ぶん移動する
  if (y > height - d / 2) {
    vy = - vy * elas;
    y = height - d / 2;
  }
  // ボールの描画
  ellipse(x, y, d, d);
}

練習1: クラスの導入・直径の属性化

ボールを表すクラス Ball を導入する。ボールの属性として、まず直径 d を用意する。

class Ball {
  float d;          // 直径
}

このクラスを使うよう、プログラムを書き換えなさい。

練習1: クラスの導入・直径の属性化

// constants
final int fps = 60;                 // 1秒間あたりのフレーム数
final float gravity = 9.8 / fps;    // 1フレームあたりの重力加速度
final float elas = 0.98;            // 反発係数
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;                  // ボールが速度ぶん移動する
  if (y > height - aBall.d / 2) {
    vy = - vy * elas;
    y = height - aBall.d / 2;
  }
  // ボールの描画
  ellipse(x, y, aBall.d, aBall.d);
}

練習2: 位置・速度の属性化

クラス Ball の属性として、中心の位置 x, y と、速度 vx, vy を追加しなさい。 なお、この問題ではボールが y 方向にしか動かないので、x 方向の速度 vx は常に 0 となる。

class Ball {
  float d;                            // ボールの直径
  float x;                            // ボールのx座標
  float y;                            // ボールのy座標
  float vx;                           // 速度のx成分
  float vy;                           // 速度のy成分
}

このクラスを使うよう、プログラムを書き換えなさい。

練習2: 位置・速度の属性化

// constants
final int fps = 60;                 // 1秒間あたりのフレーム数
final float gravity = 9.8 / fps;    // 1フレームあたりの重力加速度
final float elas = 0.98;            // 反発係数
final float d = 40;                 // ボールの直径
final float x = 100;                // ボールのx座標
final float y0 = d / 2;             // ボールのy座標の初期値
final float vx0 = 0;                // ボールの初速度のx成分 (追加)
final float vy0 = 4;                // ボールの初速度のy成分

// variables
Ball aBall;                         // ボールを参照する変数を1つ用意

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

void draw() {
  background(255, 255, 255);
  // ボールの移動
  aBall.vy += gravity;            // 速度に重力加速度を加算する
  aBall.y += aBall.vy;                  // ボールが速度ぶん移動する
  if (aBall.y > height - aBall.d / 2) {
    aBall.vy = - aBall.vy * elas;
    aBall.y = height - aBall.d / 2;
  }
  // ボールの描画
  ellipse(aBall.x, aBall.y, aBall.d, aBall.d);
}

モノの持つ性質は属性だけではない

モノには動作がある。

属性は変数で表現することができた。動作は関数で表現することができる (これをメソッドと呼ぶ)。

メソッド

属性はクラスに属する変数で表現。動作はクラスに属する関数で表現 (これをメソッドと呼ぶ)。

class Ball {
  float d;          // 直径
  float x;          // 中心のx座標
  float y;          // 中心のy座標
  float vx;         // 速度のx成分
  float vy;         // 速度のy成分
  void move() {
    x += vx;
    y += vy;
  }
}

クラスのメンバとして、1フレームにおけるボールの動きを表すメソッド move() を所属させている。

メソッドの呼び出し

Ball aBall = new Ball();        // Ball クラスのオブジェクトを生成

  aBall.x  = random(30, width-30);   // 中心のx座標
  aBall.y  = random(30, 50);         // 中心のy座標
  aBall.vx = random(-7, 5);          // 速度のx成分
  aBall.vy = random(4, 15);          // 速度のy成分

  aBall.move();

オブジェクトに対するメソッドの呼び出しは、 そのオブジェクトにメッセージを送っているとみなすことができる。
「aBall さん、move してね」

メソッドと変数のスコープ

class Ball {
  float d;          // 直径
  float x;          // 中心のx座標
  float y;          // 中心のy座標
  float vx;         // 速度のx成分
  float vy;         // 速度のy成分
  void move() {
    x += vx;
    y += vy;
  }
}

メソッドは呼び出されなければ実行されず、定義する順序には意味がない。

練習3: 移動・描画のメソッドの導入

練習2のプログラムに、移動のメソッド move, 描画のメソッド draw を追加しなさい。

class Ball {
  float d;                            // ボールの直径
  float x;                            // ボールのx座標
  float y;                            // ボールのy座標
  float vx;                           // 速度のx成分
  float vy;                           // 速度のy成分
  void move() {
    // ?
  }
  void draw() {
    // ?
  }
}

練習3: 移動・描画のメソッドの導入

class Ball {
  float d;                            // ボールの直径
  float x;                            // ボールのx座標
  float y;                            // ボールのy座標
  float vx;                           // 速度のx成分
  float vy;                           // 速度のy成分
  void move() {
    vy += gravity;            // 速度に重力加速度を加算する
    y += vy;                  // ボールが速度ぶん移動する
    if (y > height - d / 2) {
      vy = - vy * elas;
      y = height - d / 2;
    }
  }
  void draw() {
    ellipse(x, y, d, d);
  }
}

練習3: 移動・描画のメソッドの導入

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

// variables
Ball aBall;                         // ボールを参照する変数を1つ用意

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

void draw() {
  background(255, 255, 255);
  // ボールの移動
  aBall.move();
  // ボールの描画
  aBall.draw();
}

アクセサメソッドとキーワード this

class Ball {
  float d;          // 直径
  float x;          // 中心のx座標
  float y;          // 中心のy座標
  float vx;         // 速度のx成分
  float vy;         // 速度のy成分
  float getX() {
    return x;
  }
  void setX(float x) {
    this.x = x;
  }
}

練習4: アクセサメソッドの導入

練習3 のプログラムのクラス Ball にアクセサメソッドを導入しなさい。

class Ball {
  ...
  void setD(float d) {
    this.d = d;
  }
  ...
}

これを利用するように、setup 関数も書き換えなさい。

練習4: アクセサメソッドの導入

class Ball {
  float d;                            // ボールの直径
  float x;                            // ボールのx座標
  float y;                            // ボールのy座標
  float vx;                           // ボールのx方向の速度
  float vy;                           // ボールのy方向の速度
  void setD(float d) {
    this.d = d;
  }
  void setX(float x) {
    this.x = x;
  }
  void setY(float y) {
    this.y = y;
  }
  void setVX(float vx) {
    this.vx = vx;
  }
  void setVY(float vy) {
    this.vy = vy;
  }
  void move() {
    vy += gravity;            // 速度に重力加速度を加算する
    y += vy;                  // ボールが速度ぶん移動する
    if (y > height - d / 2) {
      vy = - vy * elas;
      y = height - d / 2;
    }
  }
  void draw() {
    ellipse(x, y, d, d);
  }
}

練習4: アクセサメソッドの導入

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

// variables
Ball aBall;                         // ボールを参照する変数を1つ用意

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

void draw() {
  background(255, 255, 255);
  // ボールの移動
  aBall.move();
  // ボールの描画
  aBall.draw();
}

例: 体が成長する人

体が成長するアニメーションを作成する。 胴体の高さ、胴体の幅、頭の直径についてはあらかじめ上限の値を設定する。

例: 体が成長する人

final float fps = 60;            // フレームレート
final int x = 200;               // 人の中心の x座標
final int y = 320;               // 人の足元の y座標
final float h0 = 40;             // 初期身長
final float w0 = 20;             // 初期体幅
final float d0 = 25;             // 初期の顔の直径
final float he = 240;            // 最終身長
final float we = 140;            // 最終体幅
final float de = 50;             // 最終の顔の直径
final float growthRate = 0.004;  // 成長率
Human someone;

void setup() {
  size(400, 400);                  // ウィンドウのサイズ
  frameRate(fps);
  noStroke();                      // 輪郭線を無効に
  colorMode(HSB, 360, 100, 100);   // カラーモードをHSBに
  someone = new Human();
  someone.h = h0;
  someone.w = w0;
  someone.d = d0;
}

例: 体が成長する人

/**
 *  人の成長を描く
 */
void draw() {
  background(0, 0, 100);        // 背景色(白)
  someone.grown();            // 人を成長させる
  drawHuman(someone);         // 人を描く
}

void drawHuman(Human a) {
  fill(35, 60, 100);
  ellipse(x, y - a.h, a.d, a.d);           // 顔
  fill(220, 87, 100);
  ellipse(x, y - a.h / 2 + a.d / 2, a.w, a.h); // 胴体
}

class Human {
  float h;
  float w;
  float d;
  void grown(){
    if ( h < he ) {
      h = h * (1.0 + growthRate);
    }
    if ( w < we ) {
      w = w * (1.0 + growthRate * 0.8);
    }
    if ( d < de ) {
      d = d * (1.0 + growthRate * 0.5);
    }
  }
}

アクセサメソッドを使ったり、関数 drawHuman を Human クラスのメソッドにしてみよう。

まとめ: クラスのメンバ