4
$\begingroup$

I have a animation where one object moves through the 3D space. How can I get the location of this object which it has at a specific frame?

I tried something like this:

obj = bpy.context.active_object
ol = obj.location

# set current frame to 1
bpy.context.scene.frame_current = 1

pos_1 = (ol.x, ol.y, ol.z)

# set current frame to 5
bpy.context.scene.frame_current = 5
pos_5 = (ol.x, ol.y, ol.z)

# set current frame to 10
bpy.context.scene.frame_current = 10
pos_10 = (ol.x, ol.y, ol.z)

# set current frame back to 1
bpy.context.scene.frame_current = 1

print('Frame 1 {0}, id: {1}'.format(pos_1, id(pos_1)))
print('Frame 5 {0}, id: {1}'.format(pos_5, id(pos_5)))
print('Frame 10 {0}, id: {1}'.format(pos_10, id(pos_10)))

But the output for all Frames is the same except for the id's. So, how can I get the position of an object at a specific frame?

$\endgroup$
1
$\begingroup$

The "cleanest" answer is to look into the obj.animation_data.action.fcurves list to find the ones with data_path=="location" and use

loc[fc.array_index] = fc.evaluate(frame)

to evaluate them. Things get tricker if you have NLAs or drivers. It makes me wish the animation_data property had an evaluate(data_path, frame) method.

Then again, you might be able to just bpy.context.scene.update() after each frame_current= in your original version.

$\endgroup$
  • $\begingroup$ When I look into obj.animation_data.action.fcurves list I find multiple items with data_path == 'location'. It seems that for each channel (x, y, z) there is one item in the list. Is there a way to distinguish which channel corresponds to which axes? $\endgroup$ – Miguellissimo Aug 7 '14 at 21:43
  • $\begingroup$ Yes, you distinguish them using fc.array_index. I guess I didn't explain why I did that in my code example. $\endgroup$ – Mutant Bob Aug 8 '14 at 14:45
  • 2
    $\begingroup$ Cleanest depends on your purpose, While fast and useful in some cases - this wont take a lot of things into account, Parenting, constraints, drivers, physics sim. $\endgroup$ – ideasman42 Aug 8 '14 at 16:44
6
$\begingroup$

Try using frame_set() instead of directly setting the current frame:

import bpy

obj = bpy.context.active_object
ol = obj.location

# set current frame to 1
bpy.context.scene.frame_set(1)

pos_1 = (ol.x, ol.y, ol.z)

# set current frame to 5
bpy.context.scene.frame_set(5)
pos_5 = (ol.x, ol.y, ol.z)

# set current frame to 10
bpy.context.scene.frame_set(10)
pos_10 = (ol.x, ol.y, ol.z)

# set current frame back to 1
bpy.context.scene.frame_set(1)

print('Frame 1 {0}, id: {1}'.format(pos_1, id(pos_1)))
print('Frame 5 {0}, id: {1}'.format(pos_5, id(pos_5)))
print('Frame 10 {0}, id: {1}'.format(pos_10, id(pos_10)))

Directly setting the current frame does not update all the animated values and re-evaluate drivers, etc. so the object position is not updated.
Using frame_set() will update everything, so the location is printed as expected.

$\endgroup$
  • $\begingroup$ frame_set() still doesn't update location for me. $\endgroup$ – rfabbri Sep 8 '15 at 5:17
  • $\begingroup$ huh, frame_set() worked for me, while scene.update() did not. Strange... $\endgroup$ – JonathanK Jul 14 '17 at 12:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.