[Closed] Script Controller update based on spline change
I have searched this forum high and low for a solution to this and none of them seem to work. I am writing a scripted plug-in that currently distributes an array of objects along a curve using position_script(). Everything works fine except the objects don’t update unless the frame slider is moved. This is really annoying and I remember running into it before.
I have tried a couple different things in the position script:
fn updateScript ev nd = (self.pos.script.update)
callbackItem = NodeEventCallback mouseUp:true geometryChanged:updateScript
when geometry thePath changes do (self.pos.script.update)
In both cases self is a reference to the node the script controller is on, and in both cases I get the following error:
Runtime error: Code block not created, unable to access code block local or parameter: self
Does anyone have any ideas? I’ve seen other scripts using the path constraint but because I want to add some other things I’m trying to avoid that.
Thanks!!
-Steve
Well the script controller that is on each of the objects long the path should have a variable added, lets call it path, and is should hold reference to the splines node. Any changes to the spline will result in the script controller being fired.
I absolutely agree with PEN. If you add path object as a node to a script controller any geometry change of the path has to fire the script controller update event.
By the way:
- if you want to use “when” construction it’s better to use it with unique id, and delete its handler before calling next time.
- “notify dependents” for the path has to fire the script controller update .
PS. I don’t have MAX on tap now, and I can’t double-check it.
Hey Paul,
Thanks for the reply. When updating the transform of the spline, yes, this works in realtime. However, making any changes to the splines CVs requires a move of the time slider to get the scripts to update. How annoying! So I’m trying to force an update of the script but I keep getting that error.
OK so I have managed to separate out the callback so that I’m A) not creating a crapload of them inside each position script and B) able to actually call an update to the script without it throwing that error. The problem I am now having is that the update method that I’m calling on each script doesn’t actually do anything. According to the docs update() should reevaluate the expression on the script when called, this doesn’t appear to be happening:
when geometry myPath changes do
(
updateScripts()
)
fn updateScripts =
(
try
(
print "updated scripts"
for i = 1 to myArray.count do
(
myArray[i].position.controller.update
)
)
catch
(
print "updateScripts error"
)
)
The listener is printing "updated scripts" but the actual objects aren't changing position. Is this the correct method to invoke to update a script?
Updating the PRS of the path updates the scripts just fine, but when altering CVs of the path they don’t update. I tried using notifyDependents thePath instead of running through and manually updating each script, and it also didn’t work. Am I missing something?
Here is my script as it currently stands. Right now I am creating a scripted helper plug-in and I’m adding the when construct on selection of the path. It works ok for a while but after manipulating the curve for a while max crashes. I will look into the ID and also removing the handler, but where would I add it again? Also if you are running this script the only way I’m calling the assignScripts() function is by changing the copies spinner.
plugin Helper curveArray
name:"Curve Array"
category:"Scripted Array"
classID:#(0x48bf698a, 0x4ec80c97)
extends: Dummy
(
--local nodes
local myPath
local myObj
local myArray = #()
--local functions
local calcPos
--local rollouts
local params
parameters main rollout:params
(
choosePath type:#node ui:choosePath subAnim:true
chooseObj type:#node ui:chooseObj subAnim:true
numObjs type:#Integer ui:numObjs
)
fn updateScripts =
(
try
(
for i = 1 to myArray.count do
(
myArray[i].position.controller.Update()
)
)
catch
(
print "updateScripts callback error"
)
)
fn deleteObjs =
(
if (myArray.count > 0) do
(
for i = 1 to myArray.count do
(
if (isValidNode myArray[i]) then delete myArray[i]
)
)
myArray = #()
)
rollout params "Instance Parameters" width:162 height:99
(
fn shape_filt obj = isKindOf obj Shape
fn obj_filt obj = isKindOf obj GeometryClass
pickbutton choosePath "Select Path" pos:[12,7] width:140 height:21 filter:shape_filt
--checkbox followSpline "Follow Path" pos:[69,33] width:79 height:15
spinner numObjs "Copies:" pos:[24,53] width:120 height:16 range:[1,1000,0] type:#integer
pickbutton chooseObj "Select Object" pos:[11,74] width:140 height:21 filter:obj_filt
fn assignScripts =
(
if(myObj != undefined and myPath != undefined) do
(
deleteObjs()
for i = 1 to numObjs.value do
(
ctrl = position_script()
myInst = instance myObj
myInst.pos.controller = ctrl
ctrl.addNode "thePath" myPath
ctrl.addNode "self" myInst
ctrl.addConstant "numObjects" (numObjs.value - 1)
ctrl.addConstant "ID" (i-1)
ctrl.script = "lengthInterp thePath (ID/numObjects as float)"
append myArray myInst
)
)
)
--If the user picked an object, then
on choosePath picked obj do
(
if obj != undefined do
(
myPath = obj
choosePath.text = obj.name
when geometry myPath changes do
(
updateScripts()
)
)
)--end on
on chooseObj picked obj do
(
if obj != undefined do
(
myObj = obj
chooseObj.text = obj.name
)
)--end on
on numObjs changed obj do assignScripts()
)
tool create
(
on mousePoint click do
(
case click of
(
1: coordsys grid (nodeTM.translation = gridPoint)
2: (#stop)
)
)
)
on getDisplayMesh do
(
if (meshObj == undefined) do
(
meshObj = createInstance box length:5 \
width:5 height:5 mapCoords:false \
)
meshObj.mesh
)
on deleted do
(
deleteObjs()
)
on update do
(
deleteObjs()
)
)