Blanderのカーブ（スプライン）にアニメーションを付ける方法


Blenderのカーブオブジェクトにアニメーションを付ける手段があまり解説されていないようなので
メモと補助スクリプトを書いておきます

# シェイプキー
メッシュのシェイプキー同様に　メッシュの移動状態を記録して移動量を合成します
少ない動きを直線的に合成するだけなら簡単ですが　大きい動きや曲線的な移動を作ることは困難です
![24100601.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151493/9ca567ab-bc1b-0dbc-4bbf-f3abd6fdf140.gif)

# Animall アドオン
Blender標準で内蔵されているアドオンで　頂点の移動等にもキーフレームを打てるアドオンです
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151493/d2d17ff6-6513-6270-dde9-ccc790768efb.png)

編集モードのサイドバーに追加されたタブの設定で
チェックした項目にキーフレームを打つことができます
![24100602.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151493/cff7e494-887f-3d37-8f5c-1c8c0a1174c0.gif)

::: note info
初期状態のタイムライン設定ではキーフレームが表示されない様子なので
ビューメニューの「選択したチャンネルのキーフレームのみ」のチェックを外す必要があります
:::
# Hook（フック）モディファイア
フックは他のオブジェクトやボーンの移動量で　割り当てた頂点の移動をするモディファイアです
### フックモディファイアの割り当て
編集モードで頂点を選択し　制御点メニューの **フック[Ctrl+H]** で 比較的少ない手順で設定できます
![24100603.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151493/70f82c5c-fc4d-a012-c6b2-e456ace0254c.gif)

「**新規オブジェクトにフック**」では　選択している頂点の中心の位置にエンプティが作成されて基準に
「**選択オブジェクトにフック**」では　他のオブジェクトを選択しておいて実行することで そのオブジェクトを基準に
「**選択ボーンにフック**」では　アーマチュアオブジェクトのボーン1つ選択されている状態にして アーマチュアオブジェクト選択状態で実行することで　選択ボーンを基準に

それぞれの基準の実行時の位置から相対的な移動量が選択頂点の移動量になります

### フックモディファイアの調整
ボーンのフックはアーマチュアと違い
編集モードの移動　ポーズモードの移動関係なく移動の影響を受けてしまいます

フックの移動量の計算の設定は　
編集モードでモディファイアのそれぞれのパネルに表示されるボタンを使います
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151493/e8232a4f-02e2-764c-9ce5-d65bbfc1e56c.png)
**リセット**はフックのターゲットになっているオブジェクトの現在の位置が基準になるよう値をクリア
**リセンター**は３Dカーソルの位置が基準になるよう再計算する機能
選択はそのフックの対象になってるポイントを選択　割り当ては選択している頂点がフックの対象に設定しなおします

## カーブのポイントにフックを作成するスクリプト
数個の頂点が対象なら手動でも問題ないでしょうが　カーブの各頂点を制御したい場合には非常に煩雑ですね
カーブの各ポイントにボーンでフックを作成して
カーブを微調整した後に再度実行すると調整したポイントの位置にボーンをリセットした状態にするスクリプトを作成しました

![24100604.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151493/da5062f5-fa51-69da-e5a3-18dcdd506110.gif)


``` y_set_curve2bonehook.py
import bpy
import mathutils

def set_hook_armature(curve_obj):
    # コントロール用のアーマチュアの名前
    target_name = "%s_Hook" % curve_obj.name
    # 作成するボーンのテールの相対位置
    bone_tail_offset = mathutils.Vector([0, 0, 0.25])
    # シーン内にコントロール用のアーマチュアがあるか
    if target_name in context.scene.objects.keys():
        target_armature = context.scene.objects[target_name]
        armature_data = target_armature.data
    else:
        # コントロール用のアーマチュアの作成
        armature_data = bpy.data.armatures.new(target_name)
        target_armature = bpy.data.objects.new(target_name, armature_data)
        context.scene.collection.objects.link(target_armature)
    # ボーンの編集モードに
    bpy.context.view_layer.objects.active = target_armature
    bpy.ops.object.mode_set(mode = 'EDIT')
    edit_bones = armature_data.edit_bones
    # カーブのポイントに対応するボーンの設定
    for sp_id, spline in enumerate(curve_obj.data.splines):
        for point_id in range( len(spline.points) ):
            hook_name = "Hook_%s_%s" % (sp_id, point_id)
            if hook_name in edit_bones.keys():
                hook_bone = edit_bones[ hook_name ]
            else:
                hook_bone = edit_bones.new( hook_name )
            point_pos = curve_obj.matrix_world @ spline.points[point_id].co.to_3d()
            hook_bone.head = point_pos
            hook_bone.tail = point_pos + bone_tail_offset
    bpy.ops.object.mode_set(mode = 'OBJECT')   
    return(target_armature)

def set_hook(armature, curve_obj):
    # カーブのポイントにフックの作成
    # カーブの編集モードに　編集モードでフック対象の頂点の設定をしないと動かない
    bpy.context.view_layer.objects.active = curve_obj
    bpy.ops.object.mode_set(mode = 'EDIT')
    point_index = 0
    for sp_id, spline in enumerate(curve_obj.data.splines):
        for point_id in range( len(spline.points) ):
            hook_name = "Hook_%s_%s" % (sp_id, point_id)
            if hook_name in  curve_obj.modifiers.keys():
                hook_mod = curve_obj.modifiers[hook_name]
            else:
                hook_mod = curve_obj.modifiers.new(name = hook_name, type="HOOK")
                hook_mod.object = armature
                bone_name = hook_name
                hook_mod.subtarget = bone_name
            hook_mod.vertex_indices_set([point_index])
            hook_mod.center = spline.points[point_id].co.to_3d()
            point_index += 1
            bpy.ops.object.hook_reset(modifier = hook_mod.name)
    bpy.ops.object.mode_set(mode = 'OBJECT')

context = bpy.context
# 選択したオブジェクトの種類
active_object =context.active_object
if active_object.type == "CURVE":
    curve_obj = active_object
    armature = set_hook_armature(curve_obj)
    set_hook(armature, curve_obj)
```

と　この記事を書き終えたところで　以前も同様の記事を書いていたのに忘れていたのに気づきました

https://qiita.com/yukimituki11/items/87efd49bca7b802293e6

今回のスクリプトでは再調整機能があって　ちょっと違うのでそのまま公開します
何かの参考になれば幸いです