# Get frame to frame object location Blender 2.81 +

I have a script which generates a curve path and sets an object to follow the path, I can see the object moving correctly in the animation move, and it also moves to the correct location (at least visually in the view port, the coordinates when you press n do not change) when I use bpy.context.scene.frame_set(desirved_frame). However, when I try to get the location and rotation of that object at the frame that I set, it just returns the original location of the object at frame 0. Below is the code I have used to generate the animation data and what I'm using to try and get the location.

curve = bpy.data.objects["curve"]
follower = bpy.data.objects["Follower"]
con = follower.constraints[0]
con.target = curve

bpy.ops.object.select_all(action='DESELECT')
curve.data.use_path = True

path_length_m = curve.data.splines[0].calc_length()
time = path_length_m / car_v_mps
target_fps = 30
length = int(target_fps * time)
print("Frames is: ", length)

anim = curve.data.animation_data_create()
anim.action = bpy.data.actions.new("%s-Action" % curve.data.name)

fcu = anim.action.fcurves.new("eval_time")
mod = fcu.modifiers.new('GENERATOR')
mod.coefficients = (-frame_start / length * 100, frame_start / length / frame_start * 100)

def get_ext(scene, depsgraph):
obj = scene.objects['Follower']
obj = obj.evaluated_get(depsgraph)
print("Frame: ", scene.frame_current)
print("Location: ", obj.location)

scene = bpy.data.scenes[0] #This is the only scene, == bpy.context.scene

for frame in range(scene.frame_start, scene.frame_end):
bpy.app.handlers.frame_change_post.append(get_ext)
scene.frame_set(frame)
bpy.app.handlers.frame_change_post.clear()



I have also tried replacing the final loop with:

for frame in range(scene.frame_start, scene.frame_end):
scene.frame_set(frame)
depsgraph = bpy.context.evaluated_depsgraph_get()
ob = bpy.data.objects["Follower"]
ob = ob.evaluated_get(depsgraph)
print("Location: ", ob.location)



Both methods just print out the original location of the object.

Reading this documentation I'm not sure how I would use the examples they show to extract the evaluated location and rotation from the class they register, or how to even call the execute method.

Below are some solutions and answers I have read and attempted, with no success:

How do I get a mesh data-block with modifiers and shape keys applied in Blender 2.8?

Updating a 2.7 script that uses frame_current to work with 2.8+

Number of vertices and edges report no changes after adding boolean intersect modifier

Location of an object at a specific frame

Handler frame_change_pre doesn't work in render

Sun object not moving when Rendering Animation

I'm really not sure what I'm doing wrong as some of the questions I've looked at are quite similar to my issue.

Thank you for any help you can provide.

• In the small section of code at the bottom of your post try print(f"frame: {frame} \tLocation {ob.matrix_world @ ob.location}")
– Ratt
Apr 20 at 21:02
• @Ratt Thanks for your response. ob.matrix_world @ ob.location does not return the correct location. If I use ob.matrix_world.decompose()[0] then I get the correct location. However, I think this may only be correct for the first frame. Apr 21 at 13:21
• I fixed the issue where Blender was not waiting for the render to finish by only writing the object location and rotations from the frame_change_post function. I moved the rendering to a different function that I call directly after calling scene.frame_set(frame). However, looking on other posts, it seems this may be a common issue, with one possible solution being to add time.sleep(3) (import time) after calling bpy.ops.render, but this did not work in my case. Apr 23 at 14:59

Object.matrix_world.to_translation()

Recommend using the matrix world, of the evaluated object. Which is calculated for each frame of blender. What you see in the UI, is the matrix basis.

Does a child object inherit the matrix from the parent?

Note the local matrix can be gained by multiplying the matrix world of an object, by its parents inverted matrix world. (link above shows how this relates to matrix basis, which is what we set via its components (rot, loc, scale) in the UI)

Here is a quick test script to output the global location of the cube, and evaluated cube for each frame.

There is no need to clear and add a handler for each frame. Do make sure to clear handlers if running script multiple times as they will accumulate.

import bpy

names = ("Cube",)
def frame_change_post(scene, dg):
f = scene.frame_current
for name in names:
ob = dg.objects.get(name)
if ob:
print("dg", ob.name, ob.matrix_world.to_translation()[:])
ob = scene.objects.get(name)
if ob:
print("scene:", ob.name, ob.matrix_world.to_translation()[:])

bpy.app.handlers.frame_change_post.clear()
bpy.app.handlers.frame_change_post.append(frame_change_post)

scene = bpy.context.scene
for f in range(scene.frame_start, scene.frame_end + 1):
print(f"frame {f} ------------------")
scene.frame_set(f)



Output of running script.

Here I've run the script above directly from the text editor. The cube has an arbitrary follow path. Note no difference here between result of un or evaluated object.

frame 1 ------------------
dg Cube (-0.4548962712287903, -7.230154037475586, 0.0)
scene: Cube (-0.4548962712287903, -7.230154037475586, 0.0)
frame 2 ------------------
dg Cube (-0.9097925424575806, -7.199532985687256, 0.0)
scene: Cube (-0.9097925424575806, -7.199532985687256, 0.0)
frame 3 ------------------
dg Cube (-1.357460618019104, -7.115467071533203, 0.0)
scene: Cube (-1.357460618019104, -7.115467071533203, 0.0)
frame 4 ------------------
dg Cube (-1.8044718503952026, -7.026542663574219, 0.0)
scene: Cube (-1.8044718503952026, -7.026542663574219, 0.0)
frame 5 ------------------
dg Cube (-2.238593101501465, -6.889674186706543, 0.0)
scene: Cube (-2.238593101501465, -6.889674186706543, 0.0)


Rendering animation.

dg Cube (-0.4548962712287903, -7.230154037475586, 0.0)
scene: Cube (-2.238593101501465, -6.889674186706543, 0.0)
Saved: '/tmp/0001.png'
Time: 00:00.42 (Saving: 00:00.01)

dg Cube (-0.9097925424575806, -7.199532985687256, 0.0)
scene: Cube (-2.238593101501465, -6.889674186706543, 0.0)
Saved: '/tmp/0002.png'
Time: 00:00.31 (Saving: 00:00.01)

dg Cube (-1.357460618019104, -7.115467071533203, 0.0)
scene: Cube (-0.9097925424575806, -7.199532985687256, 0.0)
Saved: '/tmp/0003.png'
Time: 00:00.34 (Saving: 00:00.01)

dg Cube (-1.8044718503952026, -7.026542663574219, 0.0)
scene: Cube (-1.357460618019104, -7.115467071533203, 0.0)
Saved: '/tmp/0004.png'
Time: 00:00.34 (Saving: 00:00.01)

dg Cube (-2.238593101501465, -6.889674186706543, 0.0)
scene: Cube (-1.8044718503952026, -7.026542663574219, 0.0)
Saved: '/tmp/0005.png'
Time: 00:00.33 (Saving: 00:00.01)

dg Cube (-2.238593101501465, -6.889674186706543, 0.0)
scene: Cube (-2.238593101501465, -6.889674186706543, 0.0)

• Thank you very much, this is working well. The demonstration of the cube location not changing when the object was retrieved from the scene helped me make sense of it. How would I also get the rotation of the object out of this? I tried using ob.rotation_euler on the object from the depsgraph, but it just prints out the original rotation. Apr 22 at 20:50
• ob.matrix_world.to_euler() Apr 23 at 3:08
• Thank you for your help. I have implemented your solution, but I'm now having trouble rendering (I had disabled this before as I just needed to output the ob locations to a file). Do you have any suggestions? Apr 23 at 14:32
• Suggest instead, ask a new question Apr 23 at 15:25