2
$\begingroup$

I am currently developing a script that I hope will streamline the approach of adding keyframes to existing actions used by action constraints.

I am doing this by allowing the user to move an object to a new position, run an operator which will find the action attached to the action constraint for the selected object, load said action onto the object, insert a new keyframe into the action, before removing the action again.

This allows the user to add a new keyframe to the action without having to go through the process of loading and unloading the action manually.

The problem is that the action constraint seems to be adding any movement the user has made to the object and adding the action on top, creating an offset effect.

The annoying thing is that this code does work when entered line by line via the python console, but when run as a whole from the text editor I get the offset problem shown below (after having moved the object and controller 1 unit on the x axis and then running the script):

enter image description here

The Wiki explains that this is expected:

When the linked action affects some location properties, the owner’s existing location is added to the result of evaluating this constraint....

My existing (and simplified) code is below, hopefully the comments explain the steps, for test purposes it is presumed that the original x location is 0 and that the first keyframe is frame 1):

import bpy;
currentFrame = bpy.context.scene.frame_current;
object = bpy.data.objects['Cube'];
constraint = object.constraints[0];   

currentLocation = object.location.copy();             #Store current position
object.location[0] = 0;                               #Undo movement                
object.animation_data.action = constraint.action;     #Assign action
object.location = currentLocation;                    #Move object       
object.keyframe_insert(data_path="location", frame=currentFrame, index=0) #Insert keyframe
bpy.context.scene.frame_current = 1                   #Return to first keyframe 
object.animation_data.action = None;                  #Remove Action            
bpy.context.scene.frame_current = currentFrame;       #Return to initial frame

So, how do I avoid the offsetting issue when running the script from the text editor (which will later be an operator)?

I have included a blend file here, it is already set up with one keyframe in the action on frame 1. Setting a second keyframe on frame 10 by running the code line by line in the python console will show the correct result, running the script from the text editor will not.

$\endgroup$
  • $\begingroup$ For debugging efforts and clarity, I've gone through the procedures outlined above and my observation is that the effect of running it from the text editor is that the origin location (and by that I mean the location of the object in the first keyframe, the one that already exists at execution time) is offset on the x axis by the x displacement of the intended position. Interestingly, only the x axis seems to be affected. The y and z distances are correct as far as I understand the intended purpose. Perhaps this will help track down the problem. $\endgroup$ – DaveLeack Jan 22 '14 at 16:11
  • $\begingroup$ @DaveLeack Thanks for looking, when I removed the 'index' property from the keyframe_insert command so that all 3 axis would be keyframed they did also seem to exhibit the same problem. $\endgroup$ – Ray Mairlot Jan 22 '14 at 16:48
  • $\begingroup$ Rats. At least that indicates the problem is functionally within either the specification of the inputs to the keyframe insert command or the command itself. $\endgroup$ – DaveLeack Jan 22 '14 at 16:52
  • 1
    $\begingroup$ You shouldn't use bpy.context.scene.frame_current = NUMBER, but bpy.context.scene.frame_set(NUMBER) $\endgroup$ – CoDEmanX Jan 22 '14 at 21:45
  • $\begingroup$ @CoDEmanX That did indeed work, if you submitted this as an answer (preferably with the reasoning behind why I shouldn't use frame_current) that would be great. Thanks. $\endgroup$ – Ray Mairlot Jan 24 '14 at 18:05
2
$\begingroup$

You change the frame number twice in your script:

bpy.context.scene.frame_current = NUMBER

The frame_current property should be used for reading the current frame number only, or some special cases in which you don't want to fully update the scene to this frame.

Use the method frame_set(...) instead. It forces an immediate update of all objects in the scene, including animations (transformation matrices etc. will be calculated)

Scripts run from the Text Editor or as addons behave slightly different compared to the Python Console. There are implicit updates when using the PyConsole, which may give the impression of a function causing an update itself. But once you try it in the Text Editor or as addon, you'll experience behavior like you described (basically missing instant updates during an operation, 'cause it's not done automatically).

By the way: although Python supports ; as punctuator, it is no good style.

$\endgroup$

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.