Introduction



The scripting interface in Moho (Anime Studio) is divided into three "modules":

LM is the lowest-level module, and includes very basic objects like vectors and colors.

LM.GUI is a user interface module that is built on top of Moho's cross-platform user interface library. Scripts can use the user interface widgets in this module to build dialog boxes, and to set up custom interfaces for toolbar buttons.

MOHO is the module that is a direct interface into Moho itself. The objects and functions in the MOHO module allow scripts to create and manipulate layers, vector artwork, bone systems, and more.

Getting started with scripting


Lua


Moho scripts are written in Lua. This describes the basics of Lua: Programming in Lua

If you're already familiar with block-structured languages then you shouldn't have any real problems with Lua.

A word about Global variables.
Rule 1 in the Moho context: if you MUST use a global, then prefix it with some unique to you identifier ... see Script Structure. E.g. Moho uses LM_; other examples are HS_ , syn_, SZ_, AE_ ... It's best to use local variables by default except where necessary -- such as all your script functions that Moho expects to find (e.g. function HS_Myscript:Name() ).

Most of the Moho data objects will be of Lua type "userdata". e.g. as shown below the code
local vec = LM.Vector2:new_local()
creates "vec" as an object of class LM_Vector2


Note that the current versions of Moho (12 and 13) use Lua 5.2 -- the current Lua version is 5.4 -- make sure you use the right : Lua language reference docs

Interfacing with Moho


You're already in the right place for understanding how to get Moho and your scripts talking to each other...

A good place to start is to take a quick look (don't try to learn them!!) at the "core" classes in Moho : AnimChannel, M_Bone, M_Curve, M_Mesh, M_Point, M_Shape, M_Skeleton, MohoDoc, MohoLayer, ScriptInterface and get a feel for what's in them and how they fit together.

The "mesh" family


Here's a summary of the mesh / point / shape / curve relationships

>> Mesh "holds" all the points. points are numbered in the mesh from 0 ... mesh:CountPoints()-1

>> Mesh also holds "curves" - which are ordered lists of points. Curves are numbered form 0 ... mesh:CountCurves()-1

>> A path (a curve) is made up of points. Points are ALSO numbered in a curve from 0 ... curve:CountPoints()-1 Note that these numbers are different -- e.g. Curve(1): Point(3) might be Mesh:Point(44)

>> A curve is made up of segments. They are numbered 0 ... curve:CountSegments() -1. Generally the start point of segment (x) is point in curve (x). If the curve is closed (i.e. a loop) segments = points; if it's not, there's one more point than segments.

>> A shape has edges; they are numbered 0 ... Shape:CountEdges()-1. An edge is a segment of a curve. One segment can be in many shapes.


Writing your script


This scripting documentation has a Script Structure page that sets out the fundamental elements for menu, tool and layer scripts. There's also template generator accessible from the "tools" button.

If you fancy trying something before starting on a "serious" script, you might like to set yourself a simple challenge - e.g. draw a circle - centre is mouse click, radius is where the "drag" ends. (You could use the LM_Shape tool as a crib sheet for that if you get stuck.) Then build on that to get the circle to glide across the screen ... etc

a code snippet


Most Moho data objects don't need to be explicitly created -- calling something that "returns" a class gives you the userdata object: (e.g.) an M_Point class is returned by M_Mesh:Point(n) so:
local x = 5                                  -- assign a value
local vec = LM.Vector2:new_local() -- LM_Vector2
local mesh = moho:Mesh() -- ScriptInterface:Mesh
local pt = mesh:Point(x) -- M_Mesh:Point
vec:Set(pt.fPos) -- M_Point

sets vec to be the position of point "5"


Creating objects


As above, when a script creates new objects in Moho, it will most often not create them directly, but call a function to do the job. For example, a script never creates a new layer object directly, but tells Moho to insert a new layer:
-- *** Ask Moho to create a new vector layer ***
local layer = moho:CreateNewLayer(MOHO.LT_VECTOR)


Typically the only time a script might need to directly create a new object is for very simple object types, such as points, vectors, and colors. For example, to create a new 2D vector object, a script might do the following:
-- *** Create a new local variable ***
local vec = LM.Vector2:new_local()


-- *** Create a new global variable ***
XX_globalVec = LM.Vector2:new()


Common Problems


Your script throws an error such as "attempt to call method 'XXXXX' (a nil value)"


This is often found where you have the correct method but not the correct class of object.
e.g. myLayer:Layer(0) requires myLayer to be of class GroupLayer and the error will be generated if myLayer has not been cast as such by using ScriptInterface:LayerAsGroup (or, of course, is not any type of layer object)

Generic classes used as the foundation for specific layer types are MohoLayer and AnimChannel

Your vector assignment looks right, but it doesn't work?!


You've defined local MyVec = LM.Vector2:new_local()
You know that mouseEvent.vec is where the mouse event took place.
But MyVec = mouseEvent.vec just doesn't work properly!?

That's because MyVec = mouseEvent.vec creates an instance of mouseEvent.vec -- and that means which means that MyVec will keep jumping to the mouse position.

To get the value, it's necessary to set each element separately:
MyVec.x = mouseEvent.vec.x
MyVec.y = mouseEvent.vec.y

or use the LM_Vector2:Set(vec2) method: MyVec:Set(mouseEvent.vec)


Similarly for other moho vectors (LM_Vector3, LM_ColorVector ...)



Moho crashes but your code looks right!


Check that you've correctly called a method. e.g. moho:CountPoints() and not moho.CountPoints()

function t:f(arg) has a implicit first arg of self so the definition is actually the same as function t.f(self, arg).
In other words moho:CountPoints() is just shorthand for moho.CountPoints(moho). Generally, scripters will use Class:Method() rather than Class.Method(Class)

Note that attributes (properties) are properly referenced by Class.Attribute