こんにちは!ビーバー@ゲーム業界歴約20年python勉強中 です。maya python初心者の方のために、カンタン・わかりやすい解説サイトを作っています。
pythonでアニメーションを処理してみよう♪
…と思ったけど、いろいろコマンドありすぎてわからん!
キーを扱うための概要をザックリおさえたいんだけど、、、
…っと思っている皆さん!おまかせください。ザックリ概要をまとめましたよ☆この記事を読めば、キーフレーム関連コマンドとその使い方の基本がわかります。
最初にコマンドをざっと紹介して、
そのあといくつかのツール作成を通して使い方を見ていくよ☆
※当サイトで紹介する商品は、アフィリエイトプログラムを利用しています。
keyframe 関連コマンド一覧
各コマンドの基本挙動を紹介していきます!
コマンド名の後ろの()に何も入れず使った場合の動作だよ!
キーを打つ setKeyframe()
選択中オブジェクトにキーを打ちます。
・タイムラインの現在フレーム
・チャネルボックスでキー可能となっているアトリビュート
にキーが打たれますよ!
()の中でオブジェクトを指定していないから、
キーを打ちたいオブジェクトを選択しておく必要があるよ!
sキー押した動作になるんだね。
キーを消す cutKey()
選択中オブジェクトのキーを全部消します。
※ただしチャネルボックスで表示されているアトリビュートのみ
キーをコピーする copyKey()
選択中オブジェクトのすべてのキーをコピーします。
そのオブジェクトが持ってるキー(カーブ)をそっくりそのまま全部コピーします。
タイムラインの範囲外もコピりますよ。
copyKeyコマンドを実行してもどってくる「結果」の値は、
コピーしたアニメーションカーブの数だよ!
キーをペーストする pasteKey()
選択中オブジェクトにキーをーペーストします。
コピーしたときのキーの時間や値を、そのままペーストするよ!
キーをスナップするsnapKey()
選択中オブジェクトのキーを、整数フレームにスナップします。
キーを選択している場合は、それだけをスナップします。
※チャネルボックスでキー入力可となってるアトリビュートのみに作用します。
結果の数字は、「スナップされなかったキーを持つ アニメーションカーブの数」だよ!
0ってことは、オブジェクトがもっている全部のキーがスナップされたんだね。
キーの時間や値をスケールする scaleKey()
キーの時間や値をスケールします。
※グラフエディタでキーを選択してスケールする↓この操作のイメージです
【動画】です。左下の再生ボタン▶をおしてね!
タイムレンジや値を何倍にするかの指定がいるから、
scaleKey() カッコの中に指定が必要だよ。
例)
0~10のタイムレンジにあるキーを、0~20のタイムレンジにスケールするなら、
cmds.scaleKey(newStartTime=0, newEndTime=20)
キーの値を2倍にスケールするなら、
cmds.scaleKey(valueScale=2)
キーの時間・値を編集したり、調べたりする keyframe()
キーの時間や値を編集したり、調べることができます。
このコマンドは、キーに対して操作を行うものなので、
()の中にオブジェクトやフラグを指定して使います。
keyframe() ←カッコの中のフラグの書き方を知りたい方はこちら!
フラグは多めだけど、このコマンドの動作をつかむと、
ほかのコマンドの動作もイメージできるようになるよ。
help の python例にあるサンプルで解説していくね!
- キーの数を調べる
cmds.keyframe( 'pCube1', attribute='translateX', query=True, keyframeCount=True )
pCube1の transXのキーを数えるよ!
- キーのタイムと値をリストで取得する
cmds.keyframe( 'pCube1', time=(0,20), query=True, valueChange=True, timeChange=True)
返ってくるリストは、アトリビュートの並びごとに
キーの時間、キーの値、と羅列されるよ!
図の例だと、リストは下記の順で時間と値が返ってきます。
可視性 0f、値1、20f、値1、transX 0f、値2、20f、値2、
transY 0f、値3、20f、値3、transZ 0f、値4、20f、値4
- 先頭から2つ目のキーの時刻を調べる
cmds.keyframe('pCube1.translateX', index=('1:1',), query=True)
キーはタイムラインの先頭からインデックスが振られているよ!
0から始まるから、index=(‘1:1’,)が2番目のキーだね。
- キーの 「時間」 を、現在時間から相対値でずらす
cmds.keyframe(edit=True, relative=True, timeChange=1, time=(10,20))
10~20の間にあるキーの「時間」を +1するよ、っていうスクリプト例だね。
結果で返ってきた数値は、キーが修正されたカーブの数だよ!
- キーの 「値」 を、相対値でずらす
cmds.keyframe("pCube1", edit=True, relative=True, valueChange=1, time=(10,20))
この例では、10~20の間にあるキーの「値」に1を足しているよ!
- キーの時間を絶対値で指定してずらす
10フレームに存在しているキーを16フレームの位置にずらす例だよ!
cmds.keyframe(time=(10,10),absolute=True,timeChange=16)
- キーの時間・値を絶対値で指定する
cmds.keyframe('pCube1.translateX',edit=True,index=(1,1),timeChange='12',valueChange=10.25)
transXに打たれている「2番目」のキーの時間を12に、値を10.25に設定する例だよ!
- 指定のフレームの瞬間、指定したアトリビュートの値はいくつかを調べる
cmds.keyframe( 'pCube1', at='tx', t=(10,10), q=True, eval=True )
10f時点のtransXの値を調べているよ。
キーのない瞬間の値でも、しらべられるんだね。
- 選択されているキーの数を調べる
cmds.keyframe( 'pCube1', selected=True, q=True, keyframeCount=True )
- 選択したキーの時間を調べる
cmds.keyframe( 'pCube1', attribute='tx', selected=True, q=True, timeChange=True )
結果は [20.0] とカッコに入っているね。
選択されたキーがひとつでも、複数でもリストで返してくれるんだよ。
選択されたキーが複数の場合の例
結果に返ってきているリストの数字は、各キーフレームの時間です。
この例では、グラフエディタで並んでいるアトリビュートの順にキー時間が返ってきました。
リストの時間の並び順
transXの1番目のキーの時間、2番目の時間、transYの1番目のキーの時間、2番目の時間、
transZの1番目のキーの時間、2番目の時間
「次のキー」の時間を調べる findKeyframe()
「キーがあるドンズバの時間」を調べるkeyframe()コマンドに対して、
findKeyframe()は、
「指定したキーや時間」の前後や、最初・最後にあるキーの時間を調べることができます。
現在時間の「あとにある」キーの時間は?とか
指定時間の「次の」キーの時間は?なんてのが調べられるんだね!
- 現在時間の 直後にあるキー の時間を調べる
cmds.findKeyframe( timeSlider=True, which="next" )
この例では、現在時間が30fに設定されていて、
その直後にあるキーの時間「40f」が、結果として返ってきているよ!
- タイムスライダ時間の 直前にあるキー の時間を調べる
cmds.findKeyframe( timeSlider=True, which="previous" )
- 先頭にあるキーの時間を調べる
cmds.findKeyframe( timeSlider=True, which="first" )
- いちばん最後にあるキーの時間を調べる
cmds.findKeyframe( timeSlider=True, which="last" )
- 指定した時間の直後にあるキーの時間を調べる
cmds.findKeyframe( 'pCube1', time=(12,12), which="next" )
12fを指定して、その直後にあるキーの時間を調べる例だよ!
- カーブのあるアトリビュート名を調べる
cmds.findKeyframe( 'pCube1', curve=True )
キーの時間を調べる以外に、アトリビュート名もしらべられるんだね!
キーの接線を編集したり、調べたりする keyTangent()
キーの接線を編集したり、調べたりすることができます。
- 接線の傾きを調べる
cmds.keyTangent( 'pCube1', query=True, time=(5,5), attribute='translateX',inAngle=True )
transX、5fにあるキーの左側接線の傾きを調べるよ!
- 接線の傾きを編集する
cmds.keyTangent( 'pCube1', edit=True, time=(5,5),
attribute='translateX',inAngle=-10 )
transX、5fにあるキーの左側接線の傾きを-10度に設定するよ!
結果で返ってきている数字は、接線が修正されたカーブの数だよ!
- 接線の種類を編集する
cmds.keyTangent( 'pCube1', edit=True, time=(5,5),
attribute='translateX',outTangentType="step")
transX、5fにあるキーの右側接線の補完方法をステップに設定するよ!
キーフレームの前後に無限タイプを設定する setInfinity()
先頭キーフレーム以前と 最後のキー以降に、サイクルなどの無限タイプを設定します。
cmds.setInfinity( preInfinite='constant', postInfinite='cycle' )
先頭キーフレーム以前にはコンスタントを、最後以降にはサイクルを設定する例だよ!
グラフエディタで先頭以前、最後以降のカーブの状態を確認するには、
メニューの ビュー > インフィニティ のチェックを入れてね!
各コマンドの書式は、だいたいこんな感じ
キー関連のコマンドの書式(フラグや引数の指定のしかた)は似ているものが多いです。
厳密にはそれぞれ違うんだけど、ザックリ理解するなら だいたいこんな感じです!
()中の 順番を解説します。
- 最初にノード名
“pCube1” や “‘pCube1.translateX” など - 時間を指定 time=(x,x)
(20,20)なら 20fだけを、(0,30)なら 0~30f の区間指定になる - その他のフラグと値
指定したノードの指定時間のキーに対して、どのような操作をするかのフラグ
例えば、pCube1の20fのキーをコピーしたいなら
cmds.copyKey("pCube1",time=(20,20))
0フレームを起点に、0~30フレームの区間にあるキーのフレームの時間を2倍にしたいなら、
cmds.scaleKey("pCube1", time=(0,30), timeScale=2, timePivot=0 )
pCube2の10~20fのキーの補完をフラットにしたいなら
cmds.keyTangent("pCube1", time=(10,20), inTangentType="flat", outTangentType="step")
…となります!
いろいろ試してみてね!
フラグはいっぱいあって覚えきれないから、
必要になった時に調べるのがよさそうだね!
では、実際に4のツールを作る例で
キーフレーム関連コマンドの使い方を見ていきましょう!
キーフレーム関連コマンドの使い方!
アニメーションをループにするツール
ループモーションを作るツールを作ってみましょう!
先頭キーをコピーして、最終フレームにペーストする単純な処理にしてみます。
cmds.copyKey( "pCube1.translateY", time=(0,0))
cmds.pasteKey( "pCube1.translateY", time=(20,20) )
copyKey()で、キーの接線もコピーされるよ!
わお!カンタンだね!
アニメーションを移植するツール
アニメーションを移植するツールを作ってみましょう!
「アニメーションのあるオブジェクトから、アニメーションのないオブジェクトへ キーをコピーする」
という処理を考えてみます。
処理の流れとしては、こんな感じでしょうか?
- 2つのオブジェクトを取得する
- アニメーションのある方・ない方を判別する
- アニメーションのあるオブジェクトのキーをコピーする
- アニメーションがないオブジェクトへ、コピーしたキーをペーストする
オブジェクトの取得や、キーのコピペはできそうだけど、
アニメーションのありなしを判別するのはどうやったらいいの~(泣)
OK!じゃあ例を見ていこう~☆
オブジェクトにアニメーションがあるかどうかを調べる
keyframe() でキーの数を調べることで、アニメーションの有無を判別できます。
そっか!キーの数が「0」であればアニメーションがないってことになるね!
例えば、3つのキーでアニメーションしているpCube1があるとして、キーの数を調べてみましょう。
cmds.keyframe( 'pCube1', query=True, keyframeCount=True )
結果、「3」と返ってきました。
0より大きい数であれば何らかアニメーションが入っているということになります。
そしたら、返ってくる値を変数に入れて、if文で0より大きい値なのかどうかを調べると
アニメーションの有無を判別することができます。
keys = cmds.keyframe( 'pCube1', query=True, keyframeCount=True )
if keys > 0:
print("Node has keys.")
else:
print("Node has nokeys.")
この例では、pCube1 の持っているキーを数えて、変数「keys」に入れ、
if文で「keys」に入っている値が0より大きかったら
“Node has keys.” と表示するようにしているよ!
移植ツールの処理を作る
それでは、移植ツールの処理を作ってみましょう。
アニメーションありpCube1 から、アニメなしpCone1 へ、キーを移植してみようと思います。
考えておいた流れをスクリプトにしていきます。
- 2つのオブジェクトを取得する
- アニメーションのある方・ない方を判別する
- アニメーションのあるオブジェクトのキーをコピーする
- アニメーションがないオブジェクトへ、コピーしたキーをペーストする
nodes = cmds.ls( selection=True )
for node in nodes:
keys = cmds.keyframe( node, query=True, keyframeCount=True )
if keys == 0:
nodeWithoutKey = node
else:
nodeWithKey = node
cmds.copyKey( nodeWithKey )
cmds.pasteKey( nodeWithoutKey )
お、、、おわあ!!もう分からん(泣)!
大丈夫!ちゃんと解説するよ~☆
スクリプトの解説です。
①選択されているオブジェクトを取得して、変数「node」に入れます。
(アニメありpCube1と、アニメなしpCone1を選択して、スクリプトを実行する想定 )
②変数「node」に入った一つずつ(pCube1とpCone1)に、以下の処理を行います。
・キーがあるかどうかを調べる
・キーがなければ、オブジェクトを変数「nodeWithoutKey」に入れる
・キーがあれば、変数「nodeWithKey」に入れる
「nodeWithKey」には pCube1 が、
「nodeWithoutKey」には pCone1 が入ることになるよ!
③nodeWithKey のキーをコピーして、nodeWithoutKey にペーストします。
これを実行すると、、、
はい、この通り~!
pCone1にアニメーションが移植されたよ!
おおッ?!分からんようでわかった、、、!
わかった気がするッッ!!
if文、for文がよくわからないよ!という方は、こちらをご覧ください~☆
for文 学習のページ↓
if文 学習のページ↓
キーをイイ感じにリダクションするツール
キーをイイ感じにリダクションするツール、なんてのもできますよ!
例えば、少ないキーを持つ pCube1 と、全フレームキーがある pCone1 があるとして、
pCone1 のキーを pCube1と同じ数にリダクションするツールを考えてみます。
こちらもまず、どの様な処理にしたらいいか考えてみます。
- pCube1 の、0フレーム目のキーの数を調べる
- キーがあれば何もしない
- キーがないなら、pCone1 の0フレーム目のキーを消す
- 1~3の処理を、フレーム毎に、最終フレーム(20)まで繰り返す
ふむふむ。先にどんなスクリプトにするか、段取りを考えておくんだね。
そうだよ。段取りがしっかり考えられていれば、あとは各コマンドを当てていくだけさ!
それではスクリプトにしてみましょう。
for i in range(21):
count = cmds.keyframe( 'pCube1', attribute='ty', query=True,
time=(i,i),keyframeCount=True )
if count == 0:
cmds.cutKey("pCone1",attribute='ty', time=(i,i))
あ、あれ?意外とあっさり。。。
構えない、構えない(笑)それじゃあ解説いくよ~☆
スクリプトの解説です。
①以下の処理を range()で、21回繰り返す。
(0~20fを調べるため。1~20fでなく0フレーム目がはいっているので、処理は21回になります)
※range() って何?という方は、こちらをご覧ください~☆
②pCube1 のtransYのキーの数を調べる。
time=(i,i)は、キーの数を調べるフレーム数の指定。
for i in range(): なので、「i」には、「for文の繰り返し何回目?」かの数がはいります。
この記述であれば、
for文処理0回目なら0fを、5回目なら5fのキーの数を調べる指定になるよ!
調べたキーの数を変数「count」に入れて、次の処理で使えるようにします。
③変数「count」に入っている値が「0」なら、pCone1のtransYにあるキーを消す。
消すキーのフレーム数は、こちらも「何回目の処理か?」と同じ数を参照。
「count」に入っている値が「0」でない場合は、なにもしないよ。
これを実行すると、、、
おおーー-ーッ!!
pCone1 のキーが、ちゃんとリダクションされたよお!
アニメーションのピークをカットするツール
最後に、アニメーションカーブのピークをカットするツールを作ってみましょう!
先頭フレームから次々に「次のキー」をみつけて、
キーの値が1より大きかったら、値を1に設定する、という処理を作ってみます。
※transYだけにキーを持つpCube1、で考えてみます。
スクリプトの処理を考えてみます。
0フレームのキーから処理を始めたいから、
現在時間をマイナス1にして、次のキーを問い合わせると
0フレームのキーがみつかるよね!…なあんちゃってえ!!(汗)
いいんじゃない?!スタートはマイナス1で、
そこから、次のキーを見つけては処理をしていく、という組み立てにしてみよう!
【処理の流れ】
■事前準備
①現在時間をマイナス1に設定する
②キーの数を調べる(キーの数ぶん、次のキーを見つける処理をしたいので)
■処理の本体
③以下の処理を キーの回数ぶん繰り返す
④現在時間の「次のキーの時間」をみつける(見つけた時間を、変数 time にいれる)
⑤変数 time の時間にある、「キーの値」を調べる
⑥「キーの値」が1よりも大きいなら、値を1に設定する
⑦現在時間を、変数 time の時間に設定する
この処理をスクリプトにします!
オエッ、、、(字詰め苦手)
解説たのんます!!
オ、オッケーー!…(汗)
スクリプトを解説します。
■事前準備(①②)
①現在時間をマイナス1に設定する
cmds.currentTime(-1)
現在時間の設定は currentTime() で設定します。
時間の指定は、() の中に数値を入れればOKです。
②キーの数を調べる(キーの数の回数ぶん、次のキーを見つける処理をしたいので)
count = cmds.keyframe( 'pCube1', attribute='translateY',
query=True, keyframeCount=True )
キーの数を調べるには、keyframe()を使います。
キー数の「照会」なので、queryフラグをTrueにするとコマンドの動作が照会モードになります。
変数 count に調べたキーの数を入れて、以降の処理で使えるようにします。
※変数って何?という方はこちら!
■処理の本体(③~⑦)
③以下の処理を キーの回数ぶん繰り返す
for i in range(count):
「以下の処理」っていうのは、このあと解説していく
④~⑦の処理のことだよ!
for文と range() で、キーの数の回数ぶん処理を繰り返します。
キーの数が変数count に入っているので、count を () 内に指定すればOKです。
※for文って何?という方は、こちら!
※range() って何?という方は、こちらをご覧ください~☆
④現在時間の「次のキーの時間」をみつける(見つけた時間を、変数 time にいれる)
time = cmds.findKeyframe("pCube1", timeSlider=True, which="next" )
「次の」キーを見つけに行きたい場合は、findKeyframe() だったね!
見つけた時間を 変数 time にいれて、以降の処理で使いやすくします。
⑤変数 time の時間にある、「キーの値」を調べる
value = cmds.keyframe( 'pCube1', time=(time,time),
query=True, valueChange=True )
ここでいう、「変数 time の時間にあるキーの値」とはつまり、
「現在時間の直後にあるキーの値」になります。
time=(time,time)で、現在時間の「次のキーの時間」を指定しているんだ。
わかりにくいけど、左辺の time は keyframe() のフラグで、
右辺の (time,time) は、④で作成した変数 time だよ!
値を調べたら、変数 value に入れて、次の処理で使えるようにします。
⑥「キーの値」が1よりも大きいなら、値を1に設定する
if value[0] > 1:
cmds.keyframe("pCube1.ty", edit=True,
time=(time,time), valueChange=1 )
あれ?なんで value[0] って、[0]がついてるの?
keyframe() で値を問い合わせると、↓図みたいにリストで返ってくるんだ。
リストのままだと使えないから、リストから数値を取り出すために[0]をつけてるんだよ。
※if文って何?という方はこちら
※リストって何?という方はこちら
value[0]が、1より大きいかどうか if文で判定して、大きい場合は
変数 time の時間にある、「キーの値」を1に設定します。
if value[0] > 1:
cmds.keyframe("pCube1.ty", edit=True,
time=(time,time), valueChange=1 )
赤字の部分で、「キーの値」を1に設定しているね。
keyframe() で、キーの値を編集するから、edit フラグを True にしているよ。
⑦現在時間を、変数 time の時間に設定する
cmds.currentTime(time)
処理の最後に、現在時間を 変数 time の時間に設定します。
次のキーの時間を見つけて、そのキーに処理をして、
最後に現在時間を「次のキーの時間」に設定して また次のキーを見つけていく、
ってくりかえしなんだね!
そのとおり~~!
スクリプト全文ものせておくから、コピペして使ってね~☆
from maya import cmds
cmds.currentTime(-1)
count = cmds.keyframe( 'pCube1', attribute='translateY',
query=True, keyframeCount=True )
for i in range(count):
time = cmds.findKeyframe("pCube1", timeSlider=True, which="next" )
print(time)
value = cmds.keyframe( 'pCube1', time=(time,time),
query=True, valueChange=True )
if value[0] > 1:
cmds.keyframe("pCube1.ty", edit=True,
time=(time,time), valueChange=1 )
cmds.currentTime(time)
まとめ
今回は、keyframe 関連コマンドとその使い方を紹介しました!
どのコマンドも、アニメーション作成をするときによくお世話になる機能です。
そして、各コマンドのだいたいの書式(フラグの指定方法)はこんな感じでした。
また、keyframe関連コマンドを使った4つのツール作成を通して、
コマンドの使い方を紹介しました。
ツールと言っても、じつは
「GUIを通して手動で行っていた操作」をpythonで自動化した
のが正体だったりします。
紹介した4つのツールのいずれも、同じ考え方で作成しています。
- 自動化したい操作を一つずつ書き出す
- pythonの処理の流れを考える
- 考えた処理の流れをpythonで書いてみる
- 知らないコマンドがあれば調べる
※いろんなコマンドの調べ方を知りたい人はこちらも見てね! - 実行して確認
日頃やっているキー操作のオペレーションで、よく繰り返すルーチンがあれば、
pythonで自動化できるかもしれません。
この記事が皆さんのツール作成の手助けになれればうれしいです!
よくやっている操作の自動化ができたら、作業効率がとっても上がるよ!
ぜひ試してみてね~☆
コメント