quick and lazy script to mirror a walk cycle


(joshwedlake) #1

heya,
thought i’d share this quick and lazy script I wrote to mirror half a walk cycle (ie one stride) and add cycle modifiers to all the fcurves.

would also welcome any crits on this cycle:

Also I know bone roll is now fixed thanks to Campbell’s great efforts, but if you ever finding yourself needing to roll a bone towards a specific vector which isn’t one of the axes (maybe something you read off another object), then this will do the job…

thanks!


(revolt_randy) #2

Interesting idea, I like it and think the script will be useful. Have you thought about integrating it into the tool shelf menu? When in pose mode, the tool shelf has 2 options - ‘x-axis mirror’ and ‘auto-ik’, it could be added to that menu…

And you say that now the bone roll is fixed? Good deal and way to go Campbell!

Randy


(joshwedlake) #3

hey randy, yep bone roll is fixed in svn builds (not in 2.55). any builds since last friday should have the bone foll fix.
im waiting a while before I give any of my scripts guis as rewriting them every time the API changes is annoying! personally i find the way the pop under operator options disappear when you move your mouse too far away really annoying and I prefer just keeping my script ‘options’/variables whatever at the top of the script and toggling which script is on view in the text editor, but then I preferred 2.49’s GUI so I’m probably just weird!
cheers


(waylow) #4

hi josh,

Overall your walk isn’t bad but I can notice a few things that will improve your walk

Feet:
-keep the foot on the ground one frame longer and then roll off from the toe
-try to avoid the feet moving forward and back in the same plane (the foot should move out from the body in the passing position)
-also add some rotation on there so it isn’t pointing straight forward
-and your passing position is a little high for a realistic walk cycle (for cartoon it is fine)

Hips:
if he is a disco character you could do a double bounce walk it will be fun!
it also looks like you don’t have any weight shift side to side make sure his weight is over the foot that is planted
(looking straight on the hips make a figure 8)

Torso:
lean him slightly more forward

That’s plenty for now hopefully that helps

Keep it up mate


(joshwedlake) #5

hey waylow, thanks for the advice! I’ll repost when I’ve had another go. the figure 8 advice is great - somethimes I’m not sure what I’m looking for in terms of arcs.
also thanks for all the rigs, been trying out a 4 legged walk with Alex - will post that too if I manage anything respectable!
cheers


(waylow) #6

4 legs are hard man!
but probably only a little bit easier than 2 legs

on the hip figure 8 thing - it isn’t a perfect figure 8
people spend a lot more time over one foot then in the middle

but keep it up


(joshwedlake) #7

there’s another attempt at a bit more of a swagger! I think I’ve got the foot rotation a bit better but still can’t get the back foot to hold without making it look too sticky(?) Hope the hips look a bit more lively but I haven’t braved up to the double bounce yet!

and here’s my first shot at a 4 legged walk

I really struggled to get the legs straight without the IK popping - soft-IK would be amazing but I can’t figure out how to do it without making the legs stretch everywhere. Or alternately you could make the geometry look straight while the bones are still bent. Do you have a blend of any walks you’ve done with this rig - would be great to see how you animated it. It feels a bit like the saddle / belly weight control is too far back. Looking at video reference it seems like riders usually sit further forward - I guess that makes sense as most of the power (esp in the gallop) comes from the front legs. Of course the rig is the least of my worries!

Was amazed to read in the ancient ‘animal locomotion’ book we have at college that in a slow horse walk the feet spend three times as long on the ground as in the air - makes it nowhere near as easy to animate as a human walk where I can just start with the front contact and rear lift in the same pose - I get lost pretty quickly if I’m not 100% concentrating! The book is years out of copyright - I’ll try and do some scans of all the walk/trot/rotatory and transverse gallop/amble charts and motion paths it has and put them up on the net if I get time at the weekend.

thanks again!


(waylow) #8

hey josh,

the feet are looking a lot better but now your knees are bowed. do you have an IK target for the knees?
you will need to animate that too to fix the awkward bending (sometimes you will need to set keys on 1’s on some body controls to fix certain parts)

your 4 legged walk is pretty damn good!
It is hard to stop the popping but don’t forget about the shoulder control.
I think I will make this a little easier in a later version of the rig

I would have to disagree with the power of the horse gallop coming from the front legs. I’m pretty sure in all quadruped’s the power comes from the hind legs.


(mishurov) #9

I’ve written the similar script which works with Blender 2.61. Code needs refactoring indeed, nonetheless it works. I’ve documented it neatly so I hope it wouldn’t be complicated to you figure out what’s going on to made necessary changes)


import bpy

#Frames of a half of a walkcycle
startFrame = 1
endFrame = 20

#Frame to bring playhead back
preprocessFrame = bpy.context.scene.frame_current

#Framerange of a half of a walkcycle
timeDistance = endFrame - startFrame + 1

#Bones are to be processed
processingObjects = bpy.context.selected_pose_bones

for procObj in processingObjects:
	#Selecting of the bone	
	bpy.ops.object.select_pattern(pattern=procObj.name, case_sensitive=True, extend=False)
	
	#Selecting of the opposite bone if any. I used string methods because the select_flip_active() function doesn't works well within iterator. So a naming convention requires the lowercase '.r' and '.l' in bone names. 
	currentObject = procObj
	if (len(procObj.name.rsplit('.',1)) > 1):
		if (procObj.name.rsplit('.',1)[1] == 'r'):
			bpy.ops.object.select_pattern(pattern=procObj.name.rsplit('.',1)[0]+'.l', case_sensitive=True, extend=False)
		elif (procObj.name.rsplit('.',1)[1] == 'l'):
			bpy.ops.object.select_pattern(pattern=procObj.name.rsplit('.',1)[0]+'.r', case_sensitive=True, extend=False)
	
	#Assigning to a variable the opposite bone if any, otherwise the same object becomes a mirrored one
	mirrorObject = bpy.context.selected_pose_bones[0]
	
	#Selecting of the bone. Just in case if the opposite bone was selected.
	bpy.ops.object.select_pattern(pattern=procObj.name, case_sensitive=True, extend=False)	
	
	#Jumping to the first frame
	bpy.context.scene.frame_set(frame=startFrame)
	
	currentFrame = startFrame
	while currentFrame<=endFrame:
		bpy.context.scene.frame_set(frame=currentFrame)
		#copying a pose
		bpy.ops.pose.copy()
		#pasting a flipped pose
		bpy.ops.pose.paste(flipped=True)
		
		#Setting keys at a second half of a walkcycle
		mirrorObject.keyframe_insert(data_path="location", frame=currentFrame+timeDistance)
		mirrorObject.keyframe_insert(data_path="rotation_quaternion", frame=currentFrame+timeDistance)
		if currentFrame==endFrame:
			break	
		#Jumping into next keyframe
		bpy.ops.screen.keyframe_jump(next=True)	
		currentFrame = bpy.context.scene.frame_current

#Playhead goes back
bpy.context.scene.frame_set(frame=preprocessFrame)


(mishurov) #10

Rewrote code. Now it acts as a GUI tool in the Toolshelf. When you are in the Pose Mode.


import bpy

class WalkCycleUtil():
    """
    An utility to copy a mirrored animation for a half of a walkcycle
    Works as a static class. Stores parameters as a class properties 
    and executes statements as a class method.   
    """
    # Properties to animate, a single string with spaces between names
    keyframing_type='location rotation_quaternion'
    
    # Framerange
    start_frame = 1
    end_frame = 20
    
    # List of possible pairs of prefixes for selecting opposite bones  .
    possible_prefixes = [('r','l'),('R','L')]
       
    @classmethod    
    def append_mirrored(cls):
        """
        The function reads keyframes within the framerange 
        and appends mirrored after the last frame
        """
        # Storing current frame to bring playhead back
        preprocess_frame = bpy.context.scene.frame_current
        time_distance = cls.end_frame - cls.start_frame + 1
        
        # Selected bones to be processed
        processing_objects = bpy.context.selected_pose_bones
        
        # Data to set keys on 
        keying_data = cls.keyframing_type.split()
        
        # Processing every bone from selected      
        for proc_obj in processing_objects:
            # Selecting the bone    
            bpy.ops.object.select_pattern(pattern=proc_obj.name, 
                                          case_sensitive=True, 
                                          extend=False)
            current_object = proc_obj

            # Selecting the opposite bone if any    
            # I used string methods because the select_flip_active() 
            # function doesn't works well within iterator.
            obj_name_prefixes = proc_obj.name.rsplit('.',1)
            possible_prefixes = cls.possible_prefixes
            
            if (len(obj_name_prefixes) > 1):
                
                for prefixes_pair in possible_prefixes:
                    if obj_name_prefixes[1] == prefixes_pair[0]:
                        mirror_name = obj_name_prefixes[0] + '.' + prefixes_pair[1]
                    elif obj_name_prefixes[1] == prefixes_pair[1]:
                        mirror_name = obj_name_prefixes[0] + '.' + prefixes_pair[0]
                
                bpy.ops.object.select_pattern(pattern=mirror_name, 
                                      case_sensitive=True, extend=False)

            # Assigning to a variable the opposite bone if any, 
            # otherwise the same object becomes a mirrored one
            mirror_object = bpy.context.selected_pose_bones[0]

            # Selecting again the bone. Just in case if the opposite bone was selected.
            bpy.ops.object.select_pattern(pattern=proc_obj.name, 
                                          case_sensitive=True, 
                                          extend=False)
            
            # Reading and setting every keyframe assigned to the bone
            current_frame = cls.start_frame
            while (current_frame <= cls.end_frame):
                bpy.context.scene.frame_set(frame=current_frame)
                # Copying a pose
                bpy.ops.pose.copy()
                # Pasting a flipped pose
                bpy.ops.pose.paste(flipped=True)
                # Setting keys at a second half of a walk cycle
                for attr in keying_data:
                     mirror_object.keyframe_insert(data_path=attr, 
                                     frame=(current_frame + time_distance))
                # Break iterator if the end frame        
                if(current_frame==cls.end_frame):
                    break
                # Jumping into next key frame
                bpy.ops.screen.keyframe_jump(next=True)
                # Break iterator if it is the last key frame
                if(current_frame == bpy.context.scene.frame_current):
                    break
                current_frame = bpy.context.scene.frame_current
        # Playhead goes back
        bpy.context.scene.frame_set(frame=preprocess_frame)
        
        # Reselecting the bones
        bpy.ops.pose.select_all(action='DESELECT')
        for proc_obj in processing_objects:
            bpy.ops.object.select_pattern(pattern=proc_obj.name, 
                              case_sensitive=True, 
                              extend=True)
        


class MirrorWalkPropGroup(bpy.types.PropertyGroup):
    '''
    A property group to store parameters in GUI to send them into the WalkCycleUtil later
    '''
    __keyframe_types_description = [("location rotation_quaternion", "LocRot", ""), 
                                    ("rotation_quaternion", "Rotation", ""),
                                    ("location", "Location", "")]
    keyframe_type = bpy.props.EnumProperty(name="Key Frame Types", 
                                           items = __keyframe_types_description, 
                                           description = "Types of keys to set")
    framerange = bpy.props.IntVectorProperty(name="Frame Range", 
                                             description="Frame Range of Half Walk Cycle", 
                                             default=(WalkCycleUtil.start_frame, 
                                                      WalkCycleUtil.end_frame), 
                                             size=2)

bpy.utils.register_class(MirrorWalkPropGroup)

# Assigning property group to Scene
bpy.types.Scene.mirror_walk_props = bpy.props.PointerProperty(type=MirrorWalkPropGroup)


class MirrorWalkOperator(bpy.types.Operator):
    '''Create mirrored animation'''
    bl_idname = "pose.mirrorwalk_operator"
    bl_label = "Mirror"
    # Option to undo at once the execution of the operator
    bl_options = {"UNDO"}
    
    def execute(self, context):
        '''
        Checks framerange correctness. Reads the properties from the property group and
        assigns them to WalkCycleUtil then executes creating of
        mirrored animation
        '''
        startFrame = bpy.context.scene.mirror_walk_props.framerange[0]
        endFrame = bpy.context.scene.mirror_walk_props.framerange[1]
        if(startFrame < endFrame):
            WalkCycleUtil.start_frame = startFrame
            WalkCycleUtil.end_frame = endFrame
            WalkCycleUtil.keyframing_type = bpy.context.scene.mirror_walk_props.keyframe_type
            WalkCycleUtil.append_mirrored()
            return {'FINISHED'}
        else:
            return {'CANCELLED'}

bpy.utils.register_class(MirrorWalkOperator)


class MirrorWalkPanel(bpy.types.Panel):
    '''
    Panel in the Toolshelf for set parameters of mirroring and launch operator
    '''
    bl_label = 'Mirror Walk Cycle'
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'TOOLS'
    bl_context = 'posemode'
    
    def draw(self, context):
        layout = self.layout

        row = layout.row()               
        row.label(text="Frame Range")
        row = layout.row()
        row.prop(bpy.context.scene.mirror_walk_props, 'framerange', text="")
        
        row = layout.row()               
        row.label(text="Key Frame Type")
        row = layout.row()
        row.prop(bpy.context.scene.mirror_walk_props, 'keyframe_type', text="")
    
        row = layout.row()
        row.operator('pose.mirrorwalk_operator')

bpy.utils.register_class(MirrorWalkPanel)


(holyenigma74) #11

your human walk the back leg that pushes forward the
toes need to extend backward a little bit(push him forward)

right now the toes arent doing anything. either bend the toe backward propelling him forward,
or bend the ankle a little bit to push the toe… one or the other…

other than that looks good. and good idea…
anything that make things easier i like. :slight_smile: