-1
$\begingroup$

I want to create an armature that has several bones that are all controlled by one bone. When that one bone is rotated, it will rotate all of the bones, but one at a time. So when one bone has rotated, say, 90 degrees (or however you decide), the next one will start rotating. When the second bone has finished, the third will start rotating, and so on. How can I create such an armature? The bones do not change position, only rotation.

$\endgroup$
1
1
$\begingroup$

Use self in drivers.

enter image description here

Similarly to @joshsanfelici have set the bones to XYZ Euler and driven the rotation z property of each bone via the target bone.

The ability to use self in drivers let's us get away with having one common driver and feed into it object properties.

In this case if one of our pose bones is self and the target bone is named "Bone" and is in the same armature then

self.id_data.pose.bones["Bone"]

is the target bone, and

self.id_data.pose.bones["Bone"].rotation_euler.z

is its $Z$ axis Euler rotation.

If we add a custom property to each bone, eg "prop" it can be used as the start rotation, eg

self["prop"] = 0.0

Rotations are in Eulers, as explained in other answer. So for example sake, if "prop2" is $\pi / 2$ can drive the bone using the target bone using a pretty long an inelegant expression that is the maximum of the minimum between 0 and the target rotation subtract the start range, and the range between our props...

Instead I have written a little script to add some props, make a simple UI, and a driver expression. The UI is appended to the Pose Mode options panel in the 3D view N Tools Panel.

import bpy
from bpy.props import FloatVectorProperty, StringProperty
from math import radians

## properties

bpy.types.PoseBone.copy_range = FloatVectorProperty(
        size=2,
        unit='ROTATION',
        default=(0.0,radians(90)),
        )

bpy.types.PoseBone.target = StringProperty(
        )

# UI 

def draw(self, context):
    ob = context.object
    pb = context.active_pose_bone
    self.layout.label(text="Bone Switch Test")
    self.layout.prop(pb, "rotation_euler", index=2)
    self.layout.prop(pb, "copy_range")
    self.layout.prop_search(pb, "target", ob.pose, "bones")
    

bpy.types.VIEW3D_PT_tools_posemode_options.append(draw)


## driver expression

def rotate_switch(self):
    target = self.id_data.pose.bones.get(self.target)
    if target:
        rz = self.copy_range
        range = rz[1] - rz[0]
        # map to 0 : range
        trz = target.rotation_euler.z
        return min(max(0, trz - rz[0]), range)  

    return 0.0
    
bpy.app.driver_namespace["rotate_switch"] = rotate_switch

See the driver expression is

min(max(0, trz - rz[0]), range)

where trz is the target $Z$ rotation, rz[0] is at which rotation of target our bone starts, and range is the end minus the start of our bones rotation.

Easily extendable.

The idea here is to make it as easy to extend as possible. Can copy any of the driven bones and change their rotation range and the name of the driving bone on the fly.

Can edit the driver function and re-run the script, for example this simple change, (dividing the target rot by 4)

    trz = target.rotation_euler.z / 4

to make the "hammers" fall 90 degrees for each revolution of target bone.

enter image description here

Also here is a little helper script to convert all selected pose bones to XYZ euler rotation, and add a driver to all selected bar the active. (assumed the active is the driving bone)

import bpy

context = bpy.context


for pb in context.selected_pose_bones:
    pb.rotation_mode = 'XYZ'
    if pb is context.active_pose_bone:
        continue
    fcurve = pb.driver_add("rotation_euler", 2)
    driver = fcurve.driver
    driver.use_self = True
    driver.expression = "rotate_switch(self)"
$\endgroup$
0
$\begingroup$

In this setup I use drivers on z rot local space of all bones.

First left bone has this expression:

var if var <pi/2 else pi/2

As "var" is the Z rot controller value, this can be read as:

"give me var, if var is minor than 90 degrees, else give me 90 degrees".

So the driven bone copies the controller rotation only if its value is less than 90 degrees, then it stops at 90.

Center bone has a driver with this scripted expression:

(var - pi/2 if var>pi/2 else 0) if var<pi else pi/2

As "var" is the Z rot controller value, this can be read as:

"give me (var minus 90 degrees, if var is above 90 degrees, else give me 0), if var is minor than 180 dgrees, else give me 90 degrees.

For the third right bone it becomes:

(var - pi if var>pi else 0) if var<3*pi/2 else pi/2

And so on, remembering that

0 degrees equal to 0 radians

1 deg = pi/180 rad

90 deg = pi/2 rad

180 deg = pi rad

270 deg = 3*pi/2 rad

enter image description here

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.