Getting Started
Lesson 3 - Good Housekeeping
In this lesson we're going to look at some things that you can do to make your scripts more readable, more maintainable, and just generally better behaved. We're not adding a lot of new functionality here. This stuff doesn't add a lot of pizzazz but it will make you a better scripter.
Let's take a look at a modified version of the final script from the pervious lesson:
{
// Create script undo group
app.beginUndoGroup("Create Square");
// create project if necessary
var proj = app.project;
if(!proj) proj = app.newProject();
// create new comp named 'my comp'
var compW = 160; // comp width
var compH = 120; // comp height
var compL = 15; // comp length (seconds)
var compRate = 24; // comp frame rate
var compBG = [48/255,63/255,84/255] // comp background color
var myItemCollection = app.project.items;
var myComp = myItemCollection.addComp('my comp',compW,compH,1,compL,compRate);
myComp.bgColor = compBG;
// create new solid named "my square"
var mySolid = myComp.layers.addSolid([1.0,1.0,0], "my square", 50, 50, 1);
// create square mask
var newMask = mySolid.Masks.addProperty("Mask");
newMask.inverted = true;
var myMaskShape = newMask.property("maskShape");
var myShape = myMaskShape.value;
myShape.vertices = [[5,5],[5,45],[45,45],[45,5]];
myShape.closed = true;
myMaskShape.setValue(myShape);
// set postition keyframes
var myPosition = mySolid.property("position");
myPosition.setValueAtTime(0,[80,30]);
myPosition.setValueAtTime(1,[130,60]);
myPosition.setValueAtTime(2,[80,90]);
myPosition.setValueAtTime(3,[30,60]);
myPosition.setValueAtTime(4,[80,30]);
// set rotation keyframes
var myRotation = mySolid.property("rotation");
myRotation.setValueAtTime(0,0);
myRotation.setValueAtTime(4,720);
// set scale keyframes
var myScale = mySolid.property("scale");
myScale.setValueAtTime(0,[100,100]);
myScale.setValueAtTime(1,[50,50]);
myScale.setValueAtTime(2,[100,100]);
myScale.setValueAtTime(3,[50,50]);
myScale.setValueAtTime(4,[100,100]);
// set opacity keyframes
var myOpacity = mySolid.property("opacity");
myOpacity.setValueAtTime(0,100);
myOpacity.setValueAtTime(1,50);
myOpacity.setValueAtTime(2,100);
myOpacity.setValueAtTime(3,50);
myOpacity.setValueAtTime(4,100);
app.endUndoGroup();
}
As before, the new stuff is shown in white.
Limiting the Scope of Variables
The first good housekeeping tip we're going to look at is limiting the "scope" of our script. Unless you instruct After Effects to do otherwise, everything you define in the main body of your script will persist, even after the script exits. Sometimes that can be a good thing - if you're planning for it and expecting it. You can use this feature to communicate between scripts. But most of the time you probably don't want to leave your variable definitions hanging around just waiting to cause baffling behavior in a new script where you forgot to define a variable before you referenced it. Anyway, it's a good idea to have your scripts clean up after themselves. It's very simple to do. All you do is add a set of opening and closing curly braces ( "{" and "}" )around your entire script. That will limit the "scope" of your variables to the body of your script and they will be deleted when the script exits.
Creating an "undo group"
The next thing we're going to do is create an "undo group" for our script. This will allow us to undo the effect of our script after it runs (in the unlikely event that it didn't do what we expected or we just changed our mind). It's very easy to do. You just enclose the body of your script between these two lines of code:
app.beginUndoGroup("name");
app.endUndoGroup();
where "name" is what you want to appear in the Edit menu in the Undo selection. In our script above, for example, after the script runs "Undo Create Square" will appear in the After Effects Edit menu.
Creating a Project if Necessary
Next, we're going to start our script with a test to see if a project exists, and if not, we'll create one. Here's the code:
var proj = app.project;
if(!proj) proj = app.newProject();
The first line creates a variable named "proj" and sets it equal to the app.project object (where "app" represents the application, After Effects).
The second line is a little JavaScript trick where we use the "if" conditional to see if our new variable is "undefined". If no project exists, our "proj" variable will have the value of "undefined" which will cause the conditional part of the statement to execute, which uses the "newProject" method (of the "app" object) to create a new project and assign its value to our variable "proj". So, either way, by the end of the second line a project will exist and our "proj" variable will reference it.
Declaring Variables
You'll notice that we preceeded the definition of our "proj" variable with the "var" keyword. This is JavaScript's way of denoting the creation of a new variable. You can do this at the same time as the first use of the variable (as in our example above) our you can do it before the first use of the variable like this:
var proj;
proj = app.project;
if(!proj) proj = app.newProject();
This declaration of variables is not required and you will usually get away with it if you don't do it. But it's good programming style to declare your variables and there are certain situations where it will keep you out of trouble. For example, when we cover JavaScript functions, you'll see that if you don't declare local variables inside your function and happen to have variables of the same name outside the function, your function will use the "global" version and not create its own. Sometimes you might do that on purpose but the potential is there to unwittingly change a variable in the main body of your script with unpredictable results. In expressions, you rarely see "var" used to declare variables because generally expressions don't have functions and don't have a lot of code so you can get away with it. In scripting you can easily end up with a large amount of code and numerous functions. So, from here on we'll be declaring our variables.
If you look at the new version of the script above, you see that I've added the "var" declaration at the first usage of each of our variables.
Note that you define each variable only once.
Indentation
The next topic of good housekeeping is indentation. We use indentation to denote the hierarchy of blocks of code. It makes it so much easier to see the scope of an if/else conditional, loop, or function. You'll see what I mean as we get into more complex scripting examples. In this script, the only block of code is the body of the script, which is now contained between the opening and closing braces. So you'll notice that everything between those braces has been indented two spaces to denote the block of code contained within the braces. I like to use two spaces for each level of nested hierarchy, but it's matter of personal choice. Some people use tabs, which gives a more dramatic spacing, but I find that when you get nested down several levels, the nested code goes way out to the right of the page and may word-wrap which actually decreases readability. I've also found that tabs can be problematic when moving your scripts from one text editor to another or from one platform to another. So I use the two spaces.
Comments
Another addition to the script that enhances readability is comments. JavaScript considers anything between a double slash "//" and the end of the line to be a comment and it doesn't affect the execution of the script. Note that these comments can be on lines by themselves or on the same line (but after) other JavaScript statements. I've included both types in the code above. Comments are really helpful when you (or somebody else) needs to figure our how your script works. You can, in my opinion, go overboard with comments and end up cluttering the program. I like to add comments explaining the purpose of variables and to describe the function of major blocks of code. I also like to start whole-line comments at the same indentation level as the block of code they're describing. You can also use the "/*" and "*/" pair to enclose multi-line comments, but you don't see that as much. I might use that format if had a whole paragraph of explanation that I needed to add, but normally I stick with "//".
Creating a Comp
The last thing I want to go over here is the other section of new code added to the above script:
// create new comp named 'my comp'
var compW = 160; // comp width
var compH = 120; // comp height
var compL = 15; // comp length (seconds)
var compRate = 24; // comp frame rate
var compBG = [48/255,63/255,84/255] // comp background color
var myItemCollection = app.project.items;
var myComp = myItemCollection.addComp('my comp',compW,compH,1,compL,compRate);
myComp.bgColor = compBG;
This is how you create a comp with a script. By adding this code we don't need to manually create a new comp before we run the script. Sometimes you'll want to use an existing comp, but often you'll just want to create a new one with your script. The first four "var" declarations are just defining parameters that will be passed to the "addComp" method, which actually creates the new comp. We could just have easily have plugged the numbers directly into the "addComp" statement, but this way makes it easier to change (or reuse) later because the parameters have descriptive names and comments.
The next "var" declaration:
var compBG = [48/255,63/255,84/255] // comp background color
is just defining the background color of the comp. Remember that colors in scripting are in the [red,green,blue] format where red, green, and blue are values between zero and one. Defining the colors, as we have above, as a ratio of each color component divided by 255 allows us to specify the color channels in the more familiar 0 to 255 range.
In the next line of code:
var myItemCollection = app.project.items;
we're declaring a new variable named "myItemCollection" that references the project's "collection" of items, which can include footage, folders and comps (these are the items that you will see in a Project Window.)
The comp actually gets created in the next line:
var myComp = myItemCollection.addComp('my comp',compW,compH,1,compL,compRate);
by using the "addComp" method applied to our item collection object. "addComp" takes six parameters, in the following order: comp name; comp width; comp height; pixel aspect ratio; comp duration; comp frame rate. In our usage above, we have used variables to specify everything except the comp name ("my comp")and the pixel aspect ratio (1 or square) which we specified directly in the call. Notice that we have also created a new variable, "myComp" which references our newly created comp, which we need in the next line of code to set the comp's background color:
myComp.bgColor = compBG;
Here were simply using the "bgColor" method of the comp object to set the background color to the value contained in the variable "compBG", which we defined a few lines earlier in the code.
Summary
Well, there you have it. A mixed bag of ideas that will (in my opinion) help make you a better script writer. You'll see more examples of these tools and techniques in the other scripts that you find on this site.