#序章
*本記事は,沼津高専アドベントカレンダー2018に投稿するために書かれたものです。
*Processingの基本ができる人を対象とし,簡単な部分の説明は省いています。
*画面サイズの関係上,PCでの閲覧をお勧めします。
どうも 最近Processingにハマっています。
S5の あきよしです。
本日は 12/14 ・・・そうです
ク リ ス マ ス(の前夜祭の方)
まで残り10日となりました。
ということで,そろそろクリスマスカード用意したいと思います!
今回はProcessingを使った説明ですが,初挑戦の p5.jsを使って作ったものもお見せします!
新しいことに挑戦するのは良きかな( ˘ω˘ )
Processingとは?? (簡単にいうと,電子アートとかデザインのためのJavaベースのプログラミング言語)
↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
Processing(公式) https://processing.org/
Processing(Wiki) https://ja.wikipedia.org/wiki/Processing
実は,みなさんが高専の授業で使ったArduinoの開発環境はProcessingベースなので
Processingの開発環境と似ています。
ほら,とても似てますよね!
#第一章 クリスマスツリーは "普通の木" です。
まず,クリスマスといったらアレですよね?
11月下旬から急速に増殖し,木の生えていないはずの屋内にまで突然と姿を現し
リア充への支援とともに非リアへダメージを与える設置型の兵器。
そうですね,クリスマスツリーです。
(実に恐ろしい兵器です...)
でも,これがなきゃクリスマスにならないので仕方なく準備しましょう。
誰がどう見てもツリーですね!
いい感じに発光してるし完璧なクリスマスツリーですね!(圧力)
(実は,クリスマスツリーって本体は木だから有名な再帰関数の木でよくね??って考えまして
頑張ってみたんですけど,この画像の木でも3^12 = 531,441回 関数呼び出して計算してるので
描画まで行うと非常に処理が重くなってしまうため,とりあえずスカスカな木のまま綺麗に見えるようにしてみました・・・)
というわけで,この普通の木をクリスマスツリーということにしました( ˘ω˘ )
・
・
・
おいおい,コード見せろよ!
わかります,その気持ち。
自分もProcessing勉強中に他の人の作品をみつけて
えぇ...どうなってんのこれ? わからん,悔しい! って思いますもん。
本当なら,自分で考えた方が勉強になりますが
今回の再帰の木は有名なのでそのままソース置いておきますね。
###クリスマスツリーのソースコード
ツリーのソースコード ←クリックするとソースコードが開きます
final int num = 12; //再帰の回数
final float angle = 120; //枝の角度
final float amp = 0.5; //次の枝の長さが前の枝の半分
float startx, starty; //初期位置(根っこ)
float start_len = 200; //最初の枝の長さ
void setup() {
size(800, 800); //画面のサイズ
frameRate(60); //60fps
background(0); //背景を黒にする
colorMode(HSB, 360, 100, 100, 100); //RGBではなくHSBの使用
blendMode(ADD); //色が重なるほど発行するモードへ変更
stroke(210, 60, 90, 100); //線の色を指定
translate(width/2, height);//座標の原点を画面の中央へ移動
startx=0; //初期(木の根っこ)の位置を代入
starty=0; //初期(木の根っこ)の位置を代入
tree(startx, starty, start_len, 0, 0); //木の描画(再帰関数)
}
void tree(float x, float y, float len, float stand, float step) {
float next_x, next_y; //次の枝の先端の点の座標
next_x = x + len * sin(radians(stand)); //次の枝の先端座標を計算
next_y = y - len * cos(radians(stand)); //次の枝の先端座標を計算
strokeWeight(num+1-step); //線の太さを設定(先端に行くほど細くなる)
if (step<num-1) {
stroke(0, 60, 50, 80); //枝の色を設定
} else {
stroke(120, 100, 100, 100); //各再帰の一番深い時の枝を葉として,その色を設定
}
line(x, y, next_x, next_y); //枝を描画する
if (step!=num) { //指定された再帰回数に達していなければ次の関数を呼び出す
tree(next_x, next_y, len * amp, stand - angle, step+1); //現在の枝より-120度回転した次の枝を計算する
tree(next_x, next_y, len * amp * 1.5, stand, step+1); //現在の枝と同じ向きの次の枝を計算する
tree(next_x, next_y, len * amp, stand + angle, step+1); //現在の枝より120度回転した次の枝を計算する
}
}
このプログラムでやっていることを簡単に説明すると,
まず1本目の枝を描きます。
その枝の先端から,次の枝を3本生成します。
さらに生成された3本の枝から,また,それぞれに3本の枝を生成します。
これを指定された回数繰り返します。
下の画像を見るとわかりやすいでしょう。
これは,上記の処理を3回行った時のツリーの様子です。
スタートは画像の中央下部の茶色の枝です。
その枝が3本に分かれ,3本のそれぞれの枝がさらに緑の3本の枝に分かれているのがわかると思います。
まあ,木の説明は簡単だしこのぐらいで
次は雪欲しいですよね!
#第二章 雪の結晶は "普通の木"×6 です。
クリスマスといったらアレですよね!
って言おうと思ったけど
雪の結晶ってクリスマスと関係ほぼないよね?って
この記事書きながら気づきました...
(だがしかし,ソースコードはもう書いてしまった。)
ただ冬だからクリスマスと同じ時期だから
なんか綺麗だし,きっとクリスマス
クリスマスの関係者に違いない!!
察しの良い方ならお気づきだろうが
この雪の結晶,ツリー使ってます。
というか,ツリーを回転させたものを6つ用意して色を変更しただけです。
雪の結晶ってクリスマスツリーからできてるんですね。
立派なクリスマス関係者でしたね!
ということで
こちらのソースコードも置いておきますね。
###雪の結晶のソースコード
雪の結晶のソースコード
final int num=4; //再帰の回数
final int corner=6; //六角形の結晶という指定
final float angle=360/corner;//木を回転させる角度
final float amp=0.4;//木を描く時の次の枝の長さを設定
float startx, starty;//初期位置
float start_len=100;//初期の枝の長さ
void setup() {
size(800, 800);//画面サイズ
frameRate(60);//60fps
background(0);//背景を黒に
colorMode(HSB, 360, 100, 100, 100);//色はRGBではなくHSBを使用
blendMode(ADD);//色が重なるほど発光するモード
stroke(210, 60, 90, 100);//結晶の色
startx=0;//初期位置
starty=0;//初期位置
}
void draw() {
background(0); //画面のリセット
translate(width/2,height/2);//原点を画面の中心へ
//rotate(radians(frameCount)); //雪の結晶が回転する
for(int i=0;i<corner;i++){ //6本の木の描画を行う。
tree(startx, starty, start_len, 0, 0);//木を描画する。
rotate(2*PI/corner); //回転させる。
}
}
void tree(float x, float y, float len, float stand, float step) {
float next_x, next_y;
next_x=x + len * sin(radians(stand));
next_y=y - len * cos(radians(stand));
strokeWeight(num+1-step);
line(x, y, next_x, next_y);
if (step!=num) {
tree(next_x, next_y, len * amp, stand - angle, step+1);
tree(next_x, next_y, len * amp * 2, stand, step+1);
tree(next_x, next_y, len * amp, stand + angle, step+1);
}
}
さて,クリスマスツリーと雪の結晶は準備できましたが
やっぱりクリスマスって雪降っていた方が雰囲気ありますよね
ということで,次は雪降らせてみましょう。
#第三章 降る雪が全部 ellipse なら。
ガッキー「降る雪が全部 メル(ぴー)キッス ならいいのにね」
2018Ver https://www.youtube.com/watch?v=Zq9zf41dh0o
私もそう思いますが
そうなったらメル(ぴー)キッスを求めて静岡からの人口流出が進みそうですね。
残念ですが,Processingにそんなものはないので
代わりに,Processingで円を描画させるときに使用する「ellipse」を降らせましょう。
ということで
↓↓↓こちら↓↓↓
https://www.openprocessing.org/sketch/642846
実際に作ったやつです。
URLを開くと雪が降ります。キーボードの左右の矢印で風が吹きます。
一応,3Dに見えるように奥の雪は小さくゆっくりと動くように
手前の雪は大きめに表現してみました。
これも,ソースコード置いておきますね。
###雪のソースコード
雪のソースコード
final float max_r=10; //雪の最大半径
final float min_r=1; //雪の最小半径
final float max_z=20; //奥行きの最大
final float min_z=0; //奥行きの最小
final float max_ys=3; //落下速度の最大
final float min_ys=1; //落下速度の最小
final int num=500; //雪の数
float wind=0; //風の強さ
boolean flag=false; //2重キー入力を防ぐためのフラグ
Snow[] snows=new Snow[num]; //雪のオブジェクト配列
void setup() {
fullScreen(); //全画面で実行
frameRate(60); //60fps
noCursor(); //カーソルを消す
background(0); //背景は黒
noSmooth(); //円を綺麗に描く機能をOFFに(軽量化のため)
noStroke(); //輪郭線を使用しない
fill(255,100); //雪は白で塗る
for (int i=0; i<num; i++) {
snows[i]=new Snow(i); //雪の生成
}
}
void draw() {
background(0);//画面のリセット
for (int i=0; i<num; i++) {
snows[i].fall(wind); //雪の座標計算
snows[i].show(); //雪を描画
}
}
class Snow{
float x;
float y;
float z;
float r; //雪の半径
float yspeed; //雪の落下スピード
int my_num; //何番目に生成された雪なのか
Snow(int i){
x=random(-width/2-wind*100,width+width/2-wind*100); //初期座標をランダムで
y=random(-height,0); //初期座標をランダムで
z=random(min_z,max_z); //初期座標をランダムで
r=map(z,min_z,max_z,min_r,max_r); //半径は z の大きさで決める
yspeed=map(z,min_z,max_z,min_ys,max_ys); //落下スピードは z の大きさで決める
my_num = i; //自分の生成番号を代入
}
void fall(float wind){
y=y+yspeed; //次の座標は現座標に落下スピードを加えたもの
if(y>0){
x+=wind*map(z,min_z,max_z,0.3,1); //風が吹いていれば次の座標がX軸方向へ動く
}
if(frameCount%20==0){
x+=random(-1.5,1.5); //雪が若干揺れる
}
if(y>height){
snows[my_num]=new Snow(my_num); //雪が地面に着いたら,生成しなおす
}
}
void show(){
ellipse(x,y,r,r); //円を描画する
}
}
void keyPressed(){ //キーが押されると自動で呼び出される
if(keyCode==RIGHT && flag==false && wind<10){ //右矢印が押されたとき
wind++; //風の強さを右に+1する
flag=true;
}else if(keyCode==LEFT && flag==false && wind>-10){ //左矢印が押されたとき
wind--; //風の強さを左に+1する
flag=true;
}
}
void keyReleased(){
flag=false;
}
仕組みは簡単です。
雪を500個生成して,1フレームごとに500個の雪の次の座標を計算していき
画面の一番下まで降ったら,その雪を生成しなおして画面の上部から降らせる。
これを繰り返すだけです!
さてさて,ここまできたら
クリスマスツリーと雪の結晶,雪をまとめてカード風にしてみましょう。
#最終章 大切なのはメリークリスマス。
おっと,クリスマスカードを作りましょうと言ったのに
ここまででクリスマス感あるものはクリスマスツリーだけだということに
気づいてしまいました。
このまま,クリスマスカードっぽくしてもクリスマス感出ない
というかサンタいないんだけど?
そう思っていると思います。(僕もそう思う)
でも,大切なのは Merry Christmas って書いてあることだと思うの
それがあれば,なんだってクリスマスに違いない。
ということで,最終的にはこんな感じになりました。
それっぽく,メリークリスマスって書いたらクリスマスっぽくなりましたね!
↓↓実際に動くプログラムのURL↓↓
https://www.openprocessing.org/sketch/643638
スマホだと画面サイズ足りないので,PCで開いてください。
Enterを数回押すとメッセージが出てきたりします。
左右矢印キーで風が吹いて雪が動きます。
ソースコードはこちらです。
###クリスマスカードのソースコード
クリスマスカード風のソースコード
final float max_r = 10;
final float min_r = 1;
final float max_z = 20;
final float min_z = 0;
final float max_ys = 3;
final float min_ys = 1;
PImage tree, crystal; //画像を扱うための変数
int num = 500;
float wind = 0;
boolean flag = false;
//animation
float x_L; //左側2つの雪の結晶のx座標
float tree_x, tree_y; //ツリーの座標
int enter_count = 0; //Enterキーの押された回数
float fade = 0; //文字がだんだん浮かび上がる効果で使用する
final float amp = 1.5; //雪の結晶のサイズを調整するための定数
Snow[] snows = new Snow[num];
void setup() {
fullScreen(P2D); //全画面モード,P2Dの使用により処理が軽くなる
frameRate(60);
noCursor();
background(0);
noSmooth();
noStroke();
tree = loadImage("tree.jpeg"); //ツリーの画像を読み込み
crystal = loadImage("snow.jpeg"); //雪の結晶の画像を読み込み
textSize(50);
textAlign(CENTER);
for (int i=0; i<num; i++) {
snows[i] = new Snow(i);
}
tree_x = width/2-340; //ツリーの初期位置 340は画像の幅
tree_y = height-787; //ツリーの初期位置 787は画像の長さ
x_L = 100; //左2つの雪の結晶の初期位置
}
void draw() {
background(0);
image(tree, tree_x, tree_y); //ツリーを表示
pushMatrix();//現在の座標軸を保存 //左上の雪の結晶
translate(x_L, 100); //原点を移動
scale(amp*sin(radians(frameCount*0.5))); //座標軸を拡大する
rotate(radians(frameCount*0.5)); //座標軸を回転させる
image(crystal, -100, -100, 200, 200); //雪の結晶を表示させる
popMatrix(); //保存されている座標軸へ戻す
pushMatrix(); //右上の雪の結晶
translate(width-100, 100);
scale(amp*sin(radians(frameCount*0.5)-PI/2));
rotate(radians(frameCount*0.5));
image(crystal, -100, -100, 200, 200);
popMatrix();
pushMatrix(); //左下の雪の結晶
translate(x_L, height-100);
scale(amp*sin(radians(frameCount*0.5)-PI/2*3));
rotate(radians(frameCount*0.5));
image(crystal, -100, -100, 200, 200);
popMatrix();
pushMatrix(); //右下の雪の結晶
translate(width-100, height-100);
scale(amp*sin(radians(frameCount*0.5)-PI));
rotate(radians(frameCount/2));
image(crystal, -100, -100, 200, 200);
popMatrix();
fill(230, 200); //雪の色
for (int i=0; i<num; i++) {
snows[i].fall(wind);
snows[i].show();
}
Message(); //メッセージの表示を行う関数
}
void Message() {
if (enter_count==1) { //Enterキーが1度押された場合
x_L = width/2; //左2つの雪の結晶の座標を変更
tree_x = width/10-340; //ツリーの座標を変更
} else if (enter_count==2) { //Enterキーが2度押された場合
fill(255, 0, 0, fade); //文字の色
text("Merry Christmas!!", (width/2+width-100)/2, height*4/10); //メセージの表示
fade++; //文字の濃さを少しづつ上げる
} else if (enter_count==3) {
fill(255, 0, 0);
text("Merry Christmas!!", (width/2+width-100)/2, height*4/10);
fill(255, 0, 0, fade);
text("Enjoy Processing", (width/2+width-100)/2, height*6/10);
fade++;
} else if (enter_count==4) {
fill(255, 0, 0);
text("Merry Christmas!!", (width/2+width-100)/2, height*4/10);
fill(255, 0, 0);
text("Enjoy Processing", (width/2+width-100)/2, height*6/10);
fill(255, 0, 0,fade);
textSize(30);
text("by Akiyoshi", (width/2+width-100)/2, height*8/10);
textSize(50);
fade++;
}else if(enter_count==5){
fill(255, 0, 0);
text("Merry Christmas!!", (width/2+width-100)/2, height*4/10);
fill(255, 0, 0);
text("Enjoy Processing", (width/2+width-100)/2, height*6/10);
fill(255, 0, 0);
textSize(30);
text("by Akiyoshi", (width/2+width-100)/2, height*8/10);
textSize(50);
}else if(enter_count==6){
enter_count=0; //Enterキーが6回押されたらカウントを0に戻す
x_L = 100; //左2つの雪の結晶を初期位置に戻す
tree_x = width/2-340; //ツリーを初期位置に戻す
}
}
class Snow{
float x;
float y;
float z;
float r;
float yspeed;
int my_num;
Snow(int i){
x=random(-width/2-wind*100,width+width/2-wind*100);
y=random(-height,0);
z=random(min_z,max_z);
r=map(z,min_z,max_z,min_r,max_r);
yspeed=map(z,min_z,max_z,min_ys,max_ys);
my_num = i;
}
void fall(float wind){
y=y+yspeed;
if(y>0){
x+=wind*map(z,min_z,max_z,0.3,1);
}
if(frameCount%20==0){
x+=random(-1.5,1.5);
}
if(y>height){
snows[my_num] = new Snow(my_num);
}
}
void show(){
ellipse(x,y,r,r);
}
}
void keyPressed() {
if (keyCode==RIGHT && flag==false && wind<10) {
wind++;
flag=true;
} else if (keyCode==LEFT && flag==false && wind>-10) {
wind--;
flag=true;
}
if (keyCode==ENTER) { //Enterが押されたときに呼び出される
enter_count++; //Enterの押された回数を1増やす
fade=0; //文字の濃さを0に戻す
}
}
void keyReleased() {
flag=false;
}
こんな感じです。
仕組みは簡単,クリスマスツリーは描画するだけ。雪の結晶は,フレームごとに回転させながらscale()を使用して大きさを変化させるだけ。降る雪は,雪が降るプログラムをそのまま使用。
だんだんと文字が濃くなっていき,出現したように見えるのはフレームごとに文字を半透明から透明じゃなくしてるだけです。
ソースコード読んで,あれ?なんか画像使ってない??って気づいた方がいると思います。
そうなんです,ここまででクリスマスツリーも雪の結晶も描画するためのプログラムを書いたのに,このソースコードを見るとその2つは画像になっていて使用されているのは主に雪が降るプログラムだけです。
これには理由があります。
簡単にまとめると,クリスマスツリーも雪の結晶も計算量すごいから
全部一緒に動かすと処理が重くなって,降る雪がカックカクになっちゃう!
そもそも,ツリーと雪の結晶は特に動きがないから毎フレーム描画するために計算し直すのは無駄すぎない??
ということで,今回は自分で作成したツリーと雪の結晶を画像化しました。
どうしても嫌だなぁって人は,かなりいいパソコンを用意すればできないこともないと思います。
でも,Processing初心者の方には画像を扱う練習になるのでいいと思います。
#終わり
こんな感じで,クリスマスカード風のものができました。
これをさらに画像化して印刷すればカードになるんじゃないでしょうか?
p5.jsに移植して Open Processing にアップロードして,そのURLを友達に渡せば動くクリスマスカードとして渡せると思います!
普段は,適当にソースコード書いて偶然生まれた面白いものを作ったりしているので
クリスマスツリーとか雪とか,何か作ろうと思いながら作るのは大変でした( ˘ω˘ )
ちなみに今回作ったソースコードでいつもみたいに遊ぶと
クリスマスツリーがこんな感じになったり。
やっぱり,適当にソースコードいじって面白そうなやつ作るの楽しい!!
皆さんもProcessingやってみてください!