[Closed] call needs function
Hi! I just finished this script that can replace selected objects and at a lookat controller instance that looks at the chosen one. Actually everythings running fine now, but I had big troubles with the error message “call needs function…” which appeared always after starting max, when the script was executed the first time.
I then moved the definitions of the functions to the top of the script and now the error message disappeared. That’s pretty fine but I’d really like to know WHY!!
So if anybody knows please gimme a hint.
Here’s the script:
fn replacing repobj =
(
objenew = #();
obje = getcurrentselection() -- aktuelle Selection heranziehen
clearSelection()
for x in 1 to obje.count do -- Objekte in Selektion einzeln abarbeiten
(
objenew[x] = instance repobj -- Referenzobjekt instanzieren
objenew[x].pos = obje[x].pos
delete obje[x] --Loesche die Ursprungsobjekte
selectmore objenew[x]
)
)
fn controllerzuweisen lookobj =
(
obje = getcurrentselection()
for a in 1 to obje.count do
(
if a == 1 then
(
obje[a].rotation.controller = LookAt_Constraint ()
obje[a].rotation.controller.appendTarget lookobj 50 -- look at object 1 with weight 50
obje[a].rotation.controller.viewline_length_abs = off
obje[a].rotation.controller.lookat_vector_length = 1
obje[a].rotation.controller.upnode_ctrl = 1
obje[a].rotation.controller.target_axis = 2
obje[a].rotation.controller.upnode_axis = 2
obje[a].rotation.controller.StoUP_axis = 1
)
else
(
obje[a].rotation.controller = obje[1].rotation.controller
)
)
)
rollout rolling "Get and Replace Objects" width:184 height:124
(
pickButton pick1 "Select Replacement Object" pos:[8,10] width:168 height:25
button btn1 "Replace!" pos:[8,45] width:168 height:25
pickButton pick2 "Select LookAt Object" pos:[8,80] width:168 height:25
button btn2 "Add Controller To Instances" pos:[8,115] width:168 height:25
global repobj
--local objenew = #()
global lookobj
on pick1 picked obj do --If the user picked an object, then
(
repobj = obj
if repobj != undefined do --see if the user did not cancel the picking...
(
repobj.wirecolor = red --if he did not, make the box's wireframe red:
pick1.text = repobj.name --and display the name of the object on the button:
)
)
on btn1 pressed do --start der funktion, jetzt geht's los
replacing repobj
on pick2 picked obj do -- bitte das hinschauobjekt aussuchen
(
global lookobj = obj
if lookobj != undefined do --see if the user did not cancel the picking...
(
lookobj.wirecolor = green --if he did not, make the box's wireframe red:
pick2.text = lookobj.name --and display the name of the object on the button:
)
)
on btn2 pressed do -- controller zuweis funktion
controllerzuweisen(lookobj)
)
floater_heinzi = newrolloutfloater "DaniVision" 210 180
addrollout rolling floater_heinzi
Make sure the functions are defined a the top of the rollout :
rollout rolling “Get and Replace Objects” width:184 height:124
(
fn replacing repobj = …
fn controllerzuweisen lookobj = …
pickButton pick1 …
etc…
This means that you cannot call a function BEFORE it has been defined :)
Because you have written your code in GLOBAL scope (as opposed to local scope by enclosing the complete script in brackets), your functions were evaluated AFTER the rollout was declared. Thus the rollout contained references to undefined functions which were defined later and the rollout had no knowledge of their existence.
The second time you would run the script, the functions would already exist in the global scope and the rollout would “see” them even before they were reevaluated.
Moving the functions to the beginning of the script made them “visible” at the time of rollout evaluation.
This is because MAXScript first evaluates, then executes.
In other words, when you press Ctrl+E or when Max loads and evaluates a new script from disk, MAXscript looks at every variable name and its value. If a variable name does not exist it is assumed to be UNDEFINED. If you are referencing to a non-existent variable, it is implictly created as local if inside some scope, or as global if at top-level.
When the rollout is first evaluated, the function variables do not exist so they are assumed to be undefined in the local scope. Later the functions are defined implicitly as global, but the rollout references its local variables with the same names which still contain undefined.
When the rollout handlers are called at run-time, MAXScript does not go looking for the functions again as it has already collected the values during the evaluation phase. So it complains that it expected a function but got undefined – it sees the implicitly local version of the variables and not the global functions.
Your options are either to define the functions before they have been referenced in a rollout or another function (as you did already), or to explicitly DECLARE variables so that MAXScript can reserve memory for them during the evaluation phase.
For example, if you would declare ‘replacing’ and ‘controllerzuweisen’ as global (or local, if working in a local scope) variables before the rollout AND before the function defintions, the rollout will encounter the variable name references in its handlers when evaluating, go looking for these variables and will find them as pre-declared albeit still containing undefined. The rollout will store a pointer to the memory address of these variables and keep on evaluating. When the functions are finally defined after the rollout, they will ALSO look for existing names in the respective scopes and will also find the pre-declared variables. They will store the function definitions AT THE SAME MEMORY the rollout is pointing at. So when you press a button at run-time, the rollout handler will suddenly find a valid function at the previously stored memory location and not undefined, and will go on happily!..
Wow, that was really helpful. I totally understand now. Thanks for clearing that up.
Daniel
Always declare functions before you use them like this
–Iterations
global FW_IterationsMany
global FW_IterationsAuto
global FW_ObjectNameCurrent
global FW_IterationsAll
If you have functions in a Struct you also must declare the functions before they are used like this:
struct Testing
(
–Function Init
fn IniSetParentName =(), fn IniSetCategory=(), fn IniLoad=(), fn IniSave=(),
fn Init=(), fn SetGroupClass=(), fn SetProgramClass=(), fn SetOpen=(), fn SetRollout=(), fn Open=(), fn SetFold=(), fn GetFold=(), fn SetSize=(), fn GetSize=(),
fn SetSizeArray=(), fn GetSizeArray=(), fn CheckSnap=(),
--Now you can write the functions
fn Init=
(
--Write the code.
),
--Write the other functions
)
Normally the cleanest way to write programs is probably to build objects, (structs) with collections of functions. That way you won’t have a zillion global variables.
/Andreas