One of the ways to further utilize animation assets in Blender is to split actions. Here is a step-by-step guide on how to split actions.
Step 1: Baking the Animation
First, bake the animation in Blender. This involves solidifying the animation data and making any necessary adjustments such as removing root motion. For detailed instructions, please refer to this link.
Step 2: Preparing a Text File for Action Splitting
Prepare a text file in the following format to split actions:
WalkForward:10-40
WalkRight:50-80
If the format of the animation names in the text file differs (e.g., "10-40 WalkForward"), please adjust the format using the script provided at this link.
Step 3: Using the ActionMakingV5.py Script
Open the script from Blender’s text editor. Set the animation_data to text prepared in Step 2, and set the strip.name to the name of the strip you want to split.
Step 4: Running the Script
Select the target armature and run the Run Script. This will appropriately split the specified action based on the text, making each action available as a separate item.
↓
import bpy
def split_action_strip_manual_steps(animation_data):
"""
Splits an NLA strip named "Action" into multiple strips by following the exact manual steps:
1. Select frame range
2. Bake with VisualKeying=True and OnlySelectedBones=False
3. Push Down Action to create a new strip
4. Rename the strip
Makes sure to prioritize the Action strip by moving its track to the top each iteration.
Args:
animation_data: A string of animation segments in format "Name:start-end"
with each segment on a new line
"""
# Parse the animation data
animations = []
for line in animation_data.strip().split('\n'):
if line.strip():
name, frame_range = line.split(':')
start, end = map(int, frame_range.split('-'))
animations.append((name, start, end))
# Sort animations by start frame
animations.sort(key=lambda x: x[1])
# Get the active object (should be an armature)
obj = bpy.context.active_object
if not obj or obj.type != 'ARMATURE':
raise Exception("Please select an armature object")
# Make sure we have animation data
if not obj.animation_data:
raise Exception("The selected armature has no animation data")
# Find the action strip in the NLA editor
action_strip = None
action_track = None
for track in obj.animation_data.nla_tracks:
for strip in track.strips:
if strip.name == "Action":
action_strip = strip
action_track = track
break
if action_strip:
break
if not action_strip:
raise Exception("Could not find a strip named 'Action' in the NLA editor")
# Store the original action
original_action = action_strip.action
if not original_action:
raise Exception("The Action strip does not have an action assigned")
# Store original frame settings
original_frame = bpy.context.scene.frame_current
# Clear any active action
stored_action = obj.animation_data.action
obj.animation_data.action = None
# Store the original strip settings to restore later
original_strip_start = action_strip.frame_start
original_strip_end = action_strip.frame_end
original_action_start = action_strip.action_frame_start
original_action_end = action_strip.action_frame_end
# Find the NLA Editor area (we'll need this for several operations)
nla_editor = None
for area in bpy.context.screen.areas:
if area.type == 'NLA_EDITOR':
nla_editor = area
break
if not nla_editor:
raise Exception("Please open an NLA Editor window")
# Create a context override to operate in the NLA editor
override = {'area': nla_editor, 'region': nla_editor.regions[-1]}
# Create a list to store the created strips for cleanup
created_strips = []
# Process each animation segment
for i, (name, start, end) in enumerate(animations):
print(f"\nProcessing {name} ({start}-{end})...")
# IMPORTANT: Move the original action track to the top of the stack
# This ensures it has priority in the animation evaluation
if i > 0: # We don't need to do this for the first iteration
# Get the current index of the action track
action_track_index = list(obj.animation_data.nla_tracks).index(action_track)
# Move the action track to the top
for _ in range(action_track_index):
with bpy.context.temp_override(**override):
action_track.select = True
bpy.ops.nla.tracks_move(direction='UP')
# STEP 1: Select frame range by modifying the action strip
action_strip.frame_start = start
action_strip.frame_end = end
action_strip.action_frame_start = start
action_strip.action_frame_end = end
# Make sure we're in pose mode
if obj.mode != 'POSE':
bpy.ops.object.mode_set(mode='POSE')
# Set the current frame to the start of the range
bpy.context.scene.frame_set(start)
# STEP 2: Bake the current action strip
# Make sure only the action strip is mutable, mute all other strips
# This ensures only our target animation is evaluated during baking
for track in obj.animation_data.nla_tracks:
for strip in track.strips:
if strip != action_strip:
strip.mute = True
# Deselect all strips
with bpy.context.temp_override(**override):
bpy.ops.nla.select_all(action='DESELECT')
# Select our action strip
action_strip.select = True
# Make sure the NLA is enabled for evaluation
obj.animation_data.use_nla = True
# Bake the animation with the specified settings
with bpy.context.temp_override(**override):
bpy.ops.nla.bake(
frame_start=start,
frame_end=end,
step=1,
only_selected=False,
visual_keying=True,
clear_constraints=False,
clear_parents=False,
use_current_action=True,
bake_types={'POSE'}
)
# STEP 3: Push Down Action to create a strip
# At this point, the bake operation would have created a new active action
baked_action = obj.animation_data.action
if not baked_action:
raise Exception(f"Baking failed for {name}, no action was created")
# Rename the baked action to match our segment name
baked_action.name = name
# Push down the action to create a strip
with bpy.context.temp_override(**override):
bpy.ops.nla.actionclip_add(action=baked_action.name)
# Find the newly created strip (should be the latest one added)
new_track = None
new_strip = None
# Look for the new strip in all tracks
for track in obj.animation_data.nla_tracks:
for strip in track.strips:
if strip.action == baked_action and strip != action_strip:
new_track = track
new_strip = strip
break
if new_strip:
break
if not new_strip:
print(f"Warning: Could not find the new strip for {name}, creating it manually")
# Create a new track and strip manually as a fallback
new_track = obj.animation_data.nla_tracks.new()
new_track.name = name
new_strip = new_track.strips.new(name=name, start=start, action=baked_action)
new_strip.frame_start = start
new_strip.frame_end = end
# STEP 4: Rename the strip
new_strip.name = name
if new_track:
new_track.name = "SplitActions"
# Add to our list of created strips
created_strips.append((new_track, new_strip))
print(f"Created strip: {new_strip.name} with action: {baked_action.name}")
# Reset for the next iteration
obj.animation_data.action = None
# Restore the original action strip settings for the next iteration
action_strip.frame_start = original_strip_start
action_strip.frame_end = original_strip_end
action_strip.action_frame_start = original_action_start
action_strip.action_frame_end = original_action_end
# Unmute the strip we just created
new_strip.mute = False
# Mute all other strips except the action strip for the next iteration
action_strip.mute = False
# Cleanup - unmute all strips
for track in obj.animation_data.nla_tracks:
for strip in track.strips:
strip.mute = False
# Restore the original frame
bpy.context.scene.frame_set(original_frame)
# Restore original active action if there was one
if stored_action:
obj.animation_data.action = stored_action
print("\nAnimation split complete!")
print("Created action strips:")
for anim in animations:
print(f"- {anim[0]} (frames {anim[1]}-{anim[2]})")
return True
# Example usage
animation_data = """Idle1:10-30
IdleToSoar:40-75
FlapForward:100-130
GlideForward:140-170
GlideRight:180-210
GlideLeft:220-250"""
# Run the function
try:
split_action_strip_manual_steps(animation_data)
except Exception as e:
print(f"Error: {str(e)}")
0 件のコメント:
コメントを投稿