ー ひものような ー  

今回は前回とはちょっと趣向を変えてひものような動きを作ってみましょう.
これは私が以前作ったNTT ICCのトップページの動きの基本になるものです.

ICCでは,さまざまな動きを加味してあり,複雑になっていますが,今回はその中での基本的なひものように動くところに注目して作っていきます.

動きはこのようになります.

そしてソースコードはこうなります.

 

point[] p;

[setup]{
p = new point[6];
for(int i = 0; i < p.length; ++i){
p[i] = new point();
}
}

[always]{
p[0].pushTo(mouse, p[0].distanceTo(mouse)*0.2);
for(int i = 1; i < p.length; ++i){
p[i].pushTo(p[i-1], p[i].distanceTo(p[i-1])*0.2);
}
}

[paint]{
clearAll();
for(int i = 1; i < p.length-1; ++i){
double ax = (p[i-1].x+p[i].x)/2;
double ay = (p[i-1].y+p[i].y)/2;
double bx = (p[i].x+p[i+1].x)/2;
double by = (p[i].y+p[i+1].y)/2;
if(i == 1){
ax = p[0].x; ay = p[0].y;
}
if(i == p.length-2){
bx = p[p.length-1].x; by = p[p.length-1].y;
}
drawBezierCurve(ax, ay, p[i].x, p[i].y, p[i].x, p[i].y, bx, by);
}
fillOval(p[0], 4, 4);
fillOval(p[p.length-1], 4, 4);
}



まず
point p[]; でひもになる点群 p を準備し,それをsetup内の
p = new point[6];
for(int i = 0; i < p.length; ++i){
p[i] = new point();
}
によって初期化しています.pは配列で宣言していて上から順番に位置を決めています.

次に動きの記述です.
動きは,先頭の点がマウスに引っ張られ,以降の点は自分の一つ前の点に引っ張られる,というようにしました.
このようにするとひもの先端を持って振り回しているように見えます.alwaysの部分の

p[0].pushTo(mouse, p[0].distanceTo(mouse)*0.2);

で,p[0],すなわち先頭がmouseに,p[0]からmouseまでの距離の0.2の力で引っ張られるようにしています.
このmouseまでの距離の0.2の力というのはつまり,マウスから遠ければ力強く,近ければ弱く引っ張ってもらうための工夫です.
距離の値をそのまま力の値とすると強すぎるために,0.2倍しました.
そして

for(int i = 1; i < p.length; ++i){
p[i].pushTo(p[i-1], p[i].distanceTo(p[i-1])*.2);
}

で,iを1からpの長さ分まで増加しながら,p[i]をp[i-1](つまり一つ前)に,p[i]からp[i-1]までの距離の0.2の力で引っ張られるようにしています.
引っ張る仕組みは先頭の場合と同様です.

このようにして点群がマウスに振り回される仕組みは出来ました.

最後にこれらの点群を描画するのですが,単純にdrawLineで各点群を直線で結ぶと、角ばった線になってしまって,ひもを振り回しているように見えません.
そこで,drawBezierCurveを使って自然な曲線を描画するように工夫します.drawBezierCurveは曲線を描くのに4点を指定しなくてはいけないので,単純に点群を最初から順番に4個ずつ引き渡してもいいのですが,それだと曲線が接続する部分が図1のように折れ曲がってしまいます.

図1図1

そこで,図2のような工夫をしました.

図2図2

つまり中心になる点p[i]を考え,その一つ前の点p[i-1]との中点と,一つ後の点p[i+1]との中点を曲線の端点にします.端点ではない真ん中の2点は両方ともp[i]にします.
このようにすると曲線どうしの接続が一直線になるため折れ曲がることはありません.それをコード化したものがpaintの部分です.

clearAll();

で,まず一度画面全体を消去し,

for(int i = 1; i < p.length-1; ++i){

によってひもの各点を巡るようにして,

double ax = (p[i-1].x+p[i].x)/2;
double ay = (p[i-1].y+p[i].y)/2;
double bx = (p[i].x+p[i+1].x)/2;
double by = (p[i].y+p[i+1].y)/2;

で,端点になる点(ax, ay)と(bx, by)を決めています.ただし,

if(i == 1){
ax = p[0].x; ay = p[0].y;
}
if(i == p.length-2){
bx = p[p.length-1].x; by = p[p.length-1].y;
}

によって,ひもの両端点は中点をとらずに本当の端点(にしても折れ曲がるということはおこらないので)しています.そして,

drawBezierCurve(ax, ay, p[i].x, p[i].y, p[i].x, p[i].y, bx, by);

で,各曲線を描画しています.そして

fillOval(p[0], 4, 4);
fillOval(p[p.length-1], 4, 4);

で,ひもの端点に結び目的な黒い点を描いています.

このようにしてひもを振り回すような動きを作りました.