Triggering unparenting (noob)


#1

Hey there,

Before I ask my question, please understand that I barely know anything about programming, and have only once used an IF statement… and it was a bit iffy.
I write basic expressions generally, rarely touch pymel, and that’s it.

I say and stress this, because every time I ask a question, a lot of people put a lot of time and effort into giving what’s actually, from my perspective, overly complicated answers to what I’m looking for, which are usually too full of jargon, and things I don’t understand how to do, to be of any use to me. Basically, I’m looking for the simplest most obvious solution to my problem, not a way to automate the process for Skynet :smiley:

So what I want to do is simply this… it’s a camera test for students, where if they move the pivot point to a goal, I want an object, which was at the pivot point, to un-parent and change colour…

So essentially what I want to work, in code that obviously doesn’t work, is this:

If (locator1.WorldRotatePivotX = <0.5 & locator.WorldRotatePivotY = <0.5 &
locator.WorldRotatePivotZ = <0.5)

then {unparent locator1 & change drawing override colour to 15 (that bright green one)};

The reason I want to use the World Rotate Pivot is so that I have a number that is relative to the world, and not the object it’s parented to, so that it still moves.
Ofcourse an alternative would be to use a Point constraint and disable it… I’m experimenting with SDKs, expressions, and swapouts at the moment, but am not quite figuring out a good way to do it yet.
I also want this to be constantly running & checking… I have no idea how to do that.

Again, sorry for my lack of expertise in this area, but I probably won’t be able understand anything much more complicated than this without a detailed explanation of why and how for every bit of it… is there a way to make just this code example work?


#2

One thing I’m trying now, to make it only hit and detatch once, is this:

if (A1_Distance_MSRShape.distance = 0.5 && $A1 Hit= 0)
		A1_Aim_LOC.Attached = 0;
		$A1Hit = 1;
		$A1HitPosX = A1_Aim_LOC.translateX;
		$A1HitPosY = A1_Aim_LOC.translateY;
		$A1HitPosZ = A1_Aim_LOC.translateZ;
		$A1HitRotX = A1_Aim_LOC.rotateX;
		$A1HitRotY = A1_Aim_LOC.rotateX;
		$A1HitRotZ = A1_Aim_LOC.rotateX;

From here, I use the Attached attribute’s status to drive both the orient and point constraint’s weight status.
I can do the same, *-1, on the weight of a second set of orient and point constraints, which are from a null whose position is driven by the above marked variables, to enable it on those, to drive it to the position and rotation that A1_Aim_LOC was at when it hit the target distance from the marker…

I understand the logical theory here. What I have absolutely zero comprehension of however, is how to write it correctly.
If I can get that code above to do what it looks like it should do, I think I’m all good.

I’ve done a lot of searching, and can find no examples of, in expressions, anybody having more than one result returned on an expression.

Also, if I’m using an expression to drive an attribute, it seems that 2015 will not let me drive another attribute with that attribute using an expression, simply because it’s already involved in an expression. Is this a bug, or am I doing something wrong? It can be so hard to tell when learning something new.


#3

(I stopped reading when you preemptively complained about the answers you’ll get. Not a good way to start a question…)


#4

Sorry mate, I’ve got Aspergers. Didn’t mean to offend nor complain, but simply give a direction of the type of answer I’m looking for. I don’t want anybody to waste their time on something I’m not going to understand, and tried to be humerous about saying so.


#5

Well I probably wont get any replies, but I’ll post my progress for future noobs trying to get past this same initial hurdle with scripting.

So far I’ve now realised that I have to define variables before using them, and that this can actually be done within an expression.

So here’s what my code looks like currently.

int $A1Hit = 0;
float $A1HitPosX = 0;
float $A1HitPosY = 0;
float $A1HitPosZ = 0;
float $A1HitRotX = 0;
float $A1HitRotY = 0;
float $A1HitRotZ = 0;

if (A1_Distance_MSRShape.distance = 0.5 && $A1Hit = 0) {
		A1_Aim_LOC.Attached = 0;
		$A1Hit = 1;
		$A1HitPosX = A1_Aim_LOC.translateX;
		$A1HitPosY = A1_Aim_LOC.translateY;
		$A1HitPosZ = A1_Aim_LOC.translateZ;
		$A1HitRotX = A1_Aim_LOC.rotateX;
		$A1HitRotY = A1_Aim_LOC.rotateY;
		$A1HitRotZ = A1_Aim_LOC.rotateZ;
}

Unfortunately this returns the error

// Error: Connection failed from A1_Aim_LOC.rotateZ to A1Hit.input[5]

I don’t know what that 5 means, but the variable going into rotateZ is not A1Hit… so it sounds like it seems to think A1Hit is some kind of owning category for the other variables, which inadvertently cannot receive values because of it. This is really annoying and I have no idea what’s going on.

Now obviously A1_Aim_LOC rotateZ is the output value, and I’m not trying to put anything into it here, but I can’t help but be paranoid about how it deals with that after seeing it refuse to let me use things as outputs in expressions, because their inputs are expressions, saying that it can’t happen because they’re already used in an expression… that rotate value is not involved in an expression, but is driven by an Orient Constraint. Obviously by all logic this shouldn’t be an issue, but I’m stuck to the fact that I don’t know what’s normal here to know if it’s my fault or simply Maya being a bit of a twot on Thursday afternoons.

One thing that’s really confusing me, is that even if I entirely remove any mention of A1Hit and change all the names of everything else… that error is still exactly the same, listing a variable I am not defining, in a place where it was never even involved.
Perhaps there is some hidden behind the scenes thing that stores lists of variables and I accidentally somehow named it “A1Hit”… but now that prevents me from defining and driving any variable in the scene, assuming I’m doing it right.
I may now try to create the same setup in a blank scene, and see what happens.
I will keep experimenting and see what I can figure out.


#6
Ok I figured it out in the end.


if (A1_Aim_LOC.AimDistance < 0.5) {
		A1_Aim_LOC.Hit = 1;
		A1_Aim_LOC.RestPosX = A1_Aim_LOC.translateX;
		A1_Aim_LOC.RestPosY = A1_Aim_LOC.translateY;
		A1_Aim_LOC.RestPosZ = A1_Aim_LOC.translateZ;
		A1_Aim_LOC.RestRotX = A1_Aim_LOC.rotateX;
		A1_Aim_LOC.RestRotY = A1_Aim_LOC.rotateY;
		A1_Aim_LOC.RestRotZ = A1_Aim_LOC.rotateZ;
}

Not sure why it wouldn’t work before… but it was simpler than expected.
Just remember if using more than one statement, that you need two pairs of brackets e.g.

if ((A1_Aim_LOC.AimDistance > 0.4) && (A1_Aim_LOC.AimDistance < 0.5)) {

#7

Did you ever read through the maya help files concerning mel and expressions? There are some problems in your script which are explained there. w.g. why did this not work:

if (A1_Distance_MSRShape.distance = 0.5 && $A1Hit = 0) {
	   .....

The problem is that you mixed an assignment = with a comparision ==. In you case you assigned 0.5 to the distance attribute. Everytime you see something like this “…input[5]” it has something to do with the connection to an array attribute of a node. The expression editor creates an expresion node which has inputs and outputs. e.g. if you create this expression:

locator.translateX = frame;

a expression node is created and gets an input[0]. This input assinged to the varaible “frame”. At the same time it has an output[0] which is the representation of locator.translateX. Have a look at the hypergraph or node editor to see the inputs and outputs of an expression node.


#8

Thanks Haggi, I appreciate you taking the time to explain it to me.
Sorry to say though I’m not quite as on top of this as you might think, and I don’t quite understand what you’re explaining to me =/
I must have had at-least 10 tabs of maya expression examples and tutorials up all day while working on this, but nothing covered anything that I was doing wrong here…

I didn’t use 2 = signs in a row, and am not sure if you mean something else by that… but I don’t understand about what you’re saying about arrays.

Do you mean that I cannot use the distance attribute the way I would normally use it, because of it being an array (I thought an array is just a collection of different attributes, and that this is just a single attribute), and that I should write my code in a different way to deal with this?
I also don’t understand what’s meant by the input[0] thing, or what the word frame does. I know I must sound pretty dumb right now, but I wasn’t kidding when I said I was new to this. I know how to see my inputs and outputs, using the hypershade, hypergraph, node editor etc… but I don’t see how that helps me here or what the problem was.
I don’t know what an Assignment is, or a Comparison >.<
Any chance you could explain it in laymen’s terms?


#9

I just thought I’d propose an answer to you original question, done entirely for my own enjoyment and knowingly ignoring your attempts at mel scripting.

So… the original question:

I solved it using a scriptJob. The code below shows the creation of a locator to test with, the definition of a function to check the locator rotate pivot and take action when it is beyond the given point. And finally the creation of the scriptJob to monitor the locators translation changes.

# import some python modules
from functools import partial
import pymel.core as pm

# create a locator in a group and move group to some place positive
grp = pm.group(empty=True)
loc = pm.spaceLocator()
loc.setParent(grp)

grp.t.set(2,1,5)

# define the goal point
pointX = pm.datatypes.Point([0.5,0.5,0.5])

# define the function that checks locator rotate pivots proximity to pointX
def lookAtMe(loc, pointX):
    locRotatePivot = loc.getRotatePivot(space='world')
    dif = locRotatePivot - pointX
    if max(dif) < 0:
        print 'go green'
        loc.overrideEnabled.set(True)
        loc.overrideColor.set(14)
        loc.setParent(None)

# create s scriptJob to monitor changes to locators translate attribute
jobNum = pm.scriptJob( attributeChange=[loc.t, partial(lookAtMe, loc, pointX)] )

# now over to you... move the locator - it only works if you actually move the locator

David


#10

Please, please! Read the maya documentation about mel scripting and expressions. Take all docs, examples and sources you can find in the web. e.g. concerning frame and time:
http://help.autodesk.com/view/MAYAUL/2016/ENU/?guid=GUID-CCF9692B-5284-4F84-B6D2-AD7A5120668F
This will help you to understand our answers.


#11

I get that time is just an attribute on a node, and that the timeslider and Play system is just a fancy interface that it changes +1 (frame) by however many your settings say per second when you hit play, and that expressions evaluate once per frame… I used this knowledge to calculate a vector once to automate some controls for a stream of particles.
What I don’t know how know an attribute could possibly have a problem with this system.
Please, is there no way you could put it in simple terms without me having to read a whole book? =/

And djx, that looks fascinating but I don’t think I’ll understand it for many years. I will experiment with it though! Do I just throw it in the script editor’s Python tab, and hit play, in a blank scene?
Or at-least create objects with the names you’ve listed, then hit play? Will try it on my lunch break!


#12

Drop it into a python tab. Select all the lines and execute. (No need to hit play). The objects will be created and so will a “scriptJob”. ScriptJobs run in the background and monitor certain specified conditions and events (there is a big list of these in the manual). In this one I’m monitoring any change to the locator translate attribute. Everytime the scriptJob detects a change, the function will be called.

Maya uses scriptJobs for lots of things. You can get a list of currently running jobs like this

pm.scriptJob(listJobs=True)

David


#13

Thanks David,

I tried to run it, and it did create a locator in a null group, at 2, 5, 1 every time, but neither move with the camera, starting the pivot any side of of 0 doesn’t make a difference to the result for me, and I did try moving both the group and the locator all over the place around the centre of the grid without seeing anything change, and I can’t see it in the job list, but knowing that that’s a thing completely changes what I thought could be done with Python in Maya… I didn’t know things could keep running after hitting play, thought they were once-offs! Glad to hear otherwise.

And sorry I couldn’t figure out how it was supposed to work, I know you put time into it.
I’m saving this for later though, for once I’ve learned a lot more than I know right now, as reference. I wish I was able to get it to work, but I think even that’s a little beyond me at the moment haha =)

As for if anybody would like to see what I came up with so far, you can download it here:
https://www.dropbox.com/sh/xn9bf5chebnzxi9/AACkpWznDgkxVE6aj4rYm_Uya?dl=0

It requires Maya 2015 or sooner to work, and this is the template functional version for what will become a level based system, probably without options for what I give my students to use. Or putting a password on that would be a nice feature I couldn’t imagine how to do, as would be a timer… Then again, real buttons outside of a panel would also be nice, and I’m not sure if that’s even a possibility :smiley:
Maybe by having a script for when an object is selected, deselect the object, and quickly swap out then in again to a depressed version of something that looks like a button, while enacting a function… But that’s way beyond me right now. This is all IF statements and SDKs.