Lua basics
Moderators: Víctor Paredes, Belgarath, slowtiger
- synthsin75
- Posts: 10280
- Joined: Mon Jan 14, 2008 11:20 pm
- Location: Oklahoma
- Contact:
http://www.mediafire.com/?mmt91metn4j
Fixed the cheat. Now it actually finds the ".dup" in the name.
Yeah this is fun stuff, but I'm nowhere near making anything new.
Fixed the cheat. Now it actually finds the ".dup" in the name.
Yeah this is fun stuff, but I'm nowhere near making anything new.
- synthsin75
- Posts: 10280
- Joined: Mon Jan 14, 2008 11:20 pm
- Location: Oklahoma
- Contact:
Mmm, now I'm getting a 'nil' error, but only at startup.
Ohhh, damn! To get the actual ".dup" out of the layer name my regex for it runs into trouble hitting the plain "name.dup" since there is only the one segment that begins with ".".
Can you put some sort of 'or' in the regex, or is my original cheat the best solution?
Ohhh, damn! To get the actual ".dup" out of the layer name my regex for it runs into trouble hitting the plain "name.dup" since there is only the one segment that begins with ".".
Can you put some sort of 'or' in the regex, or is my original cheat the best solution?
Yes, you can just do another "if then". I also eliminated an unneeded variable (zig). Bob could just be used instead.
So here in this revision we assign "bob" to be something that would always be true. Since "zag" has the potential of being "nil" we just say, if it isn't nil then concatenate the two variables. If "zag" is nil "bob" has already been assigned the correct string.
p.s. Just so you know for future reference... it's a good idea to use descriptive names for variables. I don't represent best practices in programming. Usually I try to change "bob" or "steve" to more descriptive variables when I'm finished "experimenting". It's a bad habit I started years and years ago while learning hypercard. 
In this case you could probably do the whole thing and just use the "srcName" variable for everything (except "zag" of course).
-vern
So here in this revision we assign "bob" to be something that would always be true. Since "zag" has the potential of being "nil" we just say, if it isn't nil then concatenate the two variables. If "zag" is nil "bob" has already been assigned the correct string.
Code: Select all
else
local __,__,bob = string.find(name, "(.-)%.+")
local __,__,zag = string.find(name, "%..+(%..+)")
if (zag ~= nil) then
bob = bob..zag
end
--print(bob)
local srcName= string.sub(bob,1,-5)

In this case you could probably do the whole thing and just use the "srcName" variable for everything (except "zag" of course).
-vern
- synthsin75
- Posts: 10280
- Joined: Mon Jan 14, 2008 11:20 pm
- Location: Oklahoma
- Contact:
OOOPS!
Made a mistake!
Called a NEW LOCAL bob. See the other "bob" is from the "city". This bob is a local yocal bob in the country and doesn't get out much.
Seriously, what happened is that the script works but the "print" was refering to a different bob. Bob had a twin brother also named bob.
This is called variable "scope" and it's not about bad breath.
The FIRST bob was declared outside of an "If / then" structure. The second bob was created by calling "local bob =". That is a NEW DIFFERENT bob.
I removed the "local" and now that second bob reference is using the FIRST bob declared before. This is the bane of programmers existence. Variable scope. 9 times out of ten it's what gets me when I have bugs.
-vern
Made a mistake!
Called a NEW LOCAL bob. See the other "bob" is from the "city". This bob is a local yocal bob in the country and doesn't get out much.

Code: Select all
else
__,__,bob = string.find(name, "(.-)%.+")
local __,__,zag = string.find(name, "%..+(%..+)")
if (zag ~= nil) then
bob = bob..zag
end
--print(bob)
local srcName= string.sub(bob,1,-5)
Seriously, what happened is that the script works but the "print" was refering to a different bob. Bob had a twin brother also named bob.
This is called variable "scope" and it's not about bad breath.

The FIRST bob was declared outside of an "If / then" structure. The second bob was created by calling "local bob =". That is a NEW DIFFERENT bob.
I removed the "local" and now that second bob reference is using the FIRST bob declared before. This is the bane of programmers existence. Variable scope. 9 times out of ten it's what gets me when I have bugs.
-vern
- synthsin75
- Posts: 10280
- Joined: Mon Jan 14, 2008 11:20 pm
- Location: Oklahoma
- Contact:
Removing the "local" didn't print anything for the regular 'name.dup' named layer. I see what you're saying, but the script still worked fine even with the new 'local'.
I just couldn't understand where the script was getting the name of the 'name.dup' layer. I didn't have time this morning, but I just went printing stuff to hunt this down.
I think it gets this form of the layer name through "local name". But I'm not sure where this occurs exactly.
On another matter:
I'm guessing that the 'local a, b' here is just declared, without assignment, so that the 'local' variable is set before being used in the "if/then" statement.
Is it just best practice to avoid using global variables in an AS script, or is there some actual reason for doing that?
I just couldn't understand where the script was getting the name of the 'name.dup' layer. I didn't have time this morning, but I just went printing stuff to hunt this down.
I think it gets this form of the layer name through "local name". But I'm not sure where this occurs exactly.
On another matter:
Code: Select all
local a, b
if(string.find(name, ".", 1, true) ~= nil) then
a, b = string.find(name, ".", 1, true)
Is it just best practice to avoid using global variables in an AS script, or is there some actual reason for doing that?
You have to understand what is called variable scope. Where a variable exists and can be called and used.
In that example a and be are local variables declared OUTSIDE of the if statement. So when you give them a value INSIDE the if statement they already exist.
If you used "local a,b = string.find..." then it would create NEW local variables a and b INSIDE the if statement. Those a and be variables are COMPLETELY DIFFERENT and can't be accessed out side of the if/then statement.
That is the mistake I made in my correction to your code. It still worked but I declared a NEW local "bob" variable INSIDE an if/then statement. It didn't exist outside of it but because there was ANOTHER bob variable that DID exist it was used instead.
If you need a variable to exist outside of a function or code statement it must be declared without using "local" or at least if it is local it should be at the top of the function so it can be accessed.
Using local variables frees up memory. If you define all your variables as global they hang around all the time. If they are only needed for a short time use a local variable. if you need them around to be used somewhere else make them global or you can also pass local variables off to gloable variables.
It's hard to get your brain around at first. You have to realize that declaring "local" for a variable means it only exists within the body of the current statement or function.

-vern
Code: Select all
local a, b
if(string.find(name, ".", 1, true) ~= nil) then
a, b = string.find(name, ".", 1, true)
end
If you used "local a,b = string.find..." then it would create NEW local variables a and b INSIDE the if statement. Those a and be variables are COMPLETELY DIFFERENT and can't be accessed out side of the if/then statement.
That is the mistake I made in my correction to your code. It still worked but I declared a NEW local "bob" variable INSIDE an if/then statement. It didn't exist outside of it but because there was ANOTHER bob variable that DID exist it was used instead.
If you need a variable to exist outside of a function or code statement it must be declared without using "local" or at least if it is local it should be at the top of the function so it can be accessed.
Using local variables frees up memory. If you define all your variables as global they hang around all the time. If they are only needed for a short time use a local variable. if you need them around to be used somewhere else make them global or you can also pass local variables off to gloable variables.
It's hard to get your brain around at first. You have to realize that declaring "local" for a variable means it only exists within the body of the current statement or function.
Code: Select all
local a = 5 -- outside the if then statement
local b
if (a == 5) then
local a = 10 -- a new local variable "a" inside the statement
print(a) -- prints 10
b = a
end
print(a) -- prints 5. This is the FIRST "a" declared.
-- The second a variable no longer exists
print(b) -- prints 10 because b = a. No, not that "a", the other one.

-vern
The variable scope thing can get very complicated. This can lead to issues when testing scripts with AS because a global variable could be declared and still live in memory even when the script fails or you've changed it and reopened or created a new document.
I've been caught by that before. If you really really really want to test a script you should quit AS and relaunch to be certain there is nothing in the memory. Lua is very good at "garbage collection" but you should still eliminate unneeded variables from memory if they aren't needed.
Meshinstance doesn't have any global variables that live after the script runs. The whole process is redone on each frame (shows how fast it is anyway). So there shouldn't be any problems with this. Just a heads up though. If you declare a global variable:
And don't specifically remove it:
It hangs around. If you set a global to "nil" the lua garbage collection removes it from memory.
-vern
I've been caught by that before. If you really really really want to test a script you should quit AS and relaunch to be certain there is nothing in the memory. Lua is very good at "garbage collection" but you should still eliminate unneeded variables from memory if they aren't needed.
Meshinstance doesn't have any global variables that live after the script runs. The whole process is redone on each frame (shows how fast it is anyway). So there shouldn't be any problems with this. Just a heads up though. If you declare a global variable:
Code: Select all
myGlobalVar = 6
Code: Select all
myGlobalVar = nil
-vern
- synthsin75
- Posts: 10280
- Joined: Mon Jan 14, 2008 11:20 pm
- Location: Oklahoma
- Contact:
Yeah, I'm already familiar with the "=nil" garbage collection.
So unless you clear them, AS could be holding in to any global variables? I so hate having to relaunch just to test. I was thinking of requesting that the ctrl-F5 also reload the layer scripts, but that would only help if all of your variables are local. Too bad there isn't an easy way to clear the memory.
So since 'b' in 'b=a' isn't declared as local, it uses the previously declared 'b' variable. You can assign it within the if/then statement, and as long as you don't make it 'local' to that if/then it is assumed to be the next highest (local to the same environment as the if/then) of that variable?
So you can't access the higher variable after assigning it local to the current statement?
Each variable (not declared local) is a reference of the previous local variable? But like AS layers, a child statement can only refer to its parent environment (whether a statement or not).
Something like that?
So unless you clear them, AS could be holding in to any global variables? I so hate having to relaunch just to test. I was thinking of requesting that the ctrl-F5 also reload the layer scripts, but that would only help if all of your variables are local. Too bad there isn't an easy way to clear the memory.
Code: Select all
local a = 5 -- outside the if then statement
local b
if (a == 5) then
local a = 10 -- a new local variable "a" inside the statement
print(a) -- prints 10
b = a
end
print(a) -- prints 5. This is the FIRST "a" declared.
-- The second a variable no longer exists
print(b) -- prints 10 because b = a. No, not that "a", the other one.
So you can't access the higher variable after assigning it local to the current statement?
Each variable (not declared local) is a reference of the previous local variable? But like AS layers, a child statement can only refer to its parent environment (whether a statement or not).
Code: Select all
-Environment
-local a
-if/then
-a (refers to ^)
-local a (new)
-a (refers to if/then local a)
-if/then
-a (refers to parent if/then local a)
-if/then
-a (refers to environment's local a)
Absolutely. This gets me a lot. I often THINK something is working only to discover the crazy global variable still has data from an early version of the script. When I restart AS that global is gone and now my code isn't working properly. You really need to restart AS to clear that memory if you work with important global variables that need to hang around awhile.So unless you clear them, AS could be holding in to any global variables?
IMPORTANT!
Most scripts start off as a function declaration similar to this:
Code: Select all
function LayerScript(moho)
... lots of code here
local bob = 6
... llots of code here
end
Like that yes. It's best to create new "unique" variables to avoid the confusion. You can really get confused quickly if you use the same local variable names within nested loops or structures. Hard to keep track. 6 weeks down the road if you haven't commented the code you have no idea which variable is doing what.Something like that?
One area where this happens is the "for" loop. It is used ALL THE TIME for going through the bone list (or layer list, or point list etc). The for loop can be used to check each bone or item one at a time and compare it to some value or another bone ID etc.
Code: Select all
local skel = moho:Skeleton() -- a variable we use to save on typing
local boneCount = skel:CountBones() -- saving on typing is fun!
for 1=0, boneCount-1 do -- do something to all the bones one at a time
.... do stuff here
end
Code: Select all
local skel = moho:Skeleton()
local boneCount = skel:CountBones()
for 1=0, boneCount - 1 do -- check all the bones one at a time
for j = 0, boneCount - 1 do -- compare 1 bone (i) to all the other bones
if(something is true here) then
.... do something
end
end
end
If there are 10 bones and we count from and include 0 (bone ids start at 0) then we count from 0 to 9, one less than the total number of bones to get 10. If we do 1 to 10 we miss a bone, bone ID 0.
The second internal loop again uses the "for" structure. We can't use "i" here because it refers to the "i" in the first part. We need another different local variable or else it won't work. So in this case we use "j" or we could have used "n" or "x". Whatever.
Keep in mind, once that loop reaches the "end" and moves out of the "for" loop, i and j no longer exist. If you need that info you would have to put it in a variable higher up in the scope.
Code: Select all
local theBoneID -- a variable to store a result from the loop below
local skel = moho:Skeleton()
local boneCount = skel:CountBones()
for 1=0, boneCount - 1 do -- check all the bones one at a time
for j = 0, boneCount - 1 do -- compare 1 bone to all the other bones
if(something is true here) then
theBoneID = i
end
end
end
Phew! It seems so simple and straight forward to me now but I can still remember trying to wrap my brain around this. I still get confused every so often. Especially with really "obtuse" nested if and for loops.
-vern
- synthsin75
- Posts: 10280
- Joined: Mon Jan 14, 2008 11:20 pm
- Location: Oklahoma
- Contact:
Thank you sooo much for elaborating on these details, Vern.
Just to make sure I'm clear, in your example:
Here 'j' is confined to it's own for statement. Since it isn't declared elsewhere it is an implied local declaration to that statement? So it wouldn't be accessible anywhere else but a further internal statement?
The 'theBoneID = i' provides the result to the highest local environment of the script? Since there are no globals declared, I assume the highest local variables act as the script's globals, since the rest of the script is internal to them.
The only reason to declare a 'local j' outside of the statement is if you need to access it elsewhere?
This is good stuff, thanks.
Just to make sure I'm clear, in your example:
Code: Select all
local theBoneID -- a variable to store a result from the loop below
local skel = moho:Skeleton()
local boneCount = skel:CountBones()
for 1=0, boneCount - 1 do -- check all the bones one at a time
for j = 0, boneCount - 1 do -- compare 1 bone to all the other bones
if(something is true here) then
theBoneID = i
end
end
end
The 'theBoneID = i' provides the result to the highest local environment of the script? Since there are no globals declared, I assume the highest local variables act as the script's globals, since the rest of the script is internal to them.
The only reason to declare a 'local j' outside of the statement is if you need to access it elsewhere?
This is good stuff, thanks.
Yes. If you declared "j" as a variable outside of that loop, it would still live after the loop. HOWEVER, "j" would ALWAYS be the last value in the loop unless you break out of the loop when a match is found. The for loop doesn't need to have "j" declared as local. It is assumed to be local. If you declare it as a local BEFORE the loop then it's scope is changed.The only reason to declare a 'local j' outside of the statement is if you need to access it elsewhere?
Even if the for loop finds a match or meets some criteria set in the loop body, the loop will always run to the end and j would contain the value of boneCount in this case.
You can "break" out of the loop by using "break" at the end:
Code: Select all
local j
for j = 0, boneCount - 1 do
if (j = 6) then
break
end
print j --> 6
-----
A global would be used if it needs to live AFTER the script is done. If it needs to be used by another script. Once the script runs and stops... all local variables are gone. A global variable could be declared on frame 0 and is only a READ value on other frames.
In the bone tools a variable is declared for the selected bone. The variable could be used by other scripts so there isn't a need to run through the list of bones to determine if one bone is selected.
---------
Sometimes two menu scripts can work together. The original ThaNarie's copy/paste bones script worked this way. One script copied all the bone information while on one layer. stored it in a gloabal variable.
Then you select another layer, choose the paste bones script and NEW bones are created reading in the values from the global variable created by the first script that is still in memory.
I created a set of scripts that controlled bone layers from one master bone layer. On frame 0 a global list of bones for those layers was created. On later frames those variables would be used to move the bones by reading in the list. The list of "master" and "controlled" bones is created only on frame 0.
----------
If you look at the tool scripts, at the top are a bunch of global variables that refer to the version, who created it, the text display when you rollover the tool or the text displayed when you click on it.
When AS launches and all the tool scripts load, those variables are initialized. I find if I change the display name of a tool script, it won't update that info in AS until I quit and relaunch it. Reloading the scripts doesn't overwrite the global variables.
-vern
- synthsin75
- Posts: 10280
- Joined: Mon Jan 14, 2008 11:20 pm
- Location: Oklahoma
- Contact:
Actually crtl-F5 will reload those changes. Though it did crash once, it does work most of the time. At least on the UI roll over name. I think ctrl-F5 reloads everything but the layer scripts. I could be wrong though.When AS launches and all the tool scripts load, those variables are initialized. I find if I change the display name of a tool script, it won't update that info in AS until I quit and relaunch it. Reloading the scripts doesn't overwrite the global variables.

Perhaps after working a file for awhile it's global environment just gets too polluted.