TU/TBに合わせて自動で回転させる

はじめに

アニメでのカメラワーク常套手段「回転TU/TB」。幾度となく出てくるが、等速ならまだいい。問題なのはイージング(フェアリング)やクイックの時。位置と回転を合わせるのが大変で嫌いだ...なんて経験した人もいると思う。

でもそれは、ただやり方を知らない(or 教えられてない)だけ。

エクスプレッションでリンクしてしまえば、苦悩から解き放たれる。

前提として、ここでは「スケール」ではなく「位置」プロパティのZ値を使って奥行きの表現をする。

位置と回転が合っていない例TB減速してるのに回転してるから気持ち悪い

スマホで撮るとしたら、で考える

なによりまず、何が正解なのか。何がどうなっていれば、「らしく」見えるのか。

これは個人的な考えかもだけれど、アニメだとしても常に「このカットを実写で撮った場合、現場でどういう素材を収録して、編集時にどうやるだろうか」と考える。リアルではアニメの「クイックTU/TB」は撮れっこない。いい機材を駆使したら出来なくはないだろうけど...。100m走とか水泳とかで機械制御で選手についてくるようなカメラは除外して。スマホで考える。

「らしさ」って実写らしさ、だと思う。

リアルで考えると、カメラを回転させつつ、ドリーショット(レールが無ければ手持ちで頑張る)。その際のスピードは移動も回転も、できるだけ等速で収録しておいて、編集時にタイムリマップで好きなタイミングにする、んじゃない?

(動きのある芝居を画面に入れるなら話は変わってくるので、静物のみのカットとして)

となると、「位置」と「回転」の関係って1:1の関係っていうか。「移動は等速なのに途中で回転だけが遅くなる(or早くなる)」あるいは逆に「回転は一定なのに移動スピードが変化している」というのはやっぱり不自然。多分、実写映像で見たら「下手だな...」て感じると思う。

もちろん、移動が止まっても回転する場面もある。机の上に放り投げた何かが回転しながら滑ってる時とか。でもそれって「滑ってる時」ですよね。

滑ってる演出にしたいならそれでいいけれど、基本はやっぱり滑ってないほう。

頭と尻にだけキーフレームさらにキーフレームをリニア補間にした場合これが基本と考える

手動でやるには

で、滑ってないように見せるにはAfterEffects上で何をすればいいのか。

いきなり結論を書くと

「速度グラフの形を合わせろ」

これ。

グラフエディターで速度グラフの形を合わせる。頑張って見比べながら合わせる。値グラフでは意味がないので間違えない。

「グラフの高さを自動ズーム」にしておいて、2つのプロパティを行ったり来たりしてグラフを調整する。

(右の画像はPhotoshopで合成してるので2つのグラフが重なって見えるが、AE上ではこういう重ね方は出来ない。クリック、クリック...でプロパティの選択を変えながら比べるしか無い。諦めてくれ。別のプロパティのグラフを表示する機能はあるが、グラフの縦軸スケールが全然違うので、右の画像のような表示には出来ない。)

プレビューすれば、近づけば近づくほど違和感がなくなってくるのがわかるはず。

ということは、目指すは「速度グラフの形を合わせる」ことであるのがわかる。

※形を合わせるというと語弊を生むが大目に見てください、すみません。
グラフの形を全く合わせていない時
グラフの形を近づけた時

コツは、まず「影響」の数値を一致させること。キーフレームのハンドルの長さを変えれば「影響」の値が変わる。ドラッグでは到底合いっこないので、数値をコピペする。

キーフレームをダブルクリック、もしくはCtrl + Shift+ K で「キーフレーム速度」ウィンドウを出す。

その「影響」の値をコピーしてもう一方のプロパティの「影響」に貼り付け。

あとはキーフレームの値を変更(グラフで言うと上下にドラッグ)をして形を近づけていく。

慣れれば大したことはないが、でも後で修正もするし、その度直すなんてそんな面倒なこと毎回やってられないので、どうしたら自動化できるのかなって考える。

ハンドルとその影響値
「キーフレーム速度」のウィンドウ

作りたいものは

「位置を自由に動かすと回転が勝手についてくる」というものを作りたい。だったら、エクスプレッションを書くべきは「回転」に対してである。

超初心者がやりそうな、回転を位置に直接リンクさせても意味はない。

//「回転」に書いたエクスプレッション

position[0];

なんて書いてみても、x座標の数字をそのまんまコピーしてくるだけなので全く使えないのはわかる。回転に500や1000みたいな数値が入るだけである。

位置をまんま参照するのではなく、

「位置の値が〇〇の時は、回転の値は●●。位置の値が△△の時は、回転の値は▲▲」

てことがしたい。そういうときにいいエクスプレッションがあります。

linear()について① イメージ

linear()は文字通りプロパティ同士のリニアな関係を作るエクスプレッション。ここでの「リニア」は、「1次方程式」という意味でいいだろう。数Ⅰでやりましたね。

y = ax + b

のグラフをイメージすれば良い。

方程式というのは「Xの値を決めれば自動的にYの値がわかる。」というもの。

つまり今回のケースに当てはめると、

「『位置』の値を決めれば『回転』の値がわかる。」

行けそうな気がしますね。今回はこれを使う。

linear()は、このグラフを作成しているとイメージする

linear()について② 引数

では、linear()の使い方を見ていく。

公式マニュアルを見るとlinear()だけでもいくつか載っているが、一番の基本は次のもの。他のはこれを元にした省略形に過ぎない。

linear(t, tMin, tMax, vMin, vMax);

日本語化するならば、

「tの値がtMinからtMaxに変わる間、vMinからvMaxを取る」

もしくは

「tの値がtMinのときvMinを取り、tの値がtMaxのときvMaxを取る1次方程式」

て感じ。

初めて見たときは、引数が5つも必要で敬遠しがち。でも理解すればなんてことはない。

数学っぽくyとxを使って示したほうが分かりやすい人はこうしてみる?

y = linear(x, x1, x2, y1, y2);

「xがx1のときyはy1を取り、xがx2のときyはy2を取る1次方程式」

という意味。

具体的に置き換えると、xは「位置」、yは「回転」。

ここで思い出してほしい。数学の時間、「1次関数のグラフを書け」なんて問題があったと思う。どうやって書いてた?グラフ上の2点を決めて、その両方を通る直線を引いていたのでは?(x=0のときのy切片と、y=0のときのx切片を求めることが多かったかな?)

つまり、異なる2点が分かれば1次関数のグラフが書けるってこと。逆に言うと、異なる2点の座標がわからないとグラフは書けない。その異なる2点というのが、

座標 (x1, y1) と座標 (x2, y2) である。

すなわち、5つも引数があるが、必要とされているのは、

    • 参照するプロパティ(今回は「位置」)

    • グラフを書くため(つまり1次方程式の作成)に必要な2つの座標

これだけ。

それらを与えれば、あとはx(=位置)がどんな値であろうとも、linear()が1次方程式のグラフに合わせて計算してくれる。

もひとつ気をつけたいのが、linear()は全ての引数に「スカラー値」しか取れない、ということ。

linear(position, 0, 100, 0, 100);

のように、positionをそのまま入れてしまうとエラーが出る。

positionは[x, y](あるいは[x, y, z])の値を持つので、2つ(もしくは3つ)の数字でワンセット。ベクトルである。ベクトルはスカラーと相反するもの。そのまま渡すことはできない。

なんだじゃあ「位置」とリンクはできないのか、と言うとそうじゃない。位置の[x,y,z]のうち1つをlinear()に与えてやればいい。今回はz値を参照しようと思う。

これら2点を通る直線を引けばグラフが書ける。逆に言うと、グラフを書くために最低限必要な情報がこの2点の座標だということ。

やってみる

実際にAfterEffects上でやっていく。

まずはスタートフレームとエンドフレームを決めて、「位置」と「回転」にキーフレームを打つ。(図1)

それぞれ2つずつキーフレームを打ったら、それらが「必要な2つの座標」のデータ、ということ。

今回は、

「Z位置が-500のとき、Z回転は-30°で、Z位置が0の時、Z回転は

にしたので、座標的に書くと(-500, -30)と (0,0)。(実際の数値は好きにして)

これらをlinear()に渡すと、

p = position;

linear(p[2], -500, 0, -30, 0);

こうなる。解説すると、

1行目の

p = position;

これはlinear()の中で「position」と何回も書くのがめんどくさいから1文字に置き換えているだけ。

ちなみに、thisLayer.transform.positionみたいに丁寧に書いてもいいが、むしろそれのほうがよく見かける気がするが、エクスプレッションの中では「thisLayer」と「thisProperty」は勝手に補完してくれるので、書く必要はない。あと「transform」も書かなくてもいける。なので「position」と書くだけで「そのレイヤーの位置」が拾える。ピックウィップ使ってる人は「transform.position」で入る。手打ちで書いてると、わざわざtransformを書くほうが面倒なので、この表記になっていると「この人はピックウィップで参照したな」ってわかる。だから何だという話。

閑話休題。

2行目、p[2]の部分。先に書いた通り、「位置」をlinear()にそのまま渡すことはできない。3つの中からどれかを選ぶ。今回は「Z位置」を利用しようと思うので、p[2]である。

先の状態でキーフレームを追加&移動してクイックにする。(図2,3)

エクスプレッションを書いていなければ、ページの最初に貼ったムービーの様になるが、書いているので、クイックになろうが、きれいに回転がついてくるはず。

(図1)まずはこの状態を作る
(図2)適当な場所にキーを打って...
(図3)タイミングをずらすことで、クイックにする
回転とZ座標の関係性さっきまでのグラフと変わらないが一応

まだめんどくさい

回転がきれいについてくるようにはなったが、フレームの位置とか回転とか修正したいなって思ったらどうする?とりあえず思い通りのフレーム位置にして、数値調べて、エクスプレッションを書き直す?バカバカしいですね。

ここは、キーフレームの値を参照するようにする。

エクスプレッション言語メニューの見方

また脱線するが、「エクスプレッション言語メニュー」の使い方を見ておく。

言語メニューを開くと、エクスプレッション内で使えるプロパティやメソッドを確認することが出来る。

「位置」はコンポでもレイヤーでもない。プロパティ、つまり「Property」オブジェクトである。「位置」だけに限らず、エフェクトなどもパラメータ設定できるものは「Property」オブジェクトである。なので「Property」を開いてみる。すると、またいろいろ出てくる。その中で「key」の文字のつくものがいくつかある。

これらを使って、「位置」のキーフレームにアクセスできそう、ということは容易に予想できる。それぞれの意味は、自分でリファレンスを読んで調べるとして。ここではキーフレーム番号を使ってアクセスできる、key(index)というものを使用する。

これでキーフレーム、「Key」オブジェクトが取得できるが、キーフレームといってもいろんな情報を持っている。

    • 時間

    • キーフレーム番号

これらをまとめたものが1つのキーフレームである。

「Key」オブジェクトの中にもさらにプロパティがあるということ。それを調べる。

「エクスプレッション言語メニュー」を開き直して、「Key」の項目を展開する。すると、

    • value

    • time

    • index

3つのプロパティが出てきた。こいつらにアクセスして初めて、キーフレームから値や時間、キーフレーム番号といった情報を取得できる。


閑話休題。

「エクスプレッションの言語メニュー」を見るとオブジェクト構造が把握できる
例えばその中の「Property」を開くと...
「Property」オブジェクトが使用できるプロパティやメソッドが表示される
「Key」を開くと3つのプロパティが使用できるのがわかる

キーフレームの値を参照するようにする

位置のキーフレームにアクセスするには、

position.key(1)

のように書く。ここで「1」は「1番目のキーフレーム」ということ。数字を変えれば何番目のキーフレームでもアクセスできる。もちろんキーフレームの数を超えた数値はNG。

1番目はいいとして、じゃあ次は2番目を取ればいいのか。あとでキーフレームの数が増えたらどうする?また直す?ありえないね。

エクスプレッションを書く時、ちょっとした数値の変更のためにエクスプレッション自体を修正するというのは賢くない。これは大前提、念頭に置くべき。

(めんどくさい時はやるときもあるけどさ...)

じゃあ変更する可能性があるキーフレームの数をどうやって得るか?「エクスプレッション言語メニュー」を開く。

Property -> numKeys

というのがある。言葉のまんま、「キーの数」。これが使える。そのプロパティに存在するキーフレームの数を返してくれる。

position.key(numKeys)

これで常に最後のキーフレームが取得できる。ちなみに「最後から2番目」にしたければ、

position.key(numKeys-1)

とする。

ここまでの話を踏まえると、エクスプレッションは、

// これは間違い

p = position;

linear(p[2], p.key(1), p.key(p.numKeys), key(1), key(numKeys));

となるか?いや、まだダメ。

p.key(1)とは何だったか。キーフレームの情報をパッケージングしたオブジェクト。中には、値、時間、インデックス情報が詰まっている。そんなものをlinear()の引数に渡してよかったか?

最初に、ダイレクトに数値を入れた時、そんな情報を入れたか?

// 数値をダイレクトに入れた書き方

p = position;

linear(p[2], -500, 0, -30, 0);

最終的に、この形に落ち着かないといけない。だが、p.key(1)-500にはなりえない。数値ではなくオブジェクトだから。そこには数値が欲しいのだ。

難しくない。先述の通り、Keyオブジェクトには、まだ下にプロパティがある。さっき見たように3つあった。その中で値を示すのはどう考えてもvalueしかない。

p.key(1).value

で値を取得できる。-500の代わりにp.key(1).valueを入れて、

p = position;

linear(p[2], p.key(1).value, 0, -30, 0);

で行ける!と思う?

よく見て。linear()はスカラー値(1次元の値)しか取れないのであった。対して、p.key(1).valueはどうか?「『位置』プロパティの1番目のキーフレームの値」なのだから、今回の場合だと[400, 300, -500]。バッチバチの3次元の値。

なので第一引数に[2]をつけたように、これもちゃんと1次元の値にする。

p = position;

linear(p[2], p.key(1).value[2], p.key(p.numKeys).value[2], key(1).value, key(numKeys).value);

これでよい。

プリセット化までしないと意味がない

毎回書くのめんどくさい。メモ帳にコピーとかしてる人もいるけど、ffxでプリセット化すれば使いやすくなる。

Z回転のプロパティを「アニメーションプリセットを保存」すればいいのだが、このままではダメ。

プリセット保存は、「キーフレームとエクスプレッション」を保存するようになっている。

キーフレームはいらない。だって毎回、値変わるし。プリセット使う度にいらんキーフレームつけられるのはうざったい。

どうするか。簡単。Z回転のキーフレームを全部削除する。

すると、エラーが出るはず。「エクスプレッションの中でキーフレームにアクセスしているのにキーフレームが無いです!」というエラーが出る。

「OK」で閉じると、エクスプレッションが切れた状態になるがそのままでいい。

そのままZ回転を選択して「アニメーションプリセットを保存」する。

するとキーフレームは存在しないので、エクスプレッションのみが保存される。

次回からはそれを適用すれば、エクスプレッションだけが適用されるってわけ。

もちろん、プリセット使用する前に位置と回転にキーフレームがなかったら実行と同時にエラー吐かれる。

ちなみに、Z回転以外のプロパティを選択した状態でプリセット適用すればそのプロパティに適用できる。

結果

p = position;

linear(p[2], p.key(1).value[2], p.key(p.numKeys).value[2], key(1).value, key(numKeys).value);

これで、「位置」をどんなに動かしても回転が勝手についてくる。今回はQTBにしたが、フェアリング(イージング)とか、TU,TBを繰り返して行ったり来たり(「ガチョーン」みたいな)でもちゃんとついてくるはず。

もちろん求められる表現によっては、これではダメなこともあるかもしれないが、その場合は一旦今回の方法でつけた後、エクスプレッションをキーフレーム化してしまってから手動で調整など、その辺は自由な発想でやっていただければ。