下記のような、一般的なfor文(すべてのキーフレームのデータを見て1つずつ処理するやり方では、数が多いとどう調整しても遅い。
複数のポーズオブジェクトの、何百ものボーンの、何千ものキーフレームをfor文で一括操作すると、1秒などかなり気になるレベルで処理が遅くなる。
obj = bpy.context.object for fc in obj.animation_data.action.fcurves: for ky in fc.keyframe_points: ky
foreach_get/set
コレクションデータで利用することができる、for文より数百倍高速な手段。
ただし、すべてのアイテムに対して実行する一括設定であり、指定のアイテムだけ設定することができない。
obj = bpy.context.object for fc in obj.animation_data.action.fcurves: # データの入れ子となるダミーリスト。アイテムの個数分複製する。 base_crds = [False] * len(fc.keyframe_points) fc.keyframe_points.foreach_get("select_control_point", base_crds) # 値を取得 fc.keyframe_points.foreach_set("select_control_point", base_crds) # 値を設定 # coのようなxとyで複数配列あるデータの場合は、その配列数分乗算する必要がある。 base_crds = [False] * len(fc.keyframe_points) * 2 fc.keyframe_points.foreach_get("co", base_crds) # 状態を取得
coのようなxとyで複数配列あるデータの場合は、ダミーリストは、その配列数分乗算する必要がある。
その場合、XYがすべてまぜこぜになるので、for文で偶数の値だけ設定(if i % 2 == 0)したりする必要があるが、for文を使うと結局遅くなってしまう。これの回避方法がわからない。
内蔵オペレーターを利用する
あまりきれいなやり方ではないが、正直これが一番早い。
キーの選択範囲は、各種標準のキー選択機能を利用してキーを選択し、キーの移動は標準の移動機能を実行する。
タイムラインの表示に依存するが、ボーン選択を一時的に変えたり、Fカーブのロックを活用することで、対象を限定することは可能。
サンプル
カレントフレーム・範囲・前方の3種類から選んで、対象のキーフレームを選択し、移動するサンプルコード。
エディター変更を利用しているため、コンソールエディターで下記のコードを実行した場合はクラッシュするので注意。
import bpy l = ["CURRENT","RANGE","FORWARD"] target_type = l[2] range_frame_start = 25 range_frame_end = 50 delta = 100 sc = bpy.context.scene # show keys old_view_key = sc.show_keys_from_selected_only sc.show_keys_from_selected_only = True # 一時的にタイムラインエディターに変更する old_area_type = bpy.context.area.type bpy.context.area.type = 'DOPESHEET_EDITOR' bpy.context.area.ui_type = 'TIMELINE' # all delselect bpy.ops.action.select_all(action="DESELECT") # カレントフレーム if target_type == "CURRENT": bpy.ops.action.select_column(mode='CFRA') # 指定の範囲 # 選択中のマーカーの間を選択を活用し、一時的にマーカーを作って選択し、一時マーカーを削除します elif target_type == "RANGE": old_mark_sel = [] for i in sc.timeline_markers: if i.select: old_mark_sel += [i] i.select = False item_start = sc.timeline_markers.new('LK_start', frame=range_frame_start) item_start.select = True item_end = sc.timeline_markers.new('LK_end', frame=range_frame_end) item_end.select = True bpy.ops.action.select_column(mode='MARKERS_BETWEEN') # マーカーの間を選択 sc.timeline_markers.remove(item_start) sc.timeline_markers.remove(item_end) for i in old_mark_sel: i.select = True # 現在のフレームの前方 elif target_type in {"FORWARD"}: bpy.ops.action.select_leftright(mode='RIGHT', extend=False) # 移動 bpy.ops.transform.transform(mode='TIME_TRANSLATE',value=(delta, 0, 0, 0)) # エディターを元に戻す # Switch editor bpy.context.area.type = old_area_type # restore show keys old_view_key = sc.show_keys_from_selected_only