Notifications
Clear all

[Closed] Help improving a keyframe script

Okay, so I’ve got a script that looks at a selected object and drills down through its subanimations to create an array of all the object’s keyframes.

I’d like to modify it to be able to work with multiple objects, i.e. to create a single array that stores every keyframe in a specified collection exactly once.

Ideally I’d like also like to be able to do this as one function, but can’t figure out how to make that work (recursive functions always give me a headache).

Any takers?


EDIT: Just realized there is a problem with this script. It doesn’t actually quite work, but I’m having trouble figuring out why.

fn collectFrames ctrl theFrames =
  (
  for i = 1 to ctrl.numSubs do
  (
  theKeys = ctrl[i].keys
  if theKeys != undefined do
  (
  ctrlFrames = (for k = 1 to theKeys.count collect ((theKeys[k].time as integer) / ticksPerFrame) + 1) as bitArray
  theFrames += ctrlFrames
  collectFrames ctrl[i]
  )
  catch()
  )
  
  local frameArray = theFrames as array
  for f = 1 to frameArray.count do frameArray[f] -= 1
  
  frameArray
  )
 
 
  
  fn getSelObjFrames =
  (
  if selection.count == 1 do
  (
  theFrames = #{}
  
  frameArray = collectFrames selection[1][#transform] theFrames
  theFrames = undefined
  
  frameArray
  )
  )
  
  getSelObjFrames()
10 Replies

I have this much working, but it requires a global variable, so I’m not sure if I’m moving forward or backward here…

frameBitArray = #{}

fn collectFrames ctrl =
(
for i = 1 to ctrl.numSubs do
(
theKeys = ctrl[i].keys

if theKeys != undefined do
(
ctrlFrames = (for k = 1 to theKeys.count collect ((theKeys[k].time as integer) / ticksPerFrame) + 1) as bitArray
frameBitArray += ctrlFrames
collectFrames ctrl[i]
)
)

local frameArray = frameBitArray as array
for f = 1 to frameArray.count do frameArray[f] -= 1

frameArray
)

collectFrames $

See if this is what you’re looking for:

(
	fn collectAllKeyTimes inSubAnim =
	(
		local keyTimes = #()
		if inSubAnim != undefined then
		(
			if isController inSubAnim.controller then
				keyTimes = for k in inSubAnim.controller.keys collect k.time.frame
			
			for i = 1 to inSubAnim.numSubs do
				join keyTimes (collectAllKeyTimes inSubAnim[i])
			
			keyTimes = makeUniqueArray keyTimes
		)
		return keyTimes
	)
	
	local allKeys = #()
	for o in selection do
	(
		local keys = collectAllKeyTimes o
		join allKeys keys
		allKeys = makeUniqueArray allKeys
	)
	sort allKeys
	print allKeys
)

cheers,
o

That seems to work! I’ll give it a closer look in a little bit when I get the chance. Thanks

It’s working really well for me, thanks for the help! Now I want to do something related but a little bit different, maybe you can point me in the right direction?

Basically, I’d like a function that will return an array of an object’s sub-animation keyframes, using the format of the sub-animation’s index followed by an array of the track’s keyframes (only for tracks that have any). Basically looking for an output like

#($objName[3][1][1], #(0,17)), #($objName[3][1][2], #(0,5)), #($objName[3][1][3], #(5,17)), #($objName[3][2][1], #(0,20)))

If that makes any sense.

do you want to make your own format to save and load animation?
do you known that the LoadSaveAnimation Core Interface at least tries to do the same? and!.. it does do it!.. and it does do it not bad.
the only thing that you need to do is the parse the data… look at an XAF file. it’s just an XML file.
read it, and get whatever you need.

Thanks, but actually, that’s not what I’m doing. I want to try and set up something to keep track of changes to sub-animations in real time, so that I can run callbacks when changes are made.

With the version of the script I am currently using, it will run callbacks when keys are added, removed, or dragged to another frame. But, that is not going to be enough. I need to it to be able to detect changes to the values of the sub-animations.

1 Reply
(@denist)
Joined: 11 months ago

Posts: 0

if you want to keep a controller of changes just lock it.
if you want to get a notification of any controller’s change use when construct.

I’m actually currently using a node event callback so that I can call my function with mouseUp. I was using a when construct with an earlier version of the script, and it ended up running hundreds of times while I moved objects from one place to another.

I’m also not sure if I can get even a when construct to do the things I need it to, like know which object in a group has been moved, transformed, enabled/disabled, etc., short of creating a separate handler for every single object. And then I’m trying to imagine what will happen when adjusting object A causes a transform in object B, etc.

I think I’m on the right track, just having a little trouble figuring out how to store the values the way I want them.

fn collectKeyFrames track parent: index: =
 (
 if index == unsupplied do index = track
 if parent != unsupplied do track = parent[index]
 
 local keyFrames = #()
 if isController track.controller do (keyFrames = for k in track.controller.keys collect k.time.frame as integer)
 
 if keyframes.count != 0 do format "%[%] (%) keyFrames: %
" parent index track keyFrames
 
 for i = 1 to track.numSubs do collectKeyFrames track[i] index:i parent:track
 )
 collectKeyFrames $

Edit: Worked on it a little more. Not sure if I’m getting closer to what I want or further away though.

fn collectKeyFrames track parent: index: parentID: =
(
if index == unsupplied do index = track.name
if parent != unsupplied do track = parent[index]
if parentId == unsupplied do parentID = ""

local keyFrames = #()
if isController track.controller do (keyFrames = for k in track.controller.keys collect k.time.frame as integer)
if keyframes.count != 0 do format "%[%]: %
" parentID index keyFrames

nextParent = parentID + (if classOf index == integer then "[" + index as string  + "]" else "$" + index)
for i = 1 to track.numSubs do collectKeyFrames track[i] index:i parent:track parentId:nextParent
)
collectKeyFrames $

Probably not the most efficient possible way to do this, but it’s working well enough for my needs. I’m saving the sub-animation indexes as strings to keep them from evaluating, not sure if there’s another way to do that. Obviously if anyone has any suggestions for making it better, that would be awesome.

fn getKeyVals track index: parent: parentID:"" =
(
if index == unsupplied do index = track.name
if parent != unsupplied do track = parent[index]
	 
local keyFrames = #()
if isController track.controller do (keyFrames = for k in track.controller.keys collect k.time.frame as integer)

if keyFrames.count != 0 do
(
keyVals = #(parentID + "[" + index as string + "]")
for f in keyFrames do at time f append keyVals #(f, track.value)
append keyValArray keyVals
)

local indexString
if classOf index == integer
then indexString = "[" + index as string + "]"
else indexString = "$" + index

for i = 1 to track.numSubs do getKeyVals track[i] index:i parent:track parentID:(parentID + indexString)
keyValArray
)