[Go to “teaching” in Morimoto Lab]
Here we study class.
あなたは自分でPVectorのようなデータの型を定義することができます。
そのデータの型には複数の変数や関数を持たせることができます。
これをクラス
と言います。
(前回使ったPVectorはProcessingに用意されたクラスです)
クラス
は “クッキーの型”に例えられます。自作のクッキーの型を用意できたら、それでクッキーそのものを作る必要があります。このクッキーという実体のことをインスタンス
と呼びます。
インスタンス
(=クッキー)を作るために、new
が必要です。
メソッド
は クラス内で定義された関数のことです。
また、クラス内の変数やメソッドのことを、そのクラスのメンバ変数、メンバメソッドなどと呼びます。
Note: オブジェクト指向プログラミング (Object-Oriented Programming: OOP)
オブジェクトとは、この場合、インスタンスのことを指します。
OOPはクラスの便利さがわかる考え方の一つなのですが、当講義ではあまりこの部分は深くつっこみません。(継承などは学びません)OOPは変数やメソッドなどを一つのまとまりにし(クラスのインスタンス等のことですね)、そのまとまりを組み合わせたり、使いまわしたりすることで効率よくわかりやすいプログラミングを行うという考え方です。
他の考え方に手続き型プログラミングなどがあります。
参考:手続き型言語とは?
参考:Processingの公式ページ オブジェクトのチュートリアル 。
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の間に線を描画してみましょう↓
※ball変数はこの課題ではあえてローカル変数にしています。
次は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までの説明です。
クラスの使い方
1. メンバ変数(クラスの変数)を定義しています
2. クラスのインスタンスをnewで生成しています
3. Ballクラスのメンバ変数であるbxを呼び出しています
4. Ballクラスのメンバメソッドであるdisplay()を呼び出しています。
2個以上ボールを作った場合に、display()だけでellipse(…)の代わりになるのは便利ではないですか?
クラスの記述
5. クラスの中身の記述です。
6. メンバ変数
はクラスに持たせたい変数です。ボールクラスの場合、ボールの数だけ作りたい変数などを定義しておくとよいでしょう
7. コンストラクタ
はクラス名と同じ名前の特殊なメソッドで、一つの変数につき、newするときに一度だけ実行されます。
(クラスを記述するとき、基本的にコンストラクタを定義しないといけません。Processingの場合はなくてもエラーは起きないようですが)
8. メンバメソッド
はクラスに機能として持たせたいメソッドです。描画や更新(動きのupdateなど)メソッドなどを持たせたりします。(メソッドを定義しないクラスもありです。変数だけを持たせて独自の型のようにすることもあります。)
☆★初期値の代入はここで2通り書いていますが、どちらも実行する場合は★の方が先に処理されます。
どちらか片方だけを行う場合、結果はこの場合同じです。
以下のように、クラスのインスタンスを生成するとき、newのあとに呼び出されているBall()がコンストラクタです。
Ball ball = new Ball();//定義 ←newのあとのBall()がコンストラクタを呼び出している部分です
コンストラクタは同じ名前でも引数を変えることで、複数定義できます
参考:関数のオーバーロード(多重定義、ポリモーフィズム)
以下は引数のあるコンストラクタの例になります。
Ball(float bx0, float by0){
bx = bx0;
...
}
この課題では、コンストラクタに引数を持たせて、位置を指定できるようにしましょう。
※PVectorもnew PVector(100,100)などとして、x,yの値を指定できましたね!それです。
余裕がある人はボールの速度やサイズ、また、新たにメンバ変数を追加して色なども指定してみましょう。
ボールの動きはmove()に記述します。
動きの方はまずは簡単なものでも構いません。
ヒント:
ballはdraw内でnewすると、毎フレーム、ボールの位置が初期化されてしまいます。
毎フレーム、ボールの位置が初期化されず、値を保持するために、ボールはグローバル変数にする必要があります。
プログラムの構造としては第4回の1.3のとき(このときはPVectorで定義したボールの位置がグローバル変数になっている)などと同じようになりますので、参考にしてください。
Note: クラス内ではグローバル変数やグローバル関数を使わないようにしてください
プログラム的に可能ですが、学習の最初の段階では多くの人にとってミスリーディングになると思いますので、このようにお願いします。場合によってはグローバル変数をクラス内で使う場合はありますし、そのクラスで作ったグローバルなインスタンスをクラス内で呼び出す場合もあります(Drawing&Pattern3のひまわりの参考プログラムはそのようなものになっています)。しかし、今回はそのようなことはしないようにしましょう。
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();
}
blendMode(**)
は色が重なった時にどう見えるかを指定することができ、簡単に様々な表現ができます。
**
には以下のようなパラメータを指定します。
BLEND:単純に重ねて表示するもの(デフォルト)
ADD:加算合成
などなど。
図解でわかりやすく説明したページを見てみましょう→こちら
詳細はofficial processing of blendMode()。
blendModeを使って、少し発光したような表現をしてみましょう。
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】を見てみてください。
blendMode()を使ったボールの表現。
↓参考にしたサイト。継承など、クラスについてより詳しい解説などもあります。
https://yoppa.org/teu_media18/8936.html
Ballクラスをベースにしても、そうでなくでもOKです。
生き物(動植物)の生と死や成長、などがわかりやすいと思いますが、アイデア次第で何でもOK。
死ぬ前にどんどん透明にしてフェードで消えるなどもよいですし、光ったり消えたりするような発光を表現するのもよいです。
インタラクションでパーティクルのようなエフェクトがでるなどもよいですね。
ボールじゃなくてもいいし、描画などと組み合わせてもOK。
寿命を使ったParticle and particle system class:
これで皆さんはクラスについてわかるようになったので、Processingの公式ウェブにあるparticle
classが理解できるようになりました。ぜひ見てみましょう。「よ、読める…!」となってくれればうれしいです。 (ArrayListも使ってますね)https://processing.org/examples/simpleparticle
そういえば大阪万博のロゴマーク、「いのちの輝きくん」がTwitterで色々とアレンジされていました。
今回の課題の参考に下記のURLでぜひ遊んでみてください!
いのちの輝きくんと一緒に遊べるようになった。
— シャポコ🌵 (@shapoco) August 26, 2020
👉️ https://t.co/889MuzSwI4
. pic.twitter.com/2mlINAHMYl
更に発展したMultiple Particle Systems: 更にparticle classを複数扱えるMultiple Particle Systemsクラスの例
このように、クラスを更に用いてクラスを定義することもできます。(ArrayListも使ってますね)
https://processing.org/examples/multipleparticlesystems.html