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$

2 Answers 2

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$
3
  • $\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$ Commented Aug 7, 2014 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
    Commented Aug 8, 2014 at 14:45
  • 3
    $\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
    Commented Aug 8, 2014 at 16:44
7
$\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$
3
  • $\begingroup$ frame_set() still doesn't update location for me. $\endgroup$
    – rfabbri
    Commented Sep 8, 2015 at 5:17
  • $\begingroup$ huh, frame_set() worked for me, while scene.update() did not. Strange... $\endgroup$
    – JonathanK
    Commented Jul 14, 2017 at 12:52
  • 1
    $\begingroup$ using bpy.ops.rigidbody.bake_to_keyframes() at the beginning before frame_set() solved the problem for me $\endgroup$
    – dome
    Commented May 9, 2020 at 14:44

You must log in to answer this question.

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