[概要]
エクスプレッションでシェイプを柔軟にサイズ調整するで作ったエクスプレッションのシステムを、機能追加しスクリプト化しました。
テキストの座布団としても生成できるよう拡張しました。
また、スクリプトでシェイプレイヤーを作る際の注意点をおさらいできると思い、チャレンジしました。最後の方の解説をお楽しみに♪
[使い方]
- (テキストレイヤーを選択する)
- スクリプトを実行する
[オプション]
- シェイプレイヤーのエフェクトにあるスライダー制御でサイズを調節する
【クワッド体】による【アライズ済みテクス体】との【アクセルアクティベーション】について
※ここからはこのページ独自の専門用語が出てきます。Adobe公式の名称ではないため、「世界観」なのだとご理解ください。
テキストレイヤーを選択していない状態で実行すると、【シェイプモード】として縦横100pxのシェイプレイヤーを生成する。
テキストレイヤーを選択している状態で実行すると、【座布団モード】としてテキストレイヤーと同じサイズのシェイプレイヤーを選択レイヤーの1つ下に生成する。レイヤー名も”テキスト名_zab”とリネーム(同名があれば末尾に連番追加)。
さらに、テキストレイヤーを座布団の親に設定してレイヤースケールが変わっても、大きさが変わること無くうまく受け継ぐ。はず。【追従モード】になる。親子設定を途中で付け外ししても大丈夫。なはず。これを【デタッチャブルモード】と呼ぶ。この座布団とテキストレイヤーの親子設定を繋ぐことを、このページでは【アクセルアクティベーション(A:A)】と呼ぶ。なんなら座布団も【オフトゥン】と呼ぶ。
このような世界観を理解した上で、改めて使い方についておさらいする。
まず、単純に【シェイプモード】で【エヴォリューション】する場合は、そのままスクリプトを【ゲラウェイ】するだけで良い。縦横100pxの【クワッド体】が【エピック】する。エフェクトに【アライズ】した[sizeX]のスライダーでクワッド体の【ナガル】を、[sizeY]のスライダーでクワッド体の【タガル】を【エクストリーム】できる。[←], [→], [↑], [↓]のスライダーで、クワッド体の1方向のみを【ビクト】または【セクト】できる。
そして【座布団モード】として、【オフトゥン】を【バーニング】したい場合は、事前にテキストレイヤー、いわゆる【テクス体】を【アライズ】しておく。テクス体を【ゲットレディ】状態でスクリプトをゲラウェイすれば、テクス体の位置に、テクス体と同じサイズのクワッド体が、テクス体のひとつ下の【レイヤロイド】としてエピックされる。これは…そうか、座布団として使えるわけだ。あいつめ、おもしろくなってきたぜ。
シェイプモードと同じく、エフェクトにアライズしたスライダー群である【ファクト:シクセス】でテクス体のナガル、タガルのエクストリームはもちろん、上下左右にピクト、セクトできる。
この段階では独立した【隠密モード】であるが、オフトゥンをテクス体にA:Aすると、【追従モード】に移行可能で、この場合、A:A先であるテクス体の移動(アクスル)、拡大(インティリティ)、縮小(センティリティ)、回転(カイティーン)といったトランスフォームにオフトゥンが追従することとなる。
フェイタルにサクシードする過程で、あえてジェムクスにガルドするのも面白いだろう。
[解説]
var scName="zabTheFlexShaper";
var scVersion="1.0";
app.beginUndoGroup(scName+scVersion);
var fxName1 = ["sizeX","sizeY"];//単品で使う用
var fxName2 = ["←","→","↑","↓"];//配列で流し込む用
var cmp = app.project.activeItem;
var sel = cmp.selectedLayers[0];
if ( !(cmp && cmp instanceof CompItem) ){
alert("コンポジションを選択してください。");
}
var newName=f_notSameName("シェイプレイヤー");
var shpLyr=cmp.layers.addShape();
var shpVecGr=shpLyr.property("ADBE Root Vectors Group").addProperty("ADBE Vector Group");
var shpRectGr=shpVecGr.property("ADBE Vectors Group").addProperty("ADBE Vector Shape - Rect");
shpRectGr.property("ADBE Vector Rect Size").setValue([0,0]);
if(sel==undefined || !sel instanceof TextLayer){
//テキストレイヤー未選択【シェイプモード】
shpLyr.name=newName;
f_addFx2(shpLyr,fxName1[0],100);
f_addFx2(shpLyr,fxName1[1],100);
}else{
//テキストレイヤー選択【座布団モード】
shpLyr.moveAfter(sel);
shpLyr.name=f_notSameName(sel.name+"_zab");
var txScale=sel.property("ADBE Transform Group").property("ADBE Scale").value;
var txRect=sel.sourceRectAtTime(cmp.time, false);
f_addFx2(shpLyr, fxName1[0], txRect.width*(txScale[0]/100) );
f_addFx2(shpLyr, fxName1[1], txRect.height*(txScale[1]/100) );
var txanc=sel.property("ADBE Transform Group").property("ADBE Anchor Point").value;
var txpos=sel.property("ADBE Transform Group").property("ADBE Position").value;
var txCenter=[
(txpos[0])+(txRect.left*txScale[0]/100)-(txanc[0]*txScale[0]/100)+(txRect.width/2*txScale[0]/100),
(txpos[1])+(txRect.top*txScale[0]/100) -(txanc[1]*txScale[0]/100)+(txRect.height/2*(txScale[1]/100))
];
shpLyr.property("ADBE Transform Group").property("ADBE Position").setValue(txCenter);
}
shpRectGr.property("ADBE Vector Rect Size").expression=[
'var sizeX =effect("'+fxName1[0]+'")(1);',
'var sizeY =effect("'+fxName1[1]+'")(1);',
'var left =effect("'+fxName2[0]+'")(1);',
'var right =effect("'+fxName2[1]+'")(1);',
'var top =effect("'+fxName2[2]+'")(1);',
'var bottom=effect("'+fxName2[3]+'")(1);',
'\r',
'value+[sizeX+left+right, sizeY+top+bottom];'
].join("\r");
shpLyr.property("ADBE Transform Group").property("ADBE Position").expression=[
'var sizeX =effect("'+fxName1[0]+'")(1);',
'var sizeY =effect("'+fxName1[1]+'")(1);',
'var left =effect("'+fxName2[0]+'")(1);',
'var right =effect("'+fxName2[1]+'")(1);',
'var top =effect("'+fxName2[2]+'")(1);',
'var bottom=effect("'+fxName2[3]+'")(1);',
'\r',
'// 位置打ち消しオフセット',
'var offsetLeft =left/-2;',
'var offsetRight =right/2;',
'var offsetTop =top/-2;',
'var offsetBottom=bottom/2;',
'\r',
'value+[(offsetLeft+offsetRight)*scale.value[0]/100, (offsetTop+offsetBottom)*scale.value[1]/100];'
].join("\r");
f_addFx3(shpLyr, fxName2, 0);
// 塗りをRGBで設定
var shpFill=shpVecGr.property("ADBE Vectors Group").addProperty("ADBE Vector Graphic - Fill");
shpFill.property("ADBE Vector Fill Color").setValue([.1, .1, .1]);
// 線は無しにする(Stroke 幅を 0 に設定)
var shpStroke=shpVecGr.property("ADBE Vectors Group").addProperty("ADBE Vector Graphic - Stroke");
shpStroke.property("ADBE Vector Stroke Width").setValue(0);
function f_addFx1(shpLyr){
var newFx=shpLyr.property("ADBE Effect Parade").addProperty("ADBE Slider Control");
newFx.enabled=false;
return newFx;
}
//単品のsizeXとsizeY
function f_addFx2(shpLyr, fxName1, value){
var newFx=f_addFx1(shpLyr);
newFx.name = fxName1;
newFx.property("ADBE Slider Control-0001").setValue(value);
}
//連続で流し込んでいいleft以降
function f_addFx3(shpLyr, fxName2, value){
for (var i=0;i<fxName2.length;i++){
var newFx=f_addFx1(shpLyr);
newFx.name=fxName2[i];
newFx.property("ADBE Slider Control-0001").setValue(value);
}
}
function f_notSameName(baseName){
var maxNum=-1;
for (var i=1;i<=cmp.numLayers;i++){
var rawName=cmp.layer(i).name;
var name=rawName.trim();
var match=name.match(new RegExp("^"+baseName+"\\s*(\\d*)$"));
if(match){
var num=match[1]?parseInt(match[1], 10):0;
if(num>maxNum)maxNum=num;
}
}
var newNum=maxNum+1;
var newName=(newNum===0)?baseName:baseName+" "+newNum;
return newName;
}
app.endUndoGroup();
// シェイプレイヤーの構造
// layer
// ┗コンテンツ("ADBE Root Vectors Group")
// ┗グループ 1("ADBE Vector Group")
// ┗コンテンツ("ADBE Vectors Group")←隠れプロパティにつき指定漏れ注意
// ┗長方形パス 1("ADBE Vector Shape - Rect")
// ┗サイズ("ADBE Vector Rect Size")
さぁ、めくるめくプログラミングの考え方の世界へダイブしていきましょう。
各function
function f_addFx1(shpLyr){
var newFx=shpLyr.property("ADBE Effect Parade").addProperty("ADBE Slider Control");
newFx.enabled=false;
return newFx;
}
//単品のsizeXとsizeY
function f_addFx2(shpLyr, fxName1, value){
var newFx=f_addFx1(shpLyr);
newFx.name = fxName1;
newFx.property("ADBE Slider Control-0001").setValue(value);
}
//連続で流し込んでいいleft以降
function f_addFx3(shpLyr, fxName2, value){
for (var i=0;i<fxName2.length;i++){
var newFx=f_addFx1(shpLyr);
newFx.name=fxName2[i];
newFx.property("ADBE Slider Control-0001").setValue(value);
}
}84~88行、f_addFx1()はスライダー制御をレイヤーに適用する根幹処理です。またメモリ削減のためにエフェクトの有効状態(enabled)をOFF(false)にします。次のf_addFx2()とf_addFx3()で使い回します。
91~96行、f_addFx2()はf_addFx1()で作られたスライダー制御エフェクトのリネームと初期値のセットです。sizeXとsizeYの適用に使います。【座布団モード】の場合はテキストのwidth, heightをセットしたいので処理を分けています。
98~104行、f_addFx3()は”←”,”→”,”↑”,”↓”のエフェクトを次々適用したいだけで、セットする数値も共通の0でいいので、f_addFx2()を4回呼び出すより少しだけ個人的な管理を楽にしたくてf_addFx2()と別にしました。4回呼び出すならf_addFx2()にまとめてOKな部分です。
shpRectGr.property("ADBE Vector Rect Size").expression=[
'var sizeX =effect("'+fxName1[0]+'")(1);',
'var sizeY =effect("'+fxName1[1]+'")(1);',
'var left =effect("'+fxName2[0]+'")(1);',
'var right =effect("'+fxName2[1]+'")(1);',
'var top =effect("'+fxName2[2]+'")(1);',
'var bottom=effect("'+fxName2[3]+'")(1);',
'\r',
'value+[sizeX+left+right, sizeY+top+bottom];'
].join("\r");
shpLyr.property("ADBE Transform Group").property("ADBE Position").expression=[
'var sizeX =effect("'+fxName1[0]+'")(1);',
'var sizeY =effect("'+fxName1[1]+'")(1);',
'var left =effect("'+fxName2[0]+'")(1);',
'var right =effect("'+fxName2[1]+'")(1);',
'var top =effect("'+fxName2[2]+'")(1);',
'var bottom=effect("'+fxName2[3]+'")(1);',
'\r',
'// 位置打ち消しオフセット',
'var offsetLeft =left/-2;',
'var offsetRight =right/2;',
'var offsetTop =top/-2;',
'var offsetBottom=bottom/2;',
'\r',
'value+[(offsetLeft+offsetRight)*scale.value[0]/100, (offsetTop+offsetBottom)*scale.value[1]/100];'
].join("\r");46行目からは長方形 1のサイズと、シェイプレイヤー>トランスフォーム>位置へのエクスプレッションを埋め込む部分です。
これは”sizeX”と”sizeY”が追加されている以外はエクスプレッションでシェイプを柔軟にサイズ調整するを埋め込んだだけなので解説はそちらを参照のこと。
シェイプレイヤーの構造の再確認
スクリプトで作るシェイプレイヤーのクセが強いため、おさらいです。
var newName=f_notSameName("シェイプレイヤー");
var shpLyr=cmp.layers.addShape();
var shpVecGr=shpLyr.property("ADBE Root Vectors Group").addProperty("ADBE Vector Group");
var shpRectGr=shpVecGr.property("ADBE Vectors Group").addProperty("ADBE Vector Shape - Rect");
shpRectGr.property("ADBE Vector Rect Size").setValue([0,0]);
if(sel==undefined || !sel instanceof TextLayer){
//テキストレイヤー未選択
shpLyr.name=newName;
f_addFx2(shpLyr,fxName1[0],100);
f_addFx2(shpLyr,fxName1[1],100);
}else{
//テキストレイヤー選択
shpLyr.moveAfter(sel);
shpLyr.name=f_notSameName(sel.name+"_zab");
var txScale=sel.property("ADBE Transform Group").property("ADBE Scale").value;
var txRect=sel.sourceRectAtTime(cmp.time, false);
f_addFx2(shpLyr, fxName1[0], txRect.width*(txScale[0]/100) );
f_addFx2(shpLyr, fxName1[1], txRect.height*(txScale[1]/100) );
var txanc=sel.property("ADBE Transform Group").property("ADBE Anchor Point").value;
var txpos=sel.property("ADBE Transform Group").property("ADBE Position").value;
var txCenter=[
txpos[0]+(txRect.left-txanc[0])+(txRect.width/2),
txpos[1]+(txRect.top -txanc[1])+(txRect.height/2)
];
shpLyr.property("ADBE Transform Group").property("ADBE Position").setValue(txCenter);
}After Effectsでデフォルト機能の長方形ツールでシェイプレイヤーを作った場合、構造はこうなっていましたね。
layer
┗コンテンツ("ADBE Root Vectors Group")
┗グループ 1("ADBE Vector Group")
┣1.描画モード("ADBE Vector Blend Mode")
┣2.コンテンツ("ADBE Vectors Group")
┗長方形パス 1("ADBE Vector Shape - Rect")
┣1.パスの方向(ADBE Vector Shape Direction)
┣2.サイズ("ADBE Vector Rect Size")
┣3.位置("ADBE Vector Rect Position")
┗4.角丸の半径("ADBE Vector Rect Roundness")
┣線 1("ADBE Vector Graphic - Stroke")
┗塗り 1("ADBE Vector Graphic - Fill")
┗3.長方形 1のトランスフォーム("ADBE Vector Transform Group")
┣アンカーポイント("ADBE Vector Anchor")
┣位置("ADBE Vector Position")
┣スケール("ADBE Vector Scale")
┣歪曲("ADBE Vector Skew")
┣歪曲軸("ADBE Vector Skew Axis)
┣回転("ADBE Vector Rotation")
┗不透明度("ADBE Vector Group Opacity")
参照:AfterEffects compZero様
https://sites.google.com/site/annamillersclub/script-menu/逆引きスクリプト/シェイプレイヤーを作る
この内、2.コンテンツ(“ADBE Vectors Group”)は隠れプロパティにつき実際はあるのに見えません。プロパティが多いからごちゃつかないようになのか?わかりませんが歴代のバージョンでずっとこうです。ただし、存在はしているのです。
なのでスクリプトから各プロパティへアクセスする場合は階層の指定漏れに注意です。もちろんスクリプトから長方形シェイプレイヤーを作る場合もきちんと階層を省略すること無く.addProperty()していかなければなりません。スクリプトで作っても、見えない状態で追加されます。トラップです。2.コンテンツを作らないと、長方形パス 1が作れません。
今回のスクリプトの流れは、コンポジションにコンテンツだけ存在するシェイプレイヤーをaddShape()し、デフォルトで作った際は「長方形 1」という名前が自動でつくグループ1を.addProperty(“ADBE Vector Group”)し、忘れずにコンテンツを.addProperty(“ADBE Vectors Group”)、長方形パス 1をようやく.addProperty(“ADBE Vector Shape – Rect”)します。
.addProperty(“ADBE Vector Shape – Rect”)した段階で、下の階層のパスの方向~角丸の半径までは追加されます。
つまり、線と塗りはまだ追加されませんので、また後で追加する必要があります。
var shpFill=shpVecGr.property("ADBE Vectors Group").addProperty("ADBE Vector Graphic - Fill");
shpFill.property("ADBE Vector Fill Color").setValue([.1, .1, .1]);
// 線は無しにする(Stroke 幅を 0 に設定)
var shpStroke=shpVecGr.property("ADBE Vectors Group").addProperty("ADBE Vector Graphic - Stroke");
shpStroke.property("ADBE Vector Stroke Width").setValue(0);77行目からこの処理です。
「塗り」を見えないコンテンツ(“ADBE Vectors Group”)に.addProperty(“ADBE Vector Graphic – Fill”)します。(“ADBE Vector Fill Color”)に初期値として”1A1A1A”となる薄っすらグレーを.setValue()します。RGBAを0~1で指定です。馴染み深い16進数の#RRGGBBではないので注意しましょう。アルファのAは省略可能です。
「線」は(“ADBE Vector Graphic – Stroke”)となるだけで同じ階層に追加です。(“ADBE Vector Stroke Width”)に初期値として線幅0をセットします。
デフォルト機能でシェイプレイヤーを作る際は自動でいろいろ追加してくれていたことがわかります。ユーザーに寄り添った仕様で本当にありがたいですね。ただ、表示されない(“ADBE Vectors Group”)だけは70際まで許せそうにありません。70歳になったらたとえ仕様が変わらずとも許すと決めています。
今回おさらいする処理
同名レイヤーを作らない仕掛けに使った、正規表現です。これまでの記事でも形を変えて正規表現は登場してきましたが、これもおさらいしましょう。
function f_notSameName(baseName){
var maxNum=-1;
for (var i=1;i<=cmp.numLayers;i++){
var rawName=cmp.layer(i).name;
var name=rawName.trim();
var match=name.match(new RegExp("^"+baseName+"\\s*(\\d*)$"));
if(match){
var num=match[1]?parseInt(match[1], 10):0;
if(num>maxNum)maxNum=num;
}
}
var newNum=maxNum+1;
var newName=(newNum===0)?baseName:baseName+" "+newNum;
return newName;
}108行目からのf_notSameName()でコンポジション内のレイヤー名を全てさらって、同名のレイヤー名があれば末尾に連番を追加します。「シェイプレイヤー 5」があれば「シェイプレイヤー 6」にします。
.match(new RegExp(“^”+baseName+”\s(\d)$”))で正規表現で検索しています。
- ^→開始位置の1文字目に何かがあるかを指定する。今回は”シェイプレイヤー”という文字列で始まっているものにマッチ。
- \s→空白文字があるか。
- *→直前の条件に「0個以上の」という条件を足す。\sで0個以上の空白文字を探す。
- \d→0~9までの数字があるか。
- ()→グループ化。下の処理でここでマッチした部分だけをmatch[1]として半角数字を取り出せるようにする。
- $→文字列末尾になっているか。以降に余計な文字があればマッチしない。
これらを組み合わせ、「”シェイプレイヤー”」で始まり、「半角スペース」、「数字」の形になっているレイヤー名を判定していることになります。
115行目から118行目まで、レイヤー名末尾の番号の最大値をmasNumに格納、121、122行目で最大値+1でリネームするのに使います。
ベース名の”シェイプレイヤー”で始まっているか確認するため、「^」です。「あシェイプレイヤー」など、レイヤー名冒頭が”シェイプレイヤー”で始まっていなければ無視します。今回のプログラム上はbaseNameに”シェイプレイヤー”をぶち込んでいるので、「^」はなくても動きます。
まとめ
調整の幅を広げたシェイプレイヤーのアイディアと、これの具現化、さらにシェイプレイヤーと正規表現の理解を深める、これらに発展することが出来ました。
After Effectsの表現力を高めるために、効率化と仕様の理解が不可欠です。ぜひ当サイトのスクリプト記事を使って、さらなる映像表現の探求に進まれてください。







この記事へのコメントはありません。