View Full Version : Need a script to move keyframes

07-26-2004, 08:28 AM
I'm a complete novice at scripting, but realized today after doing this same thing for the umpteenth time that I should consider scripting to streamline some of the things I do on a regular basis.

The challange of the day is to have a bunch of objects animated, and offset their keyframes by x frames.

Probably really simple, but I've never written a script.

What I've done in the past is to select the objects in the scene editor and shift keys by x frames, deselect one object, shift again, deselect, etc, etc.

Any suggestions greatly appreciated.

07-26-2004, 02:19 PM
If you don't want to get into LScript programming, this may not be useful to you.

Below are a few UDFs I've written recently that illustrate how to read and write channel keys. I include other UDFs necessary to their operation.

While this won't do what you want, I hope you'll find it instructive.

Be sure to check out the getKeyTime function in the LScript documentation on the Newtek website. Since there's no corresponding "set" function, I think you'll have to destroy the old key and create a new one with the same value and a different time.

Be warned; there's a bug in LScript that will generate an error whenever you read a Bezier-type key.

Here're the UDF's. I hope they help.



// BuildChannelList
// Build a name-array of all channels for the object
// Parameter: objChannel - Channel object
// Globals: arrChannelName, arrChannelSelect
BuildChannelList: objChannel
// Initialize glovals
arrChannelName = nil;
arrChannelSelect = nil;

// Valid object?
if (objChannel != nil)
// Populate the list of available channels
arrChannelName = nil;
chnObject = objChannel.firstChannel();
while (chnObject != nil)
arrChannelName += chnObject.name;

/* Get next channel */
chnObject = objChannel.nextChannel();

// CopyChannel
// Copy the named channel from one object to another
// Parameters: objDonor (donor object), objInheritor (inheritor object), strChannel (channel name)
// Note: Existing channel will be superceded by the copied one
CopyChannel: objDonor, objInheritor, strChannel
// Save current selected items
arrSelected = Scene().getSelect();

// Valid parameters?
if ((objDonor != nil) && (objInheritor != nil) && (sizeof(strChannel) > 0))
// Get the donor channel object
chnDonor = GetChannelObject(objDonor,strChannel);
if (chnDonor != nil)
// Find the corresponding inheritor channel
chnInheritor = GetChannelObject(objInheritor,strChannel);
if (chnInheritor != nil)
// Clear the inheritor channel
// Select the inheritor

// Create the key
chnInheritor = GetChannelObject(objInheritor,chnDonor.name);

// Copy the donor channel
chnInheritor.preBehavior = chnDonor.preBehavior + 1;
chnInheritor.postBehavior = chnDonor.postBehavior + 1;
for (i = 1; i <= chnDonor.keyCount; i++)
// Get the donor key object
keyDonor = chnDonor.keys[i];

// Create the inheritor key
chnInheritor.createKey(chnDonor.getKeyTime(keyDono r),chnDonor.getKeyValue(keyDonor));
keyInheritor = chnInheritor.keys[i];

// Set the curve type at the key
crvDonorKey = chnDonor.getKeyCurve(keyDonor);
chnInheritor.setKeyCurve(keyInheritor,crvDonorKey) ;

// Copy data associated with the curve type
switch (crvDonorKey)
case CHAN_TCB:
chnInheritor.setKeyTension(keyInheritor,chnDonor.g etKeyTension(keyDonor));
chnInheritor.setKeyContinuity(keyInheritor,chnDono r.getKeyContinuity(keyDonor));
chnInheritor.setKeyBias(keyInheritor,chnDonor.getK eyBias(keyDonor));
arrHermite = chnDonor.getKeyHermite(keyDonor);
// No data to copy

// Restore original selections


// GetChannelObject
// Find the named channel in the object
// Parameters: objItem (item object containing channels), strChannel (channel name)
// Returns: Channel object if successful, nil if not
GetChannelObject: objItem, strChannel
// Valid parameters?
if ((objItem != nil) && (sizeof(strChannel) > 0))
// Get the first channel in the object
chnObject = objItem.firstChannel();
while (chnObject != nil)
// Correct channel?
if (chnObject.name == strChannel)
/* Try the next channel */
chnObject = objItem.nextChannel();
chnObject = nil;


// ClearChannelKeys
// Clear all keys in the channel object
// Parameters: chnObject (the channel object)
ClearChannelKeys: chnObject
// Valid channel?
if (chnObject != nil)
while (chnObject.keyCount > 0)

// RestoreSelection
// Restore a scene's selections from an array of objects
// Parameter: arrSelection (array of IDs of the object(s) to be selected)
// Note: Use "arrSelected = Scene().getSelect()" to create the array to pass to this routine,
// or manually construct array of object IDs
RestoreSelection: arrSelection
// Go through array
for (i = 1; i <= arrSelection.count(); i++)
// Selecting first item?
if (i == 1)

07-26-2004, 03:08 PM
:eek: OK . . . . wow

Well, scripting is something that the left side o me brain's been nagging me to get into, and I certainly opened a door here!

The more I think about it, the more it looks like it will need to be a pretty complex plugin.

The workflow would go something like this:

1. You call up the plugin (click on the button or whatever)
2. Select the items you want to work with in the order you want them to be staggered
3. Have a dialogue for frame range, and number of frames to move, just like in the scene editor, then click OK, and watch it go.

You have something like this in After Effects under Animation > Keyframe Assistant > Sequence layers. It will do basically what I listed above and it adds the option to overlap where it will add opacity keyframes. The same thing minus the opacity frames would be great here.

I have no idea if anything you wrote there would be moving in that direction. I'll have to see if any of this makes sense later when I'm not on a deadline.

08-04-2004, 09:51 PM
outstanding!! thanks for sharing!