Scripting Frame Question

Moho allows users to write new tools and plugins. Discuss scripting ideas and problems here.

Moderators: Víctor Paredes, Belgarath, slowtiger

Post Reply
dkwroot
Posts: 680
Joined: Thu May 02, 2013 6:56 am
Location: USA
Contact:

Scripting Frame Question

Post by dkwroot »

I'm trying to write an embedded script that will print to the console the frame number that a user is on as they scrub through the frame.

Code: Select all

function Layerscript(moho)
	curframe = moho.layer:CurFrame()
	print(curframe)
end
Layerscript(moho)
I tried this, but got a traceback error. I'm not sure how to reference the current frame number. Is it possible to store a frame number in a variable that can be referenced later?

For example:
Could I make it so that the frame number that is scrubbed will only ascend upward and never downward?

I'm thinking of a program like:

local Frame_Stored = CurrentFrame# <--- Need to know how to reference this
frames_stored = { } -- Create a table
table.insert(frames_stored,Frame_Stored)

Then, just create an if statement to check if the frames_stored[1] is greater than the current frame number.
User avatar
heyvern
Posts: 7042
Joined: Fri Sep 02, 2005 4:49 am

Re: Scripting Frame Question

Post by heyvern »

Oh boy!

Okay let's see if I can take a stab at this. I won't be able to cover everything you will need. It took me a while to get the hang of scripting and the BEST way to learn is to look at other scripts. There is no way to create a comprehensive tutorial in just this one posting but below is a quick overview of some important elements.

Code: Select all

function LayerScript(moho)
	-- layer script code
end
This is how basic layer scripts start out. Everything happens inside that function and is run on each frame of the layer. You don't need to call the LayerScript() function as you did in your script sample (take note of the spelling and text case of this function. It has to be exactly as shown). Any additional functions you want to create can be created and called inside the LayerScript function. You can create functions outside of the LayerScript function, but remember you have to pass any any variables that the external function needs. It's just easier to keep the functions inside the layerscript function so you always have access to the variables and objects.

"moho" is the script interface "object" and allows scripts to interact with the document.
The function LayerScript(moho) has access to the moho object. Anything happening inside that function has access to "moho" and all of the script interface magic that goes along with it. If you try to call say, a custom function OUTSIDE of the LayerScript function that doesn't have access to the moho object directly, it will give an error. You can of course pass the moho object to a function as an argument.

http://animestudioscripting.com/moho/mo ... tinterface

Layer scripts can run multiple times on each frame. When you click or interact with the interface it will activate the script of that layer. This is so that if you have a layer script that changes things like bones or points, you want it to update if that bone is changed by the user or some other AS action like physics or constraints. There are some tricks you can do to force a script to only run once but in most cases that shouldn't be needed.

Layer scripts do not have to run on every frame. Sometimes you want to do "set up" of variables on frame zero. Frame 0 in Anime Studio is "special" and is a good place to set things up to do things on later frames. One good use of this is to set up complex tables of data that contain information like a list of bones or points etc. You don't want or need the script to do this on every frame, and you may also want the default value of the object.

You check the current frame and only run on frame 0, then other actions run on frames greater than 0.

Code: Select all

if (moho.frame == 0) then
	-- do something
end
if (moho.frame > 0) then
	-- do something else
end
For your specific script idea, the code you used to access the "current frame" is incorrect. You can set the curFrame to change where you are on the time line. You can update the curFrame to show any changes that a script has made. To actually get the current time or frame number, you will use a different script interface variable.

There are two types of "current time" or frame number and these can be accessed using the following:

This is the "top level" main time line current frame. It will return the frame number you see in the timeline.

Code: Select all

moho.frame
This is the LAYER's current frame.

Code: Select all

moho.layerFrame
This was added a while back for the Sequencer feature. A layer can be "shifted". This means that the layer's frame number would be different from the document's frame number. In working with Anime Studio this generally isn't important, but with scripting you may need things to happen on a specific frame. For example moho.frame == 0 and moho.frame > 0. If the layer is "shifted" this could cause problems because the frame is different from the layerFrame. It it's critical than you should use the moho.layerFrame instead.

What I learned very early on by studying other scripts is to create a simple variable for the current frame. Instead of typing out moho.frame every time I use:

Code: Select all

local frame = moho.frame
This makes it much easier to get the frame number. REMEMBER all of these references are INSIDE the LayerScript(moho) function as described at the top of my post.

Okay, so you want to display a frame marker on screen. If you do this using the lua console that pops up:
print(moho.frame)

This will work but will just print a contínuos list of the frame numbers and not be very helpful. The trick you need is to be able to print something on a layer or to the screen.

There are a few ways I can see this happening but would require some more study of the script interface and understanding of layer types. I will outline a few ways I would see doing this and you can maybe use that as a goal to target for study.

Text Layer (recent feature)
Unfortunately I don't know a ton about this new layer type and have not done much research in to how to script it. My thought would to use a layer script to update the text content of a text layer as a frame counter. This solution could render.

Vector layer
I created a simple scripted stroke font and use it in several of my scripts. I was thinking that because it is light weight and can be drawn quickly, it would be possible to "delete" and "redraw" the numbers on a single vector layer.

Switch layers
Okay this is tricky but might be the easiest scripting. I've done a lot of scripting with switch layers so changing the layer is easy. The hard part would be creating the counter mechanism to incrementally change the switch layers. You would use copies of a switch layer that would contain layers from 0-9, they could be images or vector whatever you choose. The layer script could then set these switches to indicate frame numbers. This would work quite well I think. This solution could render.

Interface Only Drawn Counters
This one would be tricky. It would use the gui drawing on the screen. The same way outlines for scaling points, magnet tool circles etc are drawn on screen. I have done this already and have a custom font that is drawn by the interface directly to the screen. Unfortunately this solution would not render in the output of the animation. It would only be a display on screen. From a performance point of view this would be the fastest. It wouldn't be using vectors or layers at all and would only be drawing in the interface of the application. (Actually not even sure a layer script can do this. I think it might not work as a layer script. I did this with a tool script which has an expanded set of variables and classes available to it.)
dkwroot
Posts: 680
Joined: Thu May 02, 2013 6:56 am
Location: USA
Contact:

Re: Scripting Frame Question

Post by dkwroot »

Awesome! Thank you for the quick reply, I've been reading through everything like crazy. I redid my code and it seems to work.

Code: Select all

function LayerScript(moho)
	local frame = moho.frame
	if (frame_table == nil) then
		frame_table = {}
	end
	
	if (frame_table[frame] == nil) then
		frame_table[frame] = frame --Layer 2 would be frame_table[2] = 2
	
		if (frame >= frame_table[#frame_table]) then
			print(frame)
		end	
	end
end

I've been going through the website you mentioned, but I'm just a tad confused about how the information is presented. The class names given by the site don't exactly match up with the command that is supposed to be executed.

For example, the class M_Bones isn't assigned by: something = moho.M_Skeleton but instead is something = moho:Skeleton()
How do you determine the actual execution call from the information given there? Also, some of the functions listed have int or void or float in front of the function listed. Is this the return type or something?

I've got tons of questions, so thanks for helping me. I'm determined to figure this program out.
User avatar
heyvern
Posts: 7042
Joined: Fri Sep 02, 2005 4:49 am

Re: Scripting Frame Question

Post by heyvern »

dkwroot wrote: I've been going through the website you mentioned, but I'm just a tad confused about how the information is presented. The class names given by the site don't exactly match up with the command that is supposed to be executed.
The web site is based on the original documentation for Anime Studio provided by the developers. However that original documentation was very old and hadn't been updated for many years.

Recently I created this new site with Smith Micro in the hopes of updating and expanding the documentation. This new site is intended to fix these problems in the documentation that you describe.

Some new features have been missing from the scripting documentation, but this new site is on it's way to fixing that. Although not described in detail, all current scripting features are documented on the site. I will also like to include code examples to show the proper use of these classes and functions. You are right the documentation can be confusing due to the fact that the entries are based on the "raw" C++ code that is compiled in Anime Studio to add the scripting functionality and not written in the way it's suppose to be used. There should be "real world" samples like other scripting documentation sites.

In the meantime, while I continue to update this site, the key to learning scripting would be studying other scripts. All of the Anime Studio's tools in the tool palette are scripts. Every tool is written as plain text lua files. They can be opened and studied. There are also many layer and custom tool scripts available for download on this forum to study for proper usage.

The good news about this is that lua is a very easy to learn programming language. If you have a background in other languages like JS or PHP than picking it up will be easier.

------

Yes whenever you see "void" or "int" or "bool" in front of something that indicates the return value type. In many cases the docuementation will show something as "LM_Bone" when the proper usage for that would simply be Bone()

For example, to loop through all the bones to a specific bone (by internal ID number) the common usage would be:

local skel = moho:Skeleton() -- just like moho.frame create a variable to save on typing that represents the skeleton of the bone layer
bone = skel:Bone(X) -- X would be an integer representing the bone ID number. The variable bone represents that bone now.

Usually this code would be used in a loop function to either find an exact bone or bones that meets a requirement or to change all bones in someway.

Code: Select all

	local skel = moho:Skeleton() -- another variable for simpler typing
	for i = 0, skel:CountBones() - 1 do
		local bone = skel:Bone(i) -- local variable within loop for a single bone reference
	end
Many of the code conventions you will see in scripts like "bone" or "skel" they are often variables used for readability. Using the full typed out "path" to an object like a bone would be quite cumbersome. :)

typing the variable:

Code: Select all

bone
is easier than typeing:

Code: Select all

moho:Skeleton():Bone(i)
Post Reply