1
$\begingroup$

First off, I should warn you that I am a complete newb when it comes to scripting in Blender, so I might have made some really stupid mistakes in my script.

I want to move the ikHeelRight bone in my rig by one unit in the x direction, and one in the y direction, for every single keyframe in every single animation in my blend file. Normally, I'd do this in the graph editor, but since I have upwards of 70 animations and I'm unsure if I will need to adjust a bone again in the future, I thought I'd write a script to do it.

I get no errors when I run this script, but it does absolutely nothing. I have also tried this without the keyframe "For" loop, and it also does nothing. I suspect that my script is failing to select the ikHeelRight bone, because after I run it, the heel bone is not selected at all. I have checked to make sure I have the name of the bone and the rig correct. However, I expect that's not the only failure point. Here's the script:

    import bpy
    import mathutils

    bpy.context.scene.objects.active = ob
    bpy.ops.object.mode_set(mode='POSE')

    footRight = bpy.data.objects["Armature"].data.bones["ikHeelRight"]

    sce = bpy.context.scene

    animations = list(bpy.data.actions)

    for animations in bpy.data.actions:
        for f in range(sce.frame_start, sce.frame_end+1):
            footRight.select = True
            footRight.head +=Vector((1,1,0))
            footRight.tail +=Vector((1,1,0))

Thank you guys for any help you can give.

$\endgroup$

1 Answer 1

1
$\begingroup$

While the outline of your idea is right, altering the animation data takes a bit more. Firstly armatures contain two sets of bone data, one is the rest position that we change in edit mode when creating the rig, the second set is the pose bone data that we animate. The object.data.bones that you are using is the edit bone data. Using the pose bones found at object.pose.bones you could position and keyframe the bones but you will find that altering the keyframe data directly to be easier for your task.

An action contains a list of fcurves with each fcurve containing the animation data for one value, so there is one fcurve for the x location and another for the y location and so on, so you need to alter each value separately. Each curve has a list of keyframes that contains the frame and value used for the property that is keyframed. This leads to going through the fcurves of an action and looking at the data_path and array_index to decide if it is a value you want to alter.

If you are unsure of the data_path or array_index values, you can find them in the graph editor properties region.

fcurve properties

To adjust the location of a bone in every action you could use the following.

import bpy

target_bone = 'ikHeelRight'

# Adjustments in (x, y, z) direction
adjustments = (1, 1, 0)

for act in bpy.data.actions:
    for fc in act.fcurves:
        if target_bone not in fc.data_path or 'location' not in fc.data_path:
            continue

        assert 0 <= fc.array_index < 3  # always good to make your assumptions explicit
        for p in fc.keyframe_points:
            p.co[1] += adjustments[fc.array_index]

The p.co[0] value is the frame number, so if you wanted to retime your animation you would change that.

$\endgroup$
3
  • $\begingroup$ I've taken the liberty to simplify the script a bit, in two ways. The first is to place the adjustments in a tuple (x, y, z); this avoids the triple if statement. The second change is to invert the condition inside the for fc in ... loop; by using a continue statement the rest of the loop body can be unindented a step. $\endgroup$
    – dr. Sybren
    Commented Dec 24, 2017 at 12:52
  • $\begingroup$ I think I expanded that to demonstrate what each array_index relates to. Also you changed p.co[1] to p.co[0] which will change the frame number not the keyed value. $\endgroup$
    – sambler
    Commented Dec 24, 2017 at 18:54
  • $\begingroup$ Changing p.co[1] to p.co[0] was a mistake indeed. I think that adjustments[fc.array_index] is much clearer; otherwise you have to investigate three if-statements and their corresponding bodies before understanding that they all do pretty much the same thing. And array_index is literally the index into an (x, y, z) array. $\endgroup$
    – dr. Sybren
    Commented Dec 24, 2017 at 21:03

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .