PDA

View Full Version : how do you increment a value in an transform script controller?


EverZen
07-02-2008, 06:03 PM
Hi guys,

I am running a rather strange personal project that involves a chain cannon on the end of an IK arm. Now the IK handle moves the arm which aims the chain cannon. But additionally, the chain cannon must be able to spin around its own x axis in order to fire.

The aiming is easily controlled through the rig and parenting. The GunBarrel sits in a sub group. controlling the local rotation around the x axis of this sub group spins the barrel nicely.

The trouble is that the animator wants me to include a rate of fire. We have a Pointer that is acting as a character controller, which a load of custom attributes. The animator wants to be able to set a rate of fire custom attribute. So he can animate it from 0 (barrel doesnt spin) up to a high spin rate and then back down to zero again.

I thought to do this by incrementing the x rotation by the rate of fire value. So if the rate of fire is 2 (for example) then each time the expression evaluates it adds 2 to the current rotation. (Maybe with an if statement to reset it to an initial value at frame 0)

I was going to do this with a transformation script


--Rotates the transformation locally around the X axis by the angle variable FireRate
CurrentTrans = $GunBarrel.transform
preRotateX CurrentTrans $CharacterControl.FireRate
--This returns a new rotated transformation matrix that I want the gun barrel to now use


Now this hits me with all these Illegal self referencing issues! So my question is, if you want to simply add a value to a tracks current value each time the run time expression evaluates, how can you find out what the starting value is without illegally self referencing it! Effectively I want to be able to do something like this in pseudo code:


--What is the equivalent of doing this in a run time expression on a script controller!?

--To perform this eachtime the expression evaluates
GunBarrelRotation = GunBarrelRotation + FireRate

--or

GunBarrelRotation += FireRate






I have looked into weak referencing it, by storing a custom attribute with a nodeTransformMonitor on the Character Controller that points at the GunBarrel and then finding the transform from:

nodeTransformMonitor.node.transform

But this is still hitting me with with an Illegal self reference.

There must be a very simple way to do this, but I am unfortunately completely over looking it as I have not used script controllers and expressions very often at all....

I hope this dilema makes sense! Any help would be massively appreciated!

Cheers guys,

Rich

Mathieson
07-02-2008, 06:22 PM
I've run into similar self referencing issues before. From the sounds of it you don't need to be monitoring the gun barrel's transform at all though, just a custom attribute for the rate of fire and then a script controller on the gun barrel's X rotation with the expression "rateOfFire * F".

Let me know how this works. I may have misunderstood.

PEN
07-02-2008, 07:14 PM
Mat is going in the right direction there. A float script is the way to handle this. Don't know why you are getting the circular problem unless I looked at it.

I think that something like this might be closer to what you are looking for.

degToRad ((360/6)*rate*f)

Where 6 is the number of barrels that you have. If rate is 2 you will get two shots off each frame. If you want that per second then do...

degToRad (((360/6)*rate*f)/30.0)

Assuming you are working at 30 FPS.

PEN
07-02-2008, 07:21 PM
I have the feeling this isn't going to work. You will need to store a last value I think and add to it. The reason for this is it will start to mess up once it is winding back down again. Probably will start spinning backwards.

Mathieson
07-03-2008, 12:03 AM
Yeah you're right Paul, it doesn't work for the slowdown, it does just unwind. I'm going to play around with it a little bit.

Mathieson
07-03-2008, 01:23 AM
Got an incremental system working and the gun barrel slows down properly, but it is a bit hacky and has some issues.

Since the value is being determined incrementally instead of absolutely the value is just continually being added to whenever the expression is evaluated. This is creating some in-definite animation, so if you scrub forwards or backwards it will spin the same direction either way. If you scrub back and forth over the same 10 frames or so it will just continue spinning, which is essentially the same problem. The spinning speed seems to vary depending on how fast you scrub the timeline/playback. You'll also get strange re-drawing of the animation curve on the script controller if you move your track view window around.

So anyways, its working but definitely not the best solution. Pretty gross actually. Probably going to play around a bit more.

I uploaded the file I was messing around with. Download it here. (http://www.mathiesonfacer.com/misc/chainGun/chainGun01.rar)

Here is the expression I used in the script controller.

newVal = lastVal + rateOfFire
this.SetConstant #lastVal (mod newVal 360)
degToRad newVal

Where lastVal is a declared variable in the script controller and assigned as a constant.

PEN
07-03-2008, 02:13 AM
That doesn't work well like that Mat. Better to create a DA def and add it to the script controller. Add a local variable in it called lastVal, DON'T use a parameter block variable as it is slower to write to. Then use
this.theDef.lastVal=currentVal
Because scrubbing backwards is still considered a change in time you should also store lastFrame in the def and invert the math depending if the time is higher or lower then the currentTime.

EverZen
07-03-2008, 10:08 AM
Hi Paul and Mat,

Thanks so much for looking into this. You guys picked up on why I was trying to separate this process from frame/time very early on, cause it messes up bad when you try and slow the barrel down.

I completely understand the need for the switch on the maths when you run the frames backwards, that should be doable by storing the last frame as you mentioned. Should this be combined with an initialising value at frame 0?

However, I am still a bit new to this script controller stuff and the sentence regarding "create a DA def and add it to the script controller" confused me a bit. Are you talking about adding an attribute in the parameter editor to the script controller and then referencing that? Or is that what you are trying to say to avoid?

If you could explain this terminology and the line

this.theDef.lastVal=currentVal

a little more, then I would be really grateful. I have a bit of maxscript experience, so I hopefully will be able to understand it.

Also Mat, I am only running Max 9, I could not open your scene, and I would really like to look at it, have you saved it in a newer version of Max? Sorry to be such a pain.

Thanks again guys, this is all really appreciated

Rich


Aside:
The reason I am pursuing this, is cause this barrel rotation I have already linked to a particle system that recognises when the barrels align to fire, and emits a particle at the appropriate time. These then detect collisions with scene geomtry and spark etc on impact. Since there are going to be a few guns the animator wants to just control direction and fire rate and the rest of the system will procedurally update. It is working so far :) Except this bit!

Neuro69
07-03-2008, 10:36 AM
I don't know any elegant way to achieve this (unless you think that a motor/dynamics setup is elegant...), but I've tried similar things in the past. My experience is that it's probably a bad idea to use this kind of setup, as it can easily result in unpredictable/unreproducable animation where different rendered sequences fail to match up. I'd just stick to doing it the hard way, which shouldn't really be that hard after all.

JHN
07-03-2008, 10:51 AM
Besides the script controller solution wouldn't a multiplier track to the rotation track suffice?
You could just set a default rotation rate and with relative repeat out of range and apply a multiplier track to animate the amount... just copy/instance the controller to a custom attribute... I'll bet it's the fastest solution :)

-Johan

JHN
07-03-2008, 11:01 AM
Okay, that'll will also not give the control you're after... it sort of works, but the feedback is not what you want... sorry, still I think somehow this path can be walked on with some further investigation...

--Johan

PEN
07-03-2008, 12:55 PM
Just so you know I use this method all the time that I have described and it works but if you are going to net work render you really need to bake the result so you don't get what Neuro69 points out. With a system that need to store old values it really needs to be ramped up one frame at a time to get the result that you would expect. Once baked it will net work render.

PEN
07-03-2008, 03:08 PM
Check this out then, it doesn't have a baking function built in but it works in the viewport. Note, if you jump to a frame the rotation will not be correct. I don't see this being a problem for the animator, they just need to know the rate is correct and then bake the solution before rendering.

http://paulneale.com/temp/rateOfFireTest.zip

It works using two ca defs, could be made to work with one actualy but I wanted to not complicate things from what we were discussing. First off I Freezed transforms on the gunBody object. $gunBody[#transform][#rotation][#Zero_Euler_XYZ][#z_rotation].controller=float-script()
then I created a def to store the old values and applied it to the script controller.

The second def is just for the rate spinner. This is referenced into the script controller and used to calc the rate of fire per frame.

Neuro69
07-04-2008, 02:23 PM
I gave this some thought, and came up with this script controller:

acc=0
for i in 0f to F do
(
at time i acc+=($dummy01.pos.y*0.01)
)
acc

$dummy01.pos.y is an animated throttle, the actual values would have to be tuned to requirements. The script calculates all rotation/throttling that has taken place since frame 0 (and will be slower the farther you get from frame 0, but 2000 frames worked just fine...). The animation works as required, and there is no problem with discontinuities or scrubbing.

I first tried using a track variable, but couldn't seem to make it work with "at time"?

Edit: important "gotcha", this script will not work correctly when rendering fields (and probably not for motion blur either), as it only updates on whole frames. That could be fixed by making the time-loop count half-frames or less...

EverZen
07-04-2008, 02:50 PM
Cheers for all the feedback.

Paul, unfortunately I only have Max 9 in the office, but I get the basics of what you are saying and am going to have a play. Thanks so much for taking the time to upload something.

Johan- nice idea, but as you mentioned not quite there. I had not even thought about that avenue, and have found out some other really cool stuff, just experimenting with your suggestion so thanks :)

Mat, some problem as with Paul and me only having Max 9. Would love to look at your interpretation too.

Neuro69 - gonna try and test your idea out over the weekend, havent tried using script controllers in that way. Maybe combine that method with baking the animation? That would then allow for rendering with fields/motion blur etc. Havent tried it, but u reckon it would work?

Thanks for all feedback, it is definitely a huge help.

Cheers,

Rich

Mathieson
07-05-2008, 06:43 AM
Hi Rich,

Sorry I took so long to respond, been busy.

I just converted Paul's example to Max9 for you. I was heading in a similar direction as him but the example he posted was much farther along than what I had posted.

Download the file here. (http://www.mathiesonfacer.com/misc/chainGun/chainGun02.rar)

Hope that helps.

CGTalk Moderation
07-05-2008, 06:43 AM
This thread has been automatically closed as it remained inactive for 12 months. If you wish to continue the discussion, please create a new thread in the appropriate forum.