Cannon Targeting System
The cannon base will have a look at constraint, also the turret but only over its z-axis. For both objects we need to create a point helper that finally contains the look at controller, the objects will inherit their rotation.
Create two points helper, name them Helper_LookAt_Turret and Helper_LookAt_Cannon. Select the Helper_lookAt_Turret and align it to the turret, link it to the Turret's Parent (the frame) and assign it a lookAt constraint with the Control_Target object as target. Then select the Helper_lookAt_Cannon, align it to the cannon base, link it to the cannon base parent (the Turret) and assign it a lookAt constraint with the Control_target object as target.
You may like to do not have the lookAt viewline disturbing in the viewport, so you can hide it in the motion panel, setting zero the "viewline length" value in the look_at constraint rollout.
There are some methods to get the rotation of an object that has a lookAt constraint, like using wiring with an ExposeTM Helper, which exposes an object's transform values, even if it has any constraint assigned. But, I prefer to use script controllers, because I didn't need to create any extra object to make it work.
Select the Turret, in the motion panel -> Assign Controller rollout, select the Rotation: XYZ Euler Controller and assign a Rotation List controller to it. As mentioned before, this controller will enable us to have two controller layers, the first with the script controller and the other one to make manual adjustments.
In the Available controller assign an Euler XYZ Controller, go back to the first Euler XYZ, and in the Z Rotation controller assign a script controller.
In the expression, first we need to find the local rotation values of the helper, that's because the controllers need values in local mode (local values in max are respect to the parents). We can do that using the transform values of this helper and its parent.
The way that a 3d software inherits the transform values from an object to its children is to multiply the parent's transform matrix by its children local transform matrix. Having in mind this, we may find the child's local TM by doing the inverse process. In math the inverse process of multiply two matrices isn't divide them, it's a special process called "Inverse", max has a maxScript function for this.
So, the expression goes as follows: helper's transform * inverse parent's transform
From this resulting transform we need to extract the rotation value: (helper's transform * inverse parent's transform).rotation
Then we need to convert this rotation from Quaternion values to Euler angles values in order to be able to get its Z rotation value. To don't make the code line too long, i assign this rotation to a variable and continue in the next line.
So, it goes as follows:
rot=(helper's transform * inverse parent's transform).rotation
(rot as eulerangles).z
For the helper we're going to create a variable called helper_lookAt assign the Helper_lookAt_Turret as node. We can get the parent node as an object's property. The expression finally goes as below.
rot=(helper_lookAt.transform * inverse helper_lookAt.parent.transform).rotation
degtorad (rot as eulerangles).z
degtorad is a need because the controller handles rotation values in radians.
The Cannon_base will need all the rotation values from its helper_lookAt. We cannot assign the lookAt constraint directly to the cannon, because we won't be able to make manual adjustment to it. We could assign an extra Euler XYZ controller in the rotation list for manual adjustments, but it will have unexpected behaviours when we rotate it (this problem happens in rotation controllers only, in position controllers it doesn't happen, and in the turret case, it doesn't happen either because we're using only one axis there).
But there's a way to make it possible, we're going to link the cannon to the helper. Select the cannon_base and the Control_cannon_base objects and link them to the Helper_lookAt_cannon, then select the cannon_base, align it to the Helper_lookAt_cannon (orientation), now it has a wrong orientation, so rotate it 90 degrees over its local y-axis.
Doing this the cannon will inherit the helper's orientation, and also we'll be able to make manual adjustments.
You can move the Control_Target to check this out!
Ok, and finally this is it!, out tank rig is done!. You can animate the Control_Main object throw the path spline and also modify this spline as you like.
To adapt your tanks final model to this rig, you only need to align and link the model's parts to the tank's objects. And if you want a rig more comfortable you can create layers in the max layer manager, one for helpers, other for Control objects etc. Also you could block some position and/or rotation axis in some objects (Hierarchy panel -> Link Info Button), for example in the turret's x and y rotation axis, to avoid movements that aren't possible in the tank. The rig objects like the Frame or the turret are geometric objects, but if you like they could be bones, finally both are nodes. Finally like any other rig you have to be careful with scales.
I hope you enjoyed this tutorial, more stuff will come soon!