[Go to “teaching” in Morimoto Lab]

Game by ball 7

今回は、「クラスの中でクラスを使う」ことを学びます。
これまで作ってきたBallクラスを、今度は「部品」として使います。
小さなクラスを組み合わせて大きな構造を作ることは、ゲームやCGで非常に重要です。

内容的には、
①ボールに力を加える・投げる
②見えないボールで疑似アトラクタを作る
となっています。

慣れるまではちょっと難易度高い内容になっています。


1. ボールに力を加える

classの練習、Attractorのための助走としてボールに力(=速度変化)を加えるプログラムを作る。

今回、力を加えるということは、現在のボールの速さに別のベクトルを加える、速度変化として扱います。
まず、ボールの移動プログラムを用意します(classを使いましょう)。
今回は初速度は0、重力はなし、壁もなし、空気抵抗は考慮しましょう。
次に、マウスを押したら適当なベクトルが速度に足されるコードを作りましょう。

以下はマウスを押したらボールに力を加えるプログラムのベースです。
updateメソッド内でボールの速度と位置を更新する部分は書いてみてください(Game by ball 1,2でやった内容です)。

Ball ball;
void setup(){
size(500,500);
ball = new Ball();
}
void draw(){
background(255);
ball.update();
if(mousePressed) {
ball.addForce();
}
}
class Ball{
PVector pos;
float diam = 10;
PVector vel;
float air_drag = 0.95f;//空気抵抗は考慮する
Ball()
{
pos = new PVector(width/2, height/2);
vel = new PVector(0, 0);//速度は最初0にしておく
}
void update()
{
//update velocity:ここは第1,2回にしたので省略
//update position:ここは第1,2回にしたので省略
//bound:壁とのバウンドは今回なくてよいです
display();
}
void display(){
stroke(0);
noFill();
ellipse(pos.x, pos.y, diam, diam);
}
void addForce(){
PVector vec = new PVector(3,-3);
vel.add(vec);
}
}

Ex A:上記のクラスを完成させて、マウスを押したらボールに力が加わるようにする。また、Ballクラスのコンストラクタに引数を設定し、初期位置を設定できるようにする。

コンストラクタはクラスと同じ名前で型の記載がない特殊なクラスでした。
newの時に呼び出しているのがコンストラクタです。
コンストラクタも関数なので、普通の関数のように、引数を変えて定義できます
(引数の違う複数のコンストラクタを定義することも可能です)。

//update velocity(空気抵抗)(multメソッドなど使ってもよい)
vel.x *=air_drag;
vel.y *=air_drag;

初歩的なミス:
Ballクラスの中では、メンバ変数であるposやvelをpos.xなどとして使います。
Ballクラスの中で、ball.pos.xなどとしないようにしましょう。

Alt text
↑結果例


Ex B: マウスとボールが近づいたときに距離に応じて、力が加わるようにする

<実装のステップ(例)>

  1. addForce内などで ※

  2. マウスとボールの距離を計算

  3. 一定の距離内にボールがあるかどうか判定(この「一定の距離=影響範囲」は適当に決めましょう)

  4. もし影響距離内にボールがあったら、マウスからボールへの単位ベクトルを計算し、適当な倍数にして調整して、ボールの速度にベクトルを足す

  5. 4で、一旦「適当な倍数」としましたが、距離に応じて倍数を変えるようにしましょう。近ければ近いほど強い力が加わるようにしましょう。

※上記、マウスとボールの距離を計算するメソッドを作り、draw()のところで距離判定をするのもよいかもしれません。適宜、やりやすいようにアレンジしてみてください。

ヒント1:近いほど強くなるような力の計算方法
例えば、以下のように計算した値を調整した上で力の大きさとするとよさそうです。
①(1/距離)
②(影響範囲-マウスとボールの距離)※1
③(ガウス関数などの釣鐘型の関数に距離を変数として計算)※2
経験的には②の挙動がいいみたいですが、他にもよい方法があるかもしれません。

※1 値が0以下になるときはifで場合分けの処理をすること。
※2 指数関数はexp(), べき乗はpow()で計算できる。

Alt text
↑点を並べてマウスを押して周囲に力を与えた場合

ヒント2
Ex. Bはボール一つでやってみてください。できる人は上記の図のようなボール配置でやってみても〇

ヒント3
最終的にボールは上記のように配置されます。今回は個数が固定されているのでArrayListではなく普通の配列にしましょう。

注意
上の例では、ボールは2次元配列のように見えますが、当講義では1次元配列しか教えていません。
(2次元配列は int[][] a; みたいなものです。1次元だと int[] b; でした)
今回のAttractorの課題は1次元配列でできるように設計しています。
2次元配列はどこかで学ぶとよいとは思いますが、今回に関してはだいぶ意図とは違う解き方になります。
(ちょっとこの絵はミスリーディングかもしれません。。🙇)


余裕のある人へ.ボールをマウスドラッグでつかんで、マウスを離したときに投げるようにする

※この課題は横道にそれるのでとばしてもいいですが、ポケモンGOみたいにボールを投げるコンテンツに興味のある人はやってみてください。


2. Attractorを作ろう

Attractorとは?
Attractorは数値的値の集合が動的に発展をするもの、です。
「あるルールに従って点を動かしたときにあらわれる特徴的な軌跡や状態」のことです。

Attractorの画像検索結果

Alt text

数学的Attractor:
Attractorについてのサイトをいくつか紹介。

今回実装するのはAttractorのような雰囲気のビジュアルのものである。
ここでの実装では、アトラクターは見えないボールとボールの間を線でつなぎ、
そのボールをマウスにひきつけたり、逆に離したりすることで、
不思議な模様を描きます。
”面白い動き”や”美しい模様”を作ることを重視します。

参考にしたのは以下のGenerative Designという本の例です。
https://download.bnn.co.jp/support/generativedesign/code/M_4_3_01_TOOL.html

enter image description here

以下のようにコードをダウンロードして、
デスクトップ上でこのプログラムを実行してみよう。

  1. このページの右のボタン”code”から、”download zip”などでdownloadする

  2. 解凍したフォルダからCode-Package-Processing-3.x/02_M/M_4_3_01_TOOL/を開く

  3. pdeファイルをProcessingで開く。

  4. Generative Designのライブラリをimportする必要があります。
    メニュー>スケッチ>ライブラリをインポート>ライブラリを追加 より”GenerativeDesign”をインストールする

  5. 実行できるはず

クリックして、挙動を見てみましょう。
左上のボタンを押すと、UIが現れ、影響範囲や点の動かし方を変更できます。

しかしコードを見てみると、つらく感じる人もいるだろう。
プロのプログラムを読むのはつらい作業だ(教えてないこともあるし)
むしろ、これまで培ったGame by ballの知識で一から実装してみよう。
そっちの方が簡単だと思う。

※高みを目指す人には人のプログラムは色々見てもらいたい。
とても勉強になる。
今日の課題を終えてからもう一度見てみるとよい。


Ex C: Attractorを実装しなさい(横方向一列のみ)

Ex Bを発展してAttractor(もどき?)を作りましょう。画面にボールを並べて、横線で繋ぎ、クリックしたところから近いほど強い力で移動するようにします。
ボールはx座標は固定し、y方向にしか移動しないようにします。
(この課題のイメージとしては、ExBの最後の図の横一列だけを作り、上下移動のみさせる感じです。)
具体的なやり方は以下になります。

①まず横一列を作る

  1. Ballクラスの配列を作って、横一列にボールを並べる

    • 重力や壁とのバウンドはいらない

    • クラスの配列の定義の仕方はGameByBall5Cを参照。

  2. ボールをマウスから遠ざけるように力を加える

    • 距離に応じて力の大きさを加える

  3. ボールとボールの間に線を描く(横方向だけ)※下記ヒントを参照

  4. ボールはy方向にしか動かないようにする

ヒント:Ballクラスはあくまでも一つのBallのデータを扱う=Ballクラスに配列を入れない!
Ballの配列は一旦グローバル変数として生成しましょう(BallLineクラスを作成していない段階では)。
また、このとき、線を描画するときに、Ballの配列の複数のBallデータが必要なので、Ballクラス内だけで完結することはできません。
あくまでBallクラスは「1個のボールだけを扱う部品」です。一度完成したBallクラス内はその後ほとんどいじりません。
グローバルに定義したBall配列やPVector配列をBallクラス内で扱おうとする人がいますが、これはいけません。
draw関数内で、balls[i].posとballs[i+1].posを使ってlineを描画してみましょう。

Alt text
↑今回のクラスの入れ子関係:
Ballクラスの配列を持ったBallLineクラス、
BallLineクラスの配列をグローバル変数として定義。

Ex D: Attractorを実装しなさい(全体)

②縦に増やす

  1. 横一列できたら、それをクラス化(BallLineクラス)する ※

  2. y方向にBallLineをたくさん並べる

  3. ボールを消す(ellipseを描かない)

  4. 色や点の間隔や動き方を調整する

    • 線にきれいな色(グラデーションなど)を指定する
      (HSB色空間や透明色などを使う)

※ヒント
BallLineクラスは線一本分を扱う部品です。
一本分の線の描画はBallLineクラス内で行います。
また、最終的にBall配列を定義する場所もBallLineクラス内です(=Ballクラスの配列をBallLineクラスのメンバ変数にする)
BallLineクラスの配列はグローバル変数として作り、アニメーションできるようにします

Ballクラスには位置を指定する引数を持つコンストラクタが必要になります。BallLineクラスも同様。

このテキストの最後にBallLineクラスの例を載せていますが、できれば一度見ずに作成してみましょう。

Alt text


余裕のある人へ.VJ風Interactive Visual Systemの制作

VJ(Visual Jockey)は、音楽ライブなどでリアルタイムに映像を生成・操作する表現のことです。
※必ずしもクラブ映像のようなものにする必要はありません。
スクリーンセーバー、メディアアート、MV背景、インタラクティブアートなど、自由に発想して構いません。

今回学んだ疑似Attractorの要素を利用して、オリジナルの
「VJにも使えそうなリアルタイムに変化するパターンやエフェクト表現」
を制作しなさい。

今回の疑似Attractorの要素とは、
・力による変形・移動
・キーボードやマウスによる操作
・時間変化などに対するインタラクション
・クラスを2種以上使う
・配列またはArrayListを使う
などです。

・見ていて気持ちいい
・反応して変化する
・音楽に合いそう
なものを目指しましょう。

Attractor風のビジュアルを活かせそうなものとして、例えば:
・波打つ線
・粒子表現
・軌跡
・ノードネットワーク
・残像
・幾何学模様
・色変化
・パターン生成
などを組み合わせてもよい。

Attractorの例
ちなみにこのページのM.4アトラクターに7つもコードと動画付きの例がある。

Generative Design、他の例(図のみ)

ここでは挙げていませんが、実際のVJの例も参考になるかもしれません(やや今回のAttractorぽくないものが多い印象ですが)。

他にも、TouchDesignerやUnityなどでVJ開発している人もいるみたいですね。
VJ合宿なるものもあるようです。
VJソフトウェアの開発を支援するシステム開発などもあるようです↓
2024年度未踏 ジェネラティブVJソフトウェアの開発を容易にするシステム


※(できれば見ずに作ってほしい・・・)BallLineクラスの一例
以下は、横一列のボールとその間に線を描画するBallLineクラスの実装例です。






















🐤













































🐔


















































































Alt text





































































👁





































BallLine ballLine;
int xnum=10;//横方向のボールの数
void setup() {
size(500, 500);
ballLine = new BallLine(100);
}
void draw() {
background(255);
ballLine.update();
}
class BallLine {
Ball[] balls;
BallLine(int offsetY) {
balls = new Ball[xnum];
for (int i=0; i<balls.length; i++) {
balls[i] = new Ball(i*50, 100+offsetY);
}
}
void update() {
for (int i=0; i<balls.length; i++) {
balls[i].update();
if (mousePressed) {
balls[i].addForce();
}
//draw line
if (i+1<balls.length) {
PVector a =balls[i].pos;
PVector b =balls[i+1].pos;
line(a.x, a.y, b.x, b.y);
}
}
}
}
//Ballクラスの記述は省略

!Ballクラスは初期位置(x,y)を指定できる引数を持つコンストラクタが必要です !このプログラム例では更新と描画のメソッドを分けていませんが、分けてもよい