[Go to “teaching” in Morimoto Lab]

Game by ball 5

Here we study class.



1. クラス、インスタンス、メソッド

あなたは自分でPVectorのようなデータの型を定義することができます。
そのデータの型には複数の変数や関数を持たせることができます。
これをクラスと言います。

(前回使ったPVectorはProcessingに用意されたクラスです)

クラス“クッキーの型”に例えられます。自作のクッキーの型を用意できたら、それでクッキーそのものを作る必要があります。このクッキーという実体のことをインスタンスと呼びます。

インスタンス (=クッキー)を作るために、newが必要です。

メソッドクラス内で定義された関数のことです。
また、クラス内の変数やメソッドのことを、そのクラスのメンバ変数、メンバメソッドなどと呼びます。

Processing公式のクラスのページ

Alt text

Note: オブジェクト指向プログラミング (Object-Oriented Programming: OOP)
オブジェクトとは、この場合、インスタンスのことを指します。
OOPはクラスの便利さがわかる考え方の一つなのですが、当講義ではあまりこの部分は深くつっこみません。(継承などは学びません)

OOPは変数やメソッドなどを一つのまとまりにし(クラスのインスタンス等のことですね)、そのまとまりを組み合わせたり、使いまわしたりすることで効率よくわかりやすいプログラミングを行うという考え方です。

他の考え方に手続き型プログラミングなどがあります。

参考:手続き型言語とは?
参考:Processingの公式ページ オブジェクトのチュートリアル

1.1 ボールクラスを作ろう:変数だけのボールクラス

Game by Ball 2で実装したボールプログラムなどを参考に、ボールクラスを実装しましょう。
まずは、変数だけのボールクラスの例を見てみましょう↓

void setup(){
size(500,500);
}
void draw(){
Ball ball;//定義
ball = new Ball();//インスタンスの生成
// ball.diam=100;
// ball.bx = 100;
// ball.by = 100;
ellipse(ball.bx, ball.by, ball.diam, ball.diam);//メンバ変数へのアクセス
}
//↓クラスを記述している部分です
class Ball{
// ↓ member variables (メンバ変数)
float bx;
float by;
float vx;
float vy;
float diam;
}

上記のプログラムを実行してもボールは表示されません。なぜでしょう?
→ボールの位置、速度、直径がすべて0になっているからです!

少なくとも直径を大きくすると、ボールが表示されそうです。どうやって直径を与えますか?
→setupやdrawでボールのdiamに値を代入しましょう!

※クラス内の定義のところでfloat diam=100;としてもボールが原点に表示されます!ただ、その方法だと2個以上ボールを作っても、全て同じ直径になるので、結局あとから値を指定しないといけません。

前回、PVectorではメンバ変数として.xや.yが使えましたが、自分でクラスを作るとメンバ変数を自由に増やせたり、カスタマイズすることができます。使い方はPVectorと同じです。

では、ball2を作ってballとは違うところに描画した上で、ballとball2の間に線を描画してみましょう↓

Alt text
※ball変数はこの課題ではあえてローカル変数にしています。


1.2 ボールクラスを作ろう:変数とメソッドのあるボールクラス

次はBallクラスにメソッドを追加しましょう。
PVectorにはaddやnormalizeなど、PVectorのメンバ変数に関するの便利なメソッドがたくさんありました。
今度は、
・メンバ変数の値の代入、
・ボールの描画、
・ボールの移動、
をクラス内のメソッドに記述します。ベースとなるプログラムは以下です↓
最初のプログラムとほぼ同じですが、クラス内にメソッドが追加されています。

void setup(){
size(500,500);
}
void draw(){
Ball ball; //1. 定義
ball = new Ball();//2. インスタンスの生成
//ball.diam=100;//初期値の代入☆
//ball.bx = 100;
//ball.by = 100;
//ellipse(ball.bx, ball.by, ball.diam, ball.diam);//3. メンバ変数へのアクセス
ball.display(); //4. メンバメソッドへのアクセス
}
//5. ↓クラスを記述している部分です
class Ball{
// 6. ↓ member variables (メンバ変数)
float bx;
float by;
float vx;
float vy;
float diam;
//7. ↓"constructor" (コンストラクタ)。省略可ですが、初期値の代入などに使われる
Ball(){
//diam = 100;//初期値の代入★
}
//8. ↓member methods (メンバメソッド)
void display(){
ellipse(bx, by, diam, diam);
}
void move(){
//適宜記述してください
}
}

上記サンプルコードの1-8までの説明です。

☆★初期値の代入はここで2通り書いていますが、どちらも実行する場合は★の方が先に処理されます。
どちらか片方だけを行う場合、結果はこの場合同じです。


Ex A. ボールの位置を指定する引数を持ったボールクラスのインストラクタを実装し、メンバ変数の初期値をコンストラクタ内で代入してください。

以下のように、クラスのインスタンスを生成するとき、newのあとに呼び出されているBall()がコンストラクタです。

Ball ball = new Ball();//定義 ←newのあとのBall()がコンストラクタを呼び出している部分です

コンストラクタは同じ名前でも引数を変えることで、複数定義できます
参考:関数のオーバーロード(多重定義、ポリモーフィズム)
以下は引数のあるコンストラクタの例になります。

Ball(float bx0, float by0){
bx = bx0;
...
}

この課題では、コンストラクタに引数を持たせて、位置を指定できるようにしましょう。
※PVectorもnew PVector(100,100)などとして、x,yの値を指定できましたね!それです。
余裕がある人はボールの速度やサイズ、また、新たにメンバ変数を追加して色なども指定してみましょう。


Ex B. ボールクラス内にボールを移動するメソッドを実装し、ボールを動かしてください。

ボールの動きはmove()に記述します。
動きの方はまずは簡単なものでも構いません。

ヒント:
ballはdraw内でnewすると、毎フレーム、ボールの位置が初期化されてしまいます。
毎フレーム、ボールの位置が初期化されず、値を保持するために、ボールはグローバル変数にする必要があります。
プログラムの構造としては第4回の1.3のとき(このときはPVectorで定義したボールの位置がグローバル変数になっている)などと同じようになりますので、参考にしてください。

Note: クラス内ではグローバル変数やグローバル関数を使わないようにしてください
プログラム的に可能ですが、学習の最初の段階では多くの人にとってミスリーディングになると思いますので、このようにお願いします。

場合によってはグローバル変数をクラス内で使う場合はありますし、そのクラスで作ったグローバルなインスタンスをクラス内で呼び出す場合もあります(Drawing&Pattern3のひまわりの参考プログラムはそのようなものになっています)。しかし、今回はそのようなことはしないようにしましょう。


Ex C. 配列を用いてボールをたくさん増やしましょう。

Ex Bを活かして、少しずつ違う場所を初期位置としたボールを描画してみましょう。

Note:
配列を作るとき、newが必要でした。またクラスのインスタンスを作るときもnewが必要です。なので、クラスの配列を作るときは “new”が二度必要になります。

以下はPVectorクラスを用いた配列の作成例。Ballクラスを使った場合も同様に配列を作ることができるので参考にしてください。

//1回目のnew。配列を作ります。
PVector [] pos = new PVector[10];
//2回目のnew。インスタンスを作ります。配列で作った全てのデータをnewするのでforを使っています。
for(int i=0 ; i < 10; i++){
pos[i] = new PVector();
}

Ex D. 少し横道:blendModeを使ってみよう。

blendMode(**)は色が重なった時にどう見えるかを指定することができ、簡単に様々な表現ができます。
**には以下のようなパラメータを指定します。

BLEND:単純に重ねて表示するもの(デフォルト)
ADD:加算合成
などなど。

図解でわかりやすく説明したページを見てみましょう→こちら
詳細はofficial processing of blendMode()

blendModeを使って、少し発光したような表現をしてみましょう。

  1. background()をsetup()に記載し、代わりにdraw()に記述しないようにします。

background(0);

draw()の最初に下記を追記します。

blendMode(BLEND);
fill(0,0,0,10); //RGBA, A = alpha (透明)
rect(0,0,width,height); //キャンパスの背景色を四角で描画

draw内でボールを描画する前に、下記を追記します。

blendMode(ADD);
fill(31, 127, 255, 127);

透明度の指定の方法 stroke()やfill()など、これまでRGBの三つの値を()内に指定していましたが、4つの値を指定することもでき、その場合、4つ目が透明度になります。
2つ指定した場合は、1つ目の値がモノクロの輝度(またはintによる色情報)を表し、2つ目が透明度になります。
fill()などの関数の公式ページのSyntax】を見てみてください。

Alt text

blendMode()を使ったボールの表現。

↓参考にしたサイト。継承など、クラスについてより詳しい解説などもあります。
https://yoppa.org/teu_media18/8936.html


Ex E. classにタイマーまたは寿命を追加し、それによる変化を表現してください。

Ballクラスをベースにしても、そうでなくでもOKです。
生き物(動植物)の生と死や成長、などがわかりやすいと思いますが、アイデア次第で何でもOK。

死ぬ前にどんどん透明にしてフェードで消えるなどもよいですし、光ったり消えたりするような発光を表現するのもよいです。
インタラクションでパーティクルのようなエフェクトがでるなどもよいですね。

ボールじゃなくてもいいし、描画などと組み合わせてもOK。

寿命を使ったParticle and particle system class:
これで皆さんはクラスについてわかるようになったので、Processingの公式ウェブにあるparticle
classが理解できるようになりました。ぜひ見てみましょう。「よ、読める…!」となってくれればうれしいです。 (ArrayListも使ってますね)https://processing.org/examples/simpleparticle


そういえば大阪万博のロゴマーク、「いのちの輝きくん」がTwitterで色々とアレンジされていました。
今回の課題の参考に下記のURLでぜひ遊んでみてください!


更に発展したMultiple Particle Systems: 更にparticle classを複数扱えるMultiple Particle Systemsクラスの例
このように、クラスを更に用いてクラスを定義することもできます。(ArrayListも使ってますね)
https://processing.org/examples/multipleparticlesystems.html