最終的にやりたい事
適当にシェイプで楕円形をwiggleなんかで動かして、楕円形近いシェイプ同士を線で結ぶ
connected dotsのモーションを作る
こんな感じの物ね!
縛りとしてはエクスプレッションで書く。
今の頭のイメージとしては
・シェイプをwiggleで動かす
・エフェクトのビームを使って線は描画する(オーソドックスなやり方)
・ビームの開始点には基準となるのシェイプを指定
・ビームの終了点にはエクスプレッションで基準のシェイプレイヤーに近いシェイプを取ってくる(何番目に近いかはスライダー制御で指定出来る様にする)
とまあこんな感じでモーションは出来ると思っています。
軽くまとめると
➊コンポジションにはDotという名前の楕円形シェイプレイヤーを量産して線を繋げるのは➋のエクスプレッションで自動化する。
➋ビーム用のレイヤー(平面レイヤー)終了点にエクスプレッションを書いていく
具体的には
Dotのレイヤー分と必要なのと基準シェイプから何本まで線を出すか、つまりn番目まで近いレイヤーをターゲットにするかで➋を複製する必要がある。
まずは距離を取る為にビームの終了点にプログラムを書いていくわけだが
TargetPos = thisComp.layer("Dot").transform.position;
MyPos = effect("It's Me")("レイヤー").transform.position;
c = sub(TargetPos,MyPos);
d = length(c);
( d < effect("Distance")("スライダー") )? MyPos :TargetPos;
とりあえず、これでMyPos(基準となるシェイプ開始点)からTargetPos (目標となるシェイプ終了点)までの距離は取れる
ただDotというレイヤーは量産するので、コンポジション内のDotレイヤーがいくつあるのかと、距離を出してソートする必要がある。
そこで考えたのがこのエクスプレッション
MyPos = thisComp.layer("Beam").effect("It's Me")("レイヤー").transform.position;
n = thisComp.numLayers;
var arry = [];
for (var i = 1 ; i <= n ; i++){
var ln = thisComp.layer(i).name;
if ( ln.match(/Dot/)){
var getPos = thisComp.layer(i).transform.position;
c = sub(getPos,MyPos);
d = length(c);
arry.push([ln,d])
}
}
function compareFunc(a, b) {
return a[0],(a[1] - b[1]);
}
arry.sort(compareFunc)
arry.join("\r");
arryに返ってくるのは二次元配列で[レイヤー名、距離]が返ってくる。
重要な役割を担っているソートの関数
このソートの部分がちょっと迷ったのと今回のプログラムで重要な役割を担ってるので
記事メモ。
内容は二次元配列をソートしてくれる関数
function compareFunc(a, b) {
return a[0],(a[1] - b[1]);
}
コンポジション内にあるレイヤーを全て繰り返しfor文でかけて(/Dot/)でマッチをかけて
距離を計算して配列に、レイヤー名と距離を入れる。
最後にソートして距離の小さい順に並ぶ。
ほぼ完成が見えてきた
今はわかりやすくテキストレイヤーにエクスプレッションを書いているが、これをbeamのレイヤーの終了点に持って行って、もうちょっとゴニョゴニョすれば完成すると思う。
ちなみに現在は二次元空間のみで考えています。
三次元になるとどうなるのやら。。。
完成したらまたこの続きを追記しようと思います。
という事で続き
ロジックはほぼ完成と思いきや
何か違う。。。近くの物ポジションを取っていないっぽい。
とりあえず現状
これをbeamのレイヤーの終了点に書く
prop = thisProperty.propertyGroup(1).propertyIndex;
parentTarget = effect(prop);
idx = parentTarget.name.replace(/[^0-9]/g, '');
MyPos = effect("It's Me")("レイヤー").transform.position;
neares = Math.floor(effect("NearNum "+idx)("スライダー"));
dist = effect("Distance "+idx)("スライダー");
n = thisComp.numLayers;
var arry = [];
for (var i = 1 ; i <= n ; i++){
var ln = thisComp.layer(i).name;
if ( ln.match(/Dot/)){
var getPos = thisComp.layer(i).transform.position;
c = sub(getPos,MyPos);
d = length(c);
if (d >= dist){
arry.push([getPos,d])
}
}
}
function compareFunc(a, b) {
return a[0],(a[1] - b[1]);
}
arry.sort(compareFunc);
try{
( arry[neares][1] < dist )? MyPos :arry[neares][0];
}catch(e){
MyPos
}
ごちゃごちゃしているので動画でまとめるが・・・
こうゆうことね!!
beam開始点
thisComp.layer(effect("It's Me")("レイヤー").name).transform.position
beam終了点
MyPos = thisComp.layer(effect("It's Me")("レイヤー").name).transform.position;
neares = Math.floor(thisComp.layer("Beam").effect("NearNum 1")("スライダー"));
dist = thisComp.layer("Beam").effect("Distance 1")("スライダー");
var result;
n = thisComp.numLayers;
var arry = [];
var ln ;
for (var i = 1 ; i <= n ; i++){
ln = thisComp.layer(i).name;
if ( ln.match(/Dot/)){
var getPos = thisComp.layer(i).transform.position;
c = sub(getPos,MyPos);
d = length(c);
if (d >= dist){
arry.push([getPos,d,ln])
}
}
}
function compareFunc(a, b) {
return a[0],(a[1] - b[1]);
}
arry.sort(compareFunc);
try{
var result = ( arry[neares][1] < dist )? MyPos :arry[neares][0];
// result = ( arry[neares][1] < dist )? MyPos :arry[neares][2];
}catch(e){
result = MyPos;
}
result;
とりあえずどんな物が出来たのかを発表
とりあえず、形になったというか、力技で何とか形にしたって言った方がいいかもしれない。
何故なら3次元で作ってないため、すごーく平面感があって、気に入らないから、3次元っぽく、被写界深度があるかのように無理やりカメラブラーでデプスマップ作って、それっぽく作ってるからだ。
ちなみにトップのサンプル画像に寄せた設定にするとこんな感じ
今日も撮影の合間時間があったから考えてたけど、違うアプローチ方法見つけた。
こっちの方が合理的かもしれない、からもう少し詰めてみて、いい感じであれば発表します。