Page 3 of 9

Posted: Fri Jul 18, 2008 4:18 am
by heyvern
Congratulations!

Be careful... scripting for AS can become addictive. There is something that borders on the magical when you create these small bits fo text files that make AS do things in new ways. It's like a rush.

-vern

Posted: Fri Jul 18, 2008 4:30 am
by synthsin75
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.

Posted: Fri Jul 18, 2008 5:20 am
by synthsin75
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?

Posted: Fri Jul 18, 2008 8:22 am
by heyvern
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.

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)
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

Posted: Fri Jul 18, 2008 12:21 pm
by synthsin75
I don't quite understand. Uncommenting the "print(bob)" now doesn't print anything for the "name.dup" instance. It still works, I just don't understand how. It looks like that layer doesn't pass any info to the script.

Posted: Fri Jul 18, 2008 3:51 pm
by heyvern
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. ;)

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

Posted: Fri Jul 18, 2008 10:43 pm
by synthsin75
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:

Code: Select all

local a, b
	
	if(string.find(name, ".", 1, true) ~= nil) then
	a, b = string.find(name, ".", 1, true)
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?

Posted: Fri Jul 18, 2008 11:15 pm
by heyvern
You have to understand what is called variable scope. Where a variable exists and can be called and used.

Code: Select all


local a, b
   
   if(string.find(name, ".", 1, true) ~= nil) then
      a, b = string.find(name, ".", 1, true)
   end
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.

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

Posted: Fri Jul 18, 2008 11:43 pm
by heyvern
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:

Code: Select all

myGlobalVar = 6
And don't specifically remove it:

Code: Select all

myGlobalVar = nil
It hangs around. If you set a global to "nil" the lua garbage collection removes it from memory.

-vern

Posted: Sat Jul 19, 2008 12:43 am
by synthsin75
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.




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 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).

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)
Something like that?

Posted: Sat Jul 19, 2008 4:07 am
by heyvern
So unless you clear them, AS could be holding in to any global variables?
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.

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
Any local variable declared within the "LayerScript()" function (bob for instance) is available ANYWHERE inside that function no matter how deep the structure goes. This is like a "mini" global. It's available throughout the script but is still a local variable. As long as you don't use another "local bob" again which would override that one.
Something like that?
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.

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
But what if you need to compare each bone in the list to ALL the other bones in the list:

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
In this case you have the for loop which has a "built in" local variable that we assign "i = 0" in this case. The second variable (boneCOunt) is how many times to do the for loop; the number of bones - 1 because we started at 0.

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
Now we have the result of any matches during the for loop, to use anywhere within the scope that theBoneID was declared. If theBoneID is "nil" we know there was no match.

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

Posted: Sat Jul 19, 2008 11:52 am
by synthsin75
Thank you sooo much for elaborating on these details, Vern.

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
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.

Posted: Sat Jul 19, 2008 5:47 pm
by heyvern
The only reason to declare a 'local j' outside of the statement is if you need to access it elsewhere?
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.

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
Without the "break" j would = whatever boneCount - 1 is.

-----

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

Posted: Sun Jul 20, 2008 4:17 am
by synthsin75
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.
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. :wink:

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

Posted: Sun Jul 20, 2008 5:42 am
by heyvern
I do most of my scripting on the mac. It could be it works differently there. I just know if I change the rollover name of a tool it won't update in AS no matter how many times I use "cmd-F5". I have to quit AS and restart it to see those changes.

-vern