14. Scripting Script action Scripting is by far the most powerful way to animate your animation elements. In fact, scripting is so powerful you can (if you wanted) to replace any normal animation action except for the 'Sequence reference' and 'Current camera' ones. But in practice you should only use scripting in places normal actions are getting too clumsy or are to limited. It's properties are:
Edit window For editing the script associated with a script animation action there is an object window. So lets start by opening such a window to get familiar with our 'workbench'. First you add a script action to any sequence, just like the other actions. After that you open the window by double clicking the action object. This will open a window like the one below. Please note I used the window of a configured script action to have an example in place. The script in this example calculates which segments are visible on a led display given a number between 0 and 9. You'll find it as part of the "segDisplay" sample project. The window has four main areas, namely: 'The test environment control' (left top), 'Variable mapping' (right top), 'The actual script text editor' (blue area) and an 'Object inspector' (right bottom). Of course there is also a generic toolbar, so let's walk through its buttons first:
Variable mapping To mutate animation element values with scripts you first need to map these elements to local 'simple' script variables. This is done to avoid using the long element names. It does not matter if you add analog or binary elements they will be automatically mapped to the correct script variable type (double or boolean). You add a mapping by clicking button #1, this will open the familiar element selection dialog. Choose any element and click 'Ok'. A row will be added to the mapped variables grid. This grid has four columns, namely:
The scripting language Note: I will be assuming you have some sort of experience with scripting/programming, because teaching programming from scratch isn't in the scope of this manual. If you're not familiar with any scripting but still want to learn how to use it in LD4DStudio I suggest you take up a generic (pascal) beginners tutorial first. When done creating mappings it's time to do the actual scripting. For this you need to learn the used scripting language. Programmers among you will recognize pascal in the used language. The language is indeed highly based upon pascal but it nether offers all the possibilities of pascal nor is it exclusively pascal. Some base rules:
Operators This script language is mainly about calculating, therefore you will be using a lot of formula-based variable assignments. So lets start with the operators you can use:
In this table the first column states the execution order. Operators with a lower number will be applied first. Same level ones will be done from left to right. And of course you can use '(' and ')' to enforce groups. For example the assignment: begin This will be processed like: (Presume b=3 and c=4)
One more example: begin This will be processed like: (Presume b=3, c=4)
For working with the booleans (binary elements) there are the boolean operators. An overview:
Just like the numeric operators some order of execution is in place. For example: begin This will be processed like: (Presume c=false, d=true)
It's also possible to mix boolean and numeric, in this case numeric will go above boolean. For example: begin This will be processed like: (Presume b=false, c=true)
But do note this is not very human friendly formated. It's better to include some extra '(' and ')' signs to make it easier to read for ourself. Variables Besides the mapped variables you might also want to use some extra variables for holding 'in between' values. This can be done by declaring variables at script level. You may declare any number of variables at the start of the script (before the first begin) using the var keyword. It's best shown by example: var This example uses three internal variables ('a', 'b' and 'c'). One is of type double (analog) and two are of type boolean (binary). Variables mapA and mapB are mapped variables. Like the example shows, there's no real difference between the usage of the total of five variables. You just need to prevent declaring variables with names already used by a mapping. Arrays Both local and mapped variables can also be used as (multi dimensional) arrays where needed. For the local variables you do this by adding a 'array[n..m] of' infront of the type part of a declaration row. For example: var This will make 'a' an array with 5 elements. You can declare multiple variables of the same array type by adding more names in front of the type just like when declaring simple variables. You can also use multidimensional arrays, for example: var This will give you three variables whom all have 10 elements organized in a virtual grid of 2 rows of 5 elements. Accessing specific elements in an array are done by supling a number enclosed with '[' and ']' after the variable name, like: begin Like this example shows you can also use other variables as part of the array index specification. Do note array indexes are always whole numbers, and therefor the 'double' values of any used variable are truncated. So e.g. '3.2321' will be used as just '3'. Like said arrays can be used in both local script and the mappings. It can be very handy to access multiple mappings as elements in a single array variable. This is possible by adding additional information to the mapping name in the top right grid. For example when you have 'camera 1.lookat.x' and 'camera 1.lookat.y' mapped to 'mapA' and 'mapB' you can 'upgrade' those mappings to an array by renaming the mapnames to e.g. 'camMap[0]' and 'camMap[1]'. Internally this will create an 'camMap: array[0..1] of double' variable you can use just like your local array variables. If needed you can also widen the array to more elements then just the two mappings, this can be done by changing the name to e.g. 'camMap[0 {0..9}]' and 'camMap[1]'. This will result in an internal variable of 'camMap: array[0..9] of double'. You can even use multidimensional arrays with mappings by using e.g. 'camMap[0, 2]' or even 'camMap[0 {0..9}, 1 {0..4}]'. If you want to use a series of animation elements in an array from the start just add them at once. If all are of the same type the dialog will detect this and it will ask if you want to create an array mapping with them. If you agree the below dialog will open to help you initialize the array. The dialog only needs a name for the mapped variable in order to proceed. But if also lets you control the order of elements in the array and gives you full control over the array indexes to be used. Statements Statements are the 'commands' separated by the ; character. Up till now we only used the := (assignment) statement. But there are several more statements you can use, namely:
If then else statement Very useful in any scripting language is the ability to make decisions, this is done with the if then else construction. For example: begin This script calculates the value for b differently if the value of a is above 5. The part of 'a>5' can be any full blown formula as long the result is boolean. The else is not mandatory so if nothing has to be done at the else part just leave it out. But remember the statement after the then can't have the ';' character when else is present. If you want to do multiple statements in the 'then' or 'else' part you must use a block statement like the example below begin You can of course do the same for the else part if needed. Loop statements Sometimes you want to apply the effect of a piece of script multiple times to get a desired effect. For this there are loop statements. LD4DStudio's script language supports three different kind of loops, let's go over them one by one. First there is the 'for do' loop. It's the most simple and elegant loop variant available. With it you just 'say' do the following statement(block) this much times. The most handy thing of 'for do' loop is having a control variable which will contain the current loop index. Things are is best demonstrated with an example:
var This little script will calculate the Factorial of 10. you could of course have written a:=1*2*3*4*5*6*7*8*9*10; but this is more dynamic (you can change 10 at any time). This example has only one statement as the 'do' part but just like the 'if then' statement you can use 'begin' and 'end' to encapsulate multiple 'normal' statements. The second kind of loop available it the 'while do' loop. This loop executes it's 'do' statement as long the set condition is true. For example:
var This script will result in a being '2400'. The advantage over a for loop is you can use (multiple) dynamic conditions in control of the number of loops there will be done. The downside of the while loop is it can be 'dangerous'. Because you could create an condition whom never will be met resulting in an infinite loop. This will happen in the above example if you took away the 'i++' statement. But don't worry LD4DStudio has a protection mechanism for these kind of situations. You'll read about that in the 'Runtime errors' section below. The example uses a 'begin end' block but this can also be a single statement when needed, like for example:
var The third and last loop kind available is the 'repeat until' loop. This one is very much like the 'while do' kind, except it will always be ran at least once. This is because the 'exit' condition is set after the statements in the loop. An example:
var This script will calculate how many 'powers' it take to get 64 from 2. At the end of the script i therefore will be '6' (2^6=64). Just like the 'while do' loop the 'repeat until' loop can potentially be 'dangerous' in being infinite. But don't worry the same protection is in place. Also as a result of the construction of this loop you will not be needing 'begin' and 'end' if you want to encapsulate multiple statements. In many ways the 'repeat' and 'until' keywords function like a 'begin' and 'end' on their own account. As with the 'if then' statement you may nest loops because they are after all just statements on their own. You can also use full formula based constructions for ether their control variable range (e.g. 'for a:=b to c-1 do') or loop conditions (e.g. 'while (round(a)>10) and (b or c) do'). Internal functions For certain mathematical functions you can use the internal function library. This is a collection of useful tools you can call in any formula. All functions ether give a boolean or double result based upon one or more input parameters. Available functions are:
All parameters used in the function calls can be formulas them self, for example: begin Or nested, like: begin Script constants While calculating stuff with scripts it might be useful to know which is the current frame, or the frames per second value used. To this end there are a number of constants you can use in your formulas. Constants have a name just like variables, the only difference is they start with the @ character. Constants you can use are:
The test environment When your script is completed you might want to test the results it generates. To this end you can 'simulate' a current frame's environment by adjusting the values in the top left panel. The values are:
To get the current animation's environment press the 'load current' button. You can also supply test values for the used animation elements. This is done in the mapping grid. There you can edit the 'input' cell values. After you configure your test values, you can test the script results by clicking the #8 button to run your script. If successful the 'output' column will show the result values for all mapped variables. Don't forget to press the #6 button once in a while to save your script to the script action object. The animation engine will not use your latest script if you don't do this. The object inspector Until now I kinda ignored the little panel at the right of the script text area. It's there for multiple reasons. It shows you an overview of all currently available constants, functions and variables. You access the different lists by clicking the vertical tabs on the left of the panel. In any of the lists double clicking on an item will insert it's name to the script text at the current cursor position. This will help you prevent typing mistakes. The 'functions' list can also be used as a reminder for needed parameters and the result type of a particular function. And the 'variables' list does the same for e.g. array index ranges. This is especially handy for arrays resulting from mappings, their ranges can sometimes be tricky to deduce yourself. Error messages While learning to script you will be getting a fair amount of error messages from the script compiler. Below you'll find a list of all possible messages and their meaning. Parameter count mismatch You failed to supply the correct number of parameters in a function call. For example you might used a:=abs(12,2); abs has only one parameter. You probably meant abs(12.2) a common mistake by people using the comma for decimal separation in their country. Type mismatch You tried to supply a double where a boolean is expected or the other way around. For example you might have done: a:=sin(true); Unexpected end of script The parser is not expecting the end of the script. You probably forgot the end. or just its . Unexpected token "foo" You are trying to use some character or keyword on an unsupported location. For example you start typing "a:=b*10;" without having a begin first. Unknown variable "foo" The variable you are trying to use does not exist. You most likely made a typo like a:=leftLig instead of a:=leftLeg . Unknown constant "foo" The @someName constant you are referring to does not exists. You most likely made a typo like @frmeNr instead of @frameNr. Unknown function "foo" The function you are trying to call does not exist. You most likely made a typo like rund(a) instead of round(a). Variable "foo" redeclared You are trying to declare a variable in the var block with a name used by a variable mapping. Or you are declaring a variable for the second time in the var block. Array dimension interval must be from low to high you declared an array using e.g. 'array[5..2] of double' this is not allowed, the range of elements should always be incremental. Array is too big; N elements declared, M elements allowed. You declared a huge array which exceeds the internal maximum for total number of elements in an array. It's very unlikely you actually intended such a large array because I don't see when you will be needing an array using more then 262144 elements (the current limit). Variable "foo" is an array, index information expected. You are using an array variable without specifying the wanted element index, e.g. a:=10 instead of a[0]:=10. Variable "foo" is not an array. You are using a simple variable like an array, e.g. a[10]:=0 instead of a:=0. Array index count mismatch. You are supplying the incorrect amount of array indexes to an variable reference. If you define e.g. an [0..1, 0..7] array you also need to address it with two indexes ([n, m]). Runtime errors Above errors are compile errors, you might encounter them whenever you compile your script. But there are some errors whom only occur during the execution of a script. When these kind of errors are encountered the workspace will open (or refocus) the problem script's edit window. Possible runtime errors are: Runtime index out of bound detected for variable "foo" An array index exceeds the limits set by the associated variable declaration. For example variable 'a: array[0..9] of double' is being accessed like e.g. 'a[12]'. This is a runtime error because the used index can also be the result of an formula or other variable whom are unknown at compile time. This script execution takes longer then set maximum Because of the looping possibilities it could be possible the animation engine gets stuck in a infinite loop as a result of a programming mistake (e.g. a while loop with an condition whom never can be met). To prevent the whole application from hanging the execution of any script is bound to a maximum amount of time. By default this is 40ms which should be more then enough for most scripts. But because of this limitation a very complicated script might also get killed just because it's taking to long. If thats the case you could increase the set limit in the configuration dialog, but be aware with scripts running over 40 ms you can forget a decent frame rate while playing the animation. Fatal errors There are two more possible messages, namely:
But they 'should' never happen. If you do get one of them please post a bug report Conclusion This chapter is in no way complete. You could write a whole second manual on scripting alone, but I'm not going to do that. To learn using scripting wisely I suggest you study the sample projects for some helpful examples. After that the best thing to do is to 'just' start scripting. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Best viewed with Firefox 2, Seamonkey or IE 7 at 1024x768 or higher resolution. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Copyright Roland Melkert 2008-2017, all rights reserved. LEGO is a registered trademark of the LEGO Group, which does not sponsor, endorse, or authorize this website. |